From b76de4b1f83c99c225c83e958636fd6dca09eedf Mon Sep 17 00:00:00 2001 From: Jonni Liljamo Date: Tue, 15 Jul 2025 18:42:09 +0300 Subject: [PATCH] feat: init --- .envrc | 1 + .gitignore | 3 +++ Cargo.lock | 7 +++++++ Cargo.toml | 3 +++ README.md | 14 ++++++++++++++ flake.lock | 27 +++++++++++++++++++++++++++ flake.nix | 20 ++++++++++++++++++++ hello/Cargo.toml | 10 ++++++++++ hello/src/lib.rs | 21 +++++++++++++++++++++ justfile | 47 +++++++++++++++++++++++++++++++++++++++++++++++ main.ha | 10 ++++++++++ 11 files changed, 163 insertions(+) create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 hello/Cargo.toml create mode 100644 hello/src/lib.rs create mode 100644 justfile create mode 100644 main.ha diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..c4b17d7 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use_flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1883167 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.direnv/ +/build/ +/target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..bdfea16 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "hello" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..9049d66 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +resolver = "3" +members = ["hello"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..e37587e --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# hare-rust-ffi-test + +Just a quick test on Hare <-> Rust FFI. + +Uses `cc` to link the final binary, since Rust's static libraries produce `.a` +archives which `hare build` doesn't pick up automagically (unlike plain `.o`s). + +Although, the above only matters if you want the Rust standard library. Without +it, `rustc` can build a simple `.o` file that works on its own and +automagically with `hare`s module system (assuming glibc is present, I think). + +## TODO: +### Automate the hare stdlib builds +Parse `hare deps` for that. diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..b60bcb4 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1752517496, + "narHash": "sha256-nHo/Gle6z6lMZc5kGtMOBKkVw1/Z9ilYdV2e14+Q6bU=", + "owner": "liljamo", + "repo": "nixpkgs", + "rev": "98bc1dc9a61dc6350ab81625410bab51f28d36ac", + "type": "github" + }, + "original": { + "owner": "liljamo", + "ref": "hare-0.25.2", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..e2da18d --- /dev/null +++ b/flake.nix @@ -0,0 +1,20 @@ +{ + inputs = { + #nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + nixpkgs.url = "github:liljamo/nixpkgs/hare-0.25.2"; + }; + + outputs = inputs @ {...}: let + pkgs = inputs.nixpkgs.legacyPackages."x86_64-linux"; + in { + devShells.x86_64-linux.default = pkgs.mkShell { + buildInputs = with pkgs; + [ + hare + cargo + + just + ]; + }; + }; +} diff --git a/hello/Cargo.toml b/hello/Cargo.toml new file mode 100644 index 0000000..545a12f --- /dev/null +++ b/hello/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "hello" +version = "0.1.0" +edition = "2024" + +[lib] +name = "hello" +crate-type = ["staticlib"] + +[dependencies] diff --git a/hello/src/lib.rs b/hello/src/lib.rs new file mode 100644 index 0000000..7922612 --- /dev/null +++ b/hello/src/lib.rs @@ -0,0 +1,21 @@ +use std::ffi::{CString, CStr}; +use std::os::raw::c_char; + +#[unsafe(no_mangle)] +pub extern "C" fn hello(s_ptr: *const c_char) -> *const c_char { + let s = unsafe { CStr::from_ptr(s_ptr) }.to_str().expect("invalid UTF-8"); + CString::new(format!("Hello {}!", s)).unwrap().into_raw() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let ptr = hello(CStr::from_bytes_with_nul(b"Rust\0").unwrap().as_ptr()); + let out = unsafe { CStr::from_ptr(ptr) }; + let expected = CStr::from_bytes_with_nul(b"Hello Rust!\0").unwrap(); + assert_eq!(out, expected); + } +} diff --git a/justfile b/justfile new file mode 100644 index 0000000..a724ca7 --- /dev/null +++ b/justfile @@ -0,0 +1,47 @@ +build: + #!/usr/bin/env sh + HAREPATH="" + if [[ -z "${HAREPATH}" ]]; then + HAREPATH="$(hare version -v | sed -n '/HAREPATH/{n;p;}' | tr -d '[:space:]')" + echo "HAREPATH not set, using default: $HAREPATH" + else + HAREPATH="${HAREPATH}" + fi + + # setup build dir + rm -rf build/ + mkdir -p build/bin/ + mkdir -p build/o/ + + # build rust staticlib + cargo build --release + + # build hare objects + hare build -F -lc -t o -o build/o/main.o main.ha + hare build -F -lc -t o -o build/o/ascii.o "$HAREPATH/ascii/" + hare build -F -lc -t o -o build/o/bufio.o "$HAREPATH/bufio/" + hare build -F -lc -t o -o build/o/bytes.o "$HAREPATH/bytes/" + hare build -F -lc -t o -o build/o/encoding.o "$HAREPATH/encoding/" + hare build -F -lc -t o -o build/o/encoding_utf8.o "$HAREPATH/encoding/utf8/" + hare build -F -lc -t o -o build/o/errors.o "$HAREPATH/errors/" + hare build -F -lc -t o -o build/o/fmt.o "$HAREPATH/fmt/" + hare build -F -lc -t o -o build/o/fs.o "$HAREPATH/fs/" + hare build -F -lc -t o -o build/o/io.o "$HAREPATH/io/" + hare build -F -lc -t o -o build/o/linux.o "$HAREPATH/linux/" + hare build -F -lc -t o -o build/o/linux_vdso.o "$HAREPATH/linux/vdso/" + hare build -F -lc -t o -o build/o/math.o "$HAREPATH/math/" + hare build -F -lc -t o -o build/o/memio.o "$HAREPATH/memio/" + hare build -F -lc -t o -o build/o/os.o "$HAREPATH/os/" + hare build -F -lc -t o -o build/o/path.o "$HAREPATH/path/" + hare build -F -lc -t o -o build/o/rt.o "$HAREPATH/rt/" + hare build -F -lc -t o -o build/o/sort.o "$HAREPATH/sort/" + hare build -F -lc -t o -o build/o/sort_cmp.o "$HAREPATH/sort/cmp/" + hare build -F -lc -t o -o build/o/strconv.o "$HAREPATH/strconv/" + hare build -F -lc -t o -o build/o/strings.o "$HAREPATH/strings/" + hare build -F -lc -t o -o build/o/time.o "$HAREPATH/time/" + hare build -F -lc -t o -o build/o/types.o "$HAREPATH/types/" + hare build -F -lc -t o -o build/o/types_c.o "$HAREPATH/types/c/" + + # link hare objects and rust staticlib together, into an executable + # ignore startfiles (crt1.o and friends) + cc -nostartfiles -lc build/o/*.o target/release/libhello.a -o build/bin/out diff --git a/main.ha b/main.ha new file mode 100644 index 0000000..25a6cd4 --- /dev/null +++ b/main.ha @@ -0,0 +1,10 @@ +use fmt; +use types::c; + +export @symbol("hello") fn hello(s: const *c::char) const *c::char; + +export fn main() void = { + fmt::println("Hello Hare!")!; + let rust_hello_ptr = hello(c::nulstr("Rust\0")); + fmt::println(c::tostr(rust_hello_ptr)!)!; +}; -- 2.44.1