Recipe
Compile a C file into a Rust package
Use when: a Rust package carries C source files that must be compiled and linked into it
Goal: cargo build compiles the C code, links it, and reruns the C step only when a C file changes
Taught in Build integration
-
1
Put headers in
include/and C sources inc/, next toCargo.toml.Example
include/isbn.h,c/isbn.c.Why: a fixed layout keeps the build script short and the C code easy to find.
-
2
Under
[build-dependencies], addcc = "1".Example
[build-dependencies]thencc = "1".Why: only the build script uses cc; nothing of it belongs in the program.
-
3
Print
cargo::rerun-if-changed=for each C file and header, then callcc::Build::new().file(..).include(..).compile(name).Example
cc::Build::new().file("c/isbn.c").include("include").compile("isbn");Why: cc compiles and archives the code and prints the link lines; the rerun lines stop needless rebuilds.
-
4
Declare the C functions in an
unsafe extern "C"block and call each from one safe Rust function.Example
fn isbn10_valid(code: &CStr) -> Option<bool>Why: the linker resolves the names from the library cc built; the wrapper keeps unsafe code in one place.
Common mistakes
- Putting
ccunder[dependencies]: it then compiles into the program for nothing, and the build script can't use it. - Forgetting
rerun-if-changedfor a header, so edits to it don't trigger a rebuild. - Also printing
cargo::rustc-link-libby hand for the same library: cc already does it.