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
#![allow(unused)] fn main() { }
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
#![allow(unused)] fn main() { }
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++)
#![allow(unused)] fn main() { }
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++)
#![allow(unused)] fn main() { }
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)
#![allow(unused)] fn main() { }
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)
#![allow(unused)] fn main() { }