diff options
author | Christian Poveda Ruiz <31802960+pvdrz@users.noreply.github.com> | 2022-11-01 08:16:41 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-01 08:16:41 -0500 |
commit | 4f92dfb7c59afa01e1e1ac305e9eea60d935d12d (patch) | |
tree | 6e367e4ef7da6c7941107363dbb385f3aca3b9fb | |
parent | b3ac3efcf95220db6b32b63832bff15c5a482d97 (diff) |
Add tutorial about non-system libraries (#2318)
* add tutorial about non-system libraries
* fix broken comment
* ignore code snippet
-rw-r--r-- | book/src/SUMMARY.md | 1 | ||||
-rw-r--r-- | book/src/non-system-libraries.md | 106 |
2 files changed, 107 insertions, 0 deletions
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index e7feff95..551ea0d5 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -10,6 +10,7 @@ - [Include the Generated Bindings in `src/lib.rs`](./tutorial-4.md) - [Write a Sanity Test](./tutorial-5.md) - [Publish Your Crate!](./tutorial-6.md) + - [Bindings for non-system libraries](./non-system-libraries.md) - [Command Line Usage](./command-line-usage.md) - [Customizing the Generated Bindings](./customizing-generated-bindings.md) - [Allowlisting](./allowlisting.md) diff --git a/book/src/non-system-libraries.md b/book/src/non-system-libraries.md new file mode 100644 index 00000000..c724a2fc --- /dev/null +++ b/book/src/non-system-libraries.md @@ -0,0 +1,106 @@ +Now let's suppose we want to generate bindings for a non-system library. We +will be the same crate setup as the previous tutorial. First let's create a new +directory `hello` with two files inside it. A `c` source file `hello.c` +containing +```c +int hello() { + return 42; +} +``` +and a `c` header file `hello.h` containing +```c +int hello(); +``` +given that the library has not been compiled yet, we need to modify the +`build.rs` build script to compile the `hello.c` source file into a static +libary: + +```rust,ignore +extern crate bindgen; + +use std::env; +use std::path::PathBuf; + +use bindgen::CargoCallbacks; + +fn main() { + // This is the directory where the `c` library is located. + let libdir_path = PathBuf::from("hello") + // Canonicalize the path as `rustc-link-search` requires an absolute + // path. + .canonicalize() + .expect("cannot canonicalize path"); + + // This is the path to the `c` headers file. + let headers_path = libdir_path.join("hello.h"); + let headers_path_str = headers_path.to_str().expect("Path is not a valid string"); + + // This is the path to the intermediate object file for our library. + let obj_path = libdir_path.join("hello.o"); + // This is the path to the static library file. + let lib_path = libdir_path.join("libhello.a"); + + // Tell cargo to look for shared libraries in the specified directory + println!("cargo:rustc-link-search={}", libdir_path.to_str().unwrap()); + + // Tell cargo to tell rustc to link our `hello` library. Cargo will + // automatically know it must look for a `libhello.a` file. + println!("cargo:rustc-link-lib=hello"); + + // Tell cargo to invalidate the built crate whenever the header changes. + println!("cargo:rerun-if-changed={}", headers_path_str); + + // Run `clang` to compile the `hello.c` file into a `hello.o` object file. + // Unwrap if it is not possible to spawn the process. + if !std::process::Command::new("clang") + .arg("-c") + .arg("-o") + .arg(&obj_path) + .arg(libdir_path.join("hello.c")) + .output() + .expect("could not spawn `clang`") + .status + .success() + { + // Panic if the command was not successful. + panic!("could not compile object file"); + } + + // Run `ar` to generate the `libhello.a` file from the `hello.o` file. + // Unwrap if it is not possible to spawn the process. + if !std::process::Command::new("ar") + .arg("rcs") + .arg(lib_path) + .arg(obj_path) + .output() + .expect("could not spawn `ar`") + .status + .success() + { + // Panic if the command was not successful. + panic!("could not emit library file"); + } + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header(headers_path_str) + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); + bindings + .write_to_file(out_path) + .expect("Couldn't write bindings!"); +} +``` + |