Add full calling example

This commit is contained in:
Leni Aniva 2024-03-05 15:57:48 -08:00
parent 5d1f76c332
commit a4a96bce61
Signed by: aniva
GPG Key ID: 4D9B1C8D10EA4C50
8 changed files with 158 additions and 7 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.* .*
*.olean
!.gitignore !.gitignore

49
Cargo.lock generated
View File

@ -11,12 +11,23 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "annotate-snippets"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e"
dependencies = [
"unicode-width",
"yansi-term",
]
[[package]] [[package]]
name = "bindgen" name = "bindgen"
version = "0.69.4" version = "0.69.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0"
dependencies = [ dependencies = [
"annotate-snippets",
"bitflags", "bitflags",
"cexpr", "cexpr",
"clang-sys", "clang-sys",
@ -242,6 +253,7 @@ name = "rustcalllean"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bindgen", "bindgen",
"libc",
] ]
[[package]] [[package]]
@ -280,6 +292,12 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]] [[package]]
name = "which" name = "which"
version = "4.4.2" version = "4.4.2"
@ -292,6 +310,28 @@ dependencies = [
"rustix", "rustix",
] ]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.52.0" version = "0.52.0"
@ -357,3 +397,12 @@ name = "windows_x86_64_msvc"
version = "0.52.4" version = "0.52.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
[[package]]
name = "yansi-term"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
dependencies = [
"winapi",
]

View File

@ -9,6 +9,8 @@ edition = "2021"
rust-version = "1.73" rust-version = "1.73"
[dependencies] [dependencies]
libc = "0.2.153"
[build-dependencies] [build-dependencies]
bindgen = "0.69.4" # the experimental flag must be enabled
bindgen = { version = "0.69.4", features = ["experimental"] }

View File

@ -7,3 +7,4 @@ This is based on:
* [Lean4 FFI](https://lean-lang.org/lean4/doc/dev/ffi.html#initialization) * [Lean4 FFI](https://lean-lang.org/lean4/doc/dev/ffi.html#initialization)
* [Rustonomicon FFI](https://doc.rust-lang.org/nomicon/ffi.html) * [Rustonomicon FFI](https://doc.rust-lang.org/nomicon/ffi.html)
* [Rust Bindgen](https://rust-lang.github.io/rust-bindgen/introduction.html) * [Rust Bindgen](https://rust-lang.github.io/rust-bindgen/introduction.html)
* [Rust Bindgen Static Functions](https://github.com/rust-lang/rust-bindgen/discussions/2405)

View File

@ -7,6 +7,7 @@ fn main()
{ {
let callee_root = env::var("CALLEE_PATH").expect("Callee path variable must be available"); let callee_root = env::var("CALLEE_PATH").expect("Callee path variable must be available");
let lean_root = env::var("LEAN_ROOT").expect("Lean root must exist"); let lean_root = env::var("LEAN_ROOT").expect("Lean root must exist");
let input = format!("{lean_root}/include/lean/lean.h");
println!("cargo:rustc-link-search={callee_root}"); println!("cargo:rustc-link-search={callee_root}");
println!("cargo:rustc-link-search={lean_root}/lib/lean"); println!("cargo:rustc-link-search={lean_root}/lib/lean");
println!("cargo:rustc-link-lib={CALLEE_NAME}"); println!("cargo:rustc-link-lib={CALLEE_NAME}");
@ -19,18 +20,81 @@ fn main()
let bindings = bindgen::Builder::default() let bindings = bindgen::Builder::default()
// The input header we would like to generate // The input header we would like to generate
// bindings for. // bindings for.
.header(format!("{lean_root}/include/lean/lean.h")) .header(&input)
// Tell cargo to invalidate the built crate whenever any of the // Tell cargo to invalidate the built crate whenever any of the
// included header files changed. // included header files changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
// The following is specific to `lean.h`: There are a lot of static
// function
.wrap_static_fns(true)
// but some functions use `_Atomic(int)` types that bindgen cannot handle,
// so they are blocklisted. The types which use `_Atomic` are treated as
// opaque.
.blocklist_function("lean_get_rc_mt_addr")
.opaque_type("lean_thunk_object")
.opaque_type("lean_task_object")
// Finish the builder and generate the bindings. // Finish the builder and generate the bindings.
.generate() .generate()
// Unwrap the Result and panic on failure. // Unwrap the Result and panic on failure.
.expect("Unable to generate bindings"); .expect("Unable to generate bindings");
let extern_src_path = std::env::temp_dir().join("bindgen").join("extern.c");
let extern_obj_path = std::env::temp_dir().join("bindgen").join("extern.o");
let output_path = PathBuf::from(std::env::var("OUT_DIR").unwrap());
// Write the bindings to the $OUT_DIR/bindings.rs file. // Write the bindings to the $OUT_DIR/bindings.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings bindings
.write_to_file(out_path.join("lean.rs")) .write_to_file(out_path.join("lean.rs"))
.expect("Couldn't write bindings!"); .expect("Couldn't write bindings!");
// This is the path to the object file.
let obj_path = output_path.join("extern.o");
// This is the path to the static library file.
let lib_path = output_path.join("libextern.a");
// Compile the generated wrappers into an object file.
let clang_output = std::process::Command::new("clang")
.arg("-O")
.arg("-c")
.arg("-o")
.arg(extern_obj_path)
.arg(extern_src_path)
.arg("-include")
.arg(&input)
.output()
.unwrap();
if !clang_output.status.success() {
panic!(
"Could not compile object file:\n{}",
String::from_utf8_lossy(&clang_output.stderr)
);
}
// Turn the object file into a static library
#[cfg(not(target_os = "windows"))]
let lib_output = std::process::Command::new("ar")
.arg("rcs")
.arg(output_path.join(&lib_path))
.arg(obj_path)
.output()
.unwrap();
#[cfg(target_os = "windows")]
let lib_output = std::process::Command::new("LIB")
.arg(obj_path)
.arg(format!(
"/OUT:{}",
out_dir_path.join("libextern.lib").display()
))
.output()
.unwrap();
if !lib_output.status.success() {
panic!(
"Could not emit library file:\n{}",
String::from_utf8_lossy(&lib_output.stderr)
);
}
// Statically link against the generated lib
println!("cargo:rustc-link-search={}", output_path.display());
println!("cargo:rustc-link-lib=static=extern");
} }

View File

@ -41,6 +41,8 @@
LEAN_ROOT = leanPkgs.lean-all; LEAN_ROOT = leanPkgs.lean-all;
buildInputs = common ++ [ buildInputs = common ++ [
pkgs.rustc pkgs.rustc
# This is not needed for compilation. It is here for diagnostic purposes
pkgs.rust-bindgen
]; ];
}; };
}; };

2
rust-toolchain.toml Normal file
View File

@ -0,0 +1,2 @@
[toolchain]
channel = "nightly"

View File

@ -1,21 +1,51 @@
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
mod lean { #[allow(dead_code)]
mod lean
{
include!(concat!(env!("OUT_DIR"), "/lean.rs")); include!(concat!(env!("OUT_DIR"), "/lean.rs"));
#[link(name = "lean")] #[link(name = "lean")]
extern { extern "C" {
pub fn lean_initialize_runtime_module(); pub fn lean_initialize_runtime_module();
pub fn lean_initialize(); pub fn lean_initialize();
// lean_io_mark_end_initialization is already defined. // lean_io_mark_end_initialization is already defined.
} }
#[link(name = "Callee")]
extern "C" {
pub fn initialize_Callee(builtin: u8, world: *mut lean_object) -> *mut lean_object;
pub fn my_function(s: lean_obj_arg) -> lean_obj_arg;
} }
fn main() { }
fn main()
{
unsafe { unsafe {
lean::lean_initialize_runtime_module(); lean::lean_initialize_runtime_module();
lean::lean_initialize(); lean::lean_initialize();
let builtin: u8 = 1;
let res = lean::initialize_Callee(builtin, lean::lean_io_mk_world());
if lean::lean_io_result_is_ok(res) {
lean::lean_dec_ref(res);
} else {
lean::lean_io_result_show_error(res);
lean::lean_dec(res);
panic!("Failed to load callee");
}
lean::lean_io_mark_end_initialization(); lean::lean_io_mark_end_initialization();
} }
println!("Lean initialization complete!"); println!("Lean initialization complete!");
let c_str = std::ffi::CString::new("hello ").unwrap();
let result = unsafe {
let native_str = lean::lean_mk_string(c_str.as_ptr());
let result_obj = lean::my_function(native_str);
let result_c_str = std::ffi::CStr::from_ptr(lean::lean_string_cstr(result_obj));
let str = result_c_str.to_str().unwrap().to_string();
// Clean up the resultant objects
lean::lean_dec(result_obj);
str
};
println!("result: {result}");
} }