A => .envrc +1 -0
A => .gitignore +3 -0
@@ 1,3 @@
+/.direnv/
+/build/
+/target/
A => Cargo.lock +7 -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"
A => Cargo.toml +3 -0
@@ 1,3 @@
+[workspace]
+resolver = "3"
+members = ["hello"]
A => README.md +14 -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.
A => flake.lock +27 -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
+}
A => flake.nix +20 -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
+ ];
+ };
+ };
+}
A => hello/Cargo.toml +10 -0
@@ 1,10 @@
+[package]
+name = "hello"
+version = "0.1.0"
+edition = "2024"
+
+[lib]
+name = "hello"
+crate-type = ["staticlib"]
+
+[dependencies]
A => hello/src/lib.rs +21 -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);
+ }
+}
A => justfile +47 -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
A => main.ha +10 -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)!)!;
+};