Build Time Tooling

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

cc cat-development-tools cat-development-tools::build-utils

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::flags⮳.

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

cc cat-development-tools

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

cc cat-development-tools

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() {
}