• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1Now let's suppose we want to generate bindings for a non-system library. We
2will be the same crate setup as the previous tutorial. First let's create a new
3directory `hello` with two files inside it. A C source file `hello.c`
4containing
5```c
6int hello() {
7    return 42;
8}
9```
10and a C header file `hello.h` containing
11```c
12int hello();
13```
14
15Given that the library has not been compiled yet, we need to modify the
16`build.rs` build script to compile the `hello.c` source file into a static
17library:
18
19```rust,ignore
20use std::env;
21use std::path::PathBuf;
22
23use bindgen::CargoCallbacks;
24
25fn main() {
26    // This is the directory where the `c` library is located.
27    let libdir_path = PathBuf::from("hello")
28        // Canonicalize the path as `rustc-link-search` requires an absolute
29        // path.
30        .canonicalize()
31        .expect("cannot canonicalize path");
32
33    // This is the path to the `c` headers file.
34    let headers_path = libdir_path.join("hello.h");
35    let headers_path_str = headers_path.to_str().expect("Path is not a valid string");
36
37    // This is the path to the intermediate object file for our library.
38    let obj_path = libdir_path.join("hello.o");
39    // This is the path to the static library file.
40    let lib_path = libdir_path.join("libhello.a");
41
42    // Tell cargo to look for shared libraries in the specified directory
43    println!("cargo:rustc-link-search={}", libdir_path.to_str().unwrap());
44
45    // Tell cargo to tell rustc to link our `hello` library. Cargo will
46    // automatically know it must look for a `libhello.a` file.
47    println!("cargo:rustc-link-lib=hello");
48
49    // Run `clang` to compile the `hello.c` file into a `hello.o` object file.
50    // Unwrap if it is not possible to spawn the process.
51    if !std::process::Command::new("clang")
52        .arg("-c")
53        .arg("-o")
54        .arg(&obj_path)
55        .arg(libdir_path.join("hello.c"))
56        .output()
57        .expect("could not spawn `clang`")
58        .status
59        .success()
60    {
61        // Panic if the command was not successful.
62        panic!("could not compile object file");
63    }
64
65    // Run `ar` to generate the `libhello.a` file from the `hello.o` file.
66    // Unwrap if it is not possible to spawn the process.
67    if !std::process::Command::new("ar")
68        .arg("rcs")
69        .arg(lib_path)
70        .arg(obj_path)
71        .output()
72        .expect("could not spawn `ar`")
73        .status
74        .success()
75    {
76        // Panic if the command was not successful.
77        panic!("could not emit library file");
78    }
79
80    // The bindgen::Builder is the main entry point
81    // to bindgen, and lets you build up options for
82    // the resulting bindings.
83    let bindings = bindgen::Builder::default()
84        // The input header we would like to generate
85        // bindings for.
86        .header(headers_path_str)
87        // Tell cargo to invalidate the built crate whenever any of the
88        // included header files changed.
89        .parse_callbacks(Box::new(CargoCallbacks::new()))
90        // Finish the builder and generate the bindings.
91        .generate()
92        // Unwrap the Result and panic on failure.
93        .expect("Unable to generate bindings");
94
95    // Write the bindings to the $OUT_DIR/bindings.rs file.
96    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs");
97    bindings
98        .write_to_file(out_path)
99        .expect("Couldn't write bindings!");
100}
101```
102