Build time tooling
Recipe | Crates | Categories |
---|---|---|
Compile and link statically to a bundled C library | ||
Compile and link statically to a bundled C++ library | ||
Compile a C library while setting custom defines |
This section covers "build-time" tooling, or code that is run prior to compiling a crate's source code. Conventionally, build-time code lives in a build.rs
file and is commonly referred to as a "build script". Common use cases include rust code generation and compilation of bundled C/C++/asm code. See crates.io
's documentation on the matter⮳ for more information.
Compile and link statically to a bundled C library
To accommodate scenarios where additional C, C++, or assembly is required in a project, the cc
⮳ crate offers a simple api for compiling bundled C/C++/asm code into static libraries (.a
) that can be statically linked to by rustc
.
The following example has some bundled C code (src/hello.c
) that will be used from rust. Before compiling rust source code, the "build" file (build.rs
) specified in Cargo.toml
runs. Using the cc⮳ crate, a static library file will be produced (in this case, libhello.a
, see cc::Build::compile
⮳) which can then be used from rust by declaring the external function signatures in an compile
⮳, which can then be used from rust by declaring the external function signatures in an extern block⮳ block.
Since the bundled C is very simple, only a single source file needs to be passed to cc::Build
⮳. For more complex build requirements, cc::Build
⮳ offers a full suite of builder methods for specifying cc::Build::include
⮳ paths and extra compiler cc::Build::flag
s⮳.
Cargo.toml
[package]
...
build = "build.rs"
[build-dependencies]
cc = "1"
[dependencies]
error-chain = "0.11"
build.rs
fn main() { // cc::Build::new().file("src/hello.c").compile("hello"); // // outputs `libhello.a` }
src/hello.c
#include <stdio.h>
void hello() {
printf("Hello from C!\n");
}
void greet(const char* name) {
printf("Hello, %s!\n", name);
}
src/main.rs
use std::ffi::CString; use std::os::raw::c_char; use anyhow::Result; fn prompt(s: &str) -> Result<String> { use std::io::Write; print!("{}", s); std::io::stdout().flush()?; let mut input = String::new(); std::io::stdin().read_line(&mut input)?; Ok(input.trim().to_string()) } unsafe extern "C" { fn hello(); fn greet(name: *const c_char); } fn main() -> Result<()> { // unsafe { hello() } let name = prompt("What's your name? ")?; let c_name = CString::new(name)?; // unsafe { greet(c_name.as_ptr()) } Ok(()) }
Compile and link statically to a bundled C++ library
Linking a bundled C++ library is very similar to linking a bundled C library. The two core differences when compiling and statically linking a bundled C++ library are specifying a C++ compiler via the builder method cc::Build::cpp
⮳ and preventing name mangling by the C++ compiler by adding the extern "C"
section at the top of our C++ source file.
Cargo.toml
(static C++)
[package]
...
build = "build.rs"
[build-dependencies]
cc = "1"
build.rs
(static C++)
fn main() { // cc::Build::new() // .cpp(true) // .file("src/foo.cpp") // .compile("foo"); // println!(""); }
src/foo.cpp
(static C++)
extern "C" {
int multiply(int x, int y);
}
int multiply(int x, int y) {
return x*y;
}
src/main.rs
(static C++)
unsafe extern "C" { fn multiply(x: i32, y: i32) -> i32; } fn main() { // unsafe { // println!("{}", multiply(5, 7)); // } }
Compile a C library while setting custom defines
It is simple to build bundled C code with custom defines using cc::Build::define
⮳
The method takes an std::option::Option
⮳ value, so it is possible to create defines such as #define APP_NAME "foo"
as well as #define WELCOME
(pass std::option::Option::None
⮳ as the value for a value-less define). This example builds
a bundled C file with dynamic defines set in build.rs
and prints "Welcome to foo - version 1.0.2
"
when run. Cargo sets some environment variables⮳ which may be useful for some custom defines.
Cargo.toml
(custom defines)
[package]
...
version = "1.0.2"
build = "build.rs"
[build-dependencies]
cc = "1"
build.rs
(custom defines)
fn main() { // cc::Build::new() // .define("APP_NAME", "\"foo\"") // .define( // "VERSION", // format!("\"{}\"", env!("CARGO_PKG_VERSION")).as_str(), // ) // .define("WELCOME", None) // .file("src/foo.c") // .compile("foo"); }
src/foo.c
(custom defines)
#include <stdio.h>
void print_app_info() {
#ifdef WELCOME
printf("Welcome to ");
#endif
printf("%s - version %s\n", APP_NAME, VERSION);
}
src/main.rs
(custom defines)
unsafe extern "C" { fn print_app_info(); } fn main() { // unsafe { // print_app_info(); // } println!("Printed app info."); }