| 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 |
| library: |
| |
| ```rust,ignore |
| use std::env; |
| use std::path::PathBuf; |
| |
| 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"); |
| |
| // 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(bindgen::CargoCallbacks::new())) |
| // 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!"); |
| } |
| ``` |