Skip to main content

Crate cc

Crate cc 

Source
Expand description

A library for Cargo build scripts to compile a set of C/C++/assembly/CUDA files into a static archive for Cargo to link into the crate being built. This crate does not compile code itself; it calls out to the default compiler for the platform. This crate will automatically detect situations such as cross compilation and various environment variables and will build code appropriately.

§Example

First, you’ll want to both add a build script for your crate (build.rs) and also add this crate to your Cargo.toml via:

[build-dependencies]
cc = "1.0"

Next up, you’ll want to write a build script like so:

// build.rs
cc::Build::new()
    .file("foo.c")
    .file("bar.c")
    .compile("foo");

And that’s it! Running cargo build should take care of the rest and your Rust application will now have the C files foo.c and bar.c compiled into a file named libfoo.a. If the C files contain

void foo_function(void) { ... }

and

int32_t bar_function(int32_t x) { ... }

you can call them from Rust by declaring them in your Rust code like so:

extern "C" {
    fn foo_function();
    fn bar_function(x: i32) -> i32;
}

pub fn call() {
    unsafe {
        foo_function();
        bar_function(42);
    }
}

fn main() {
    call();
}

See the Rustonomicon for more details.

§External configuration via environment variables

To control the programs and flags used for building, the builder can set a number of different environment variables.

  • CFLAGS - a series of space separated flags passed to compilers. Note that individual flags cannot currently contain spaces, so doing something like: -L=foo\ bar is not possible.
  • CC - the actual C compiler used. Note that this supports passing a known wrapper via sccache cc. This compiler must understand the -c flag. For certain TARGETs, it also is assumed to know about other flags (most common is -fPIC). ccache, distcc, sccache, icecc, cachepot, buildcache and kache are supported, for sccache, simply set CC to sccache cc. For other custom CC wrapper, just set CC_KNOWN_WRAPPER_CUSTOM to the custom wrapper used in CC. CC can also include flags, for example gcc -m32, which are passed before all other flags.
  • AR - the ar (archiver) executable to use to build the static library.
  • ARFLAGS - a series of space separated flags passed to the archiver, before those from Build::ar_flag.
  • RANLIB and RANLIBFLAGS - the ranlib executable and its flags that Build::get_ranlib returns. cc doesn’t run ranlib itself.
  • CC_MASM_ASM - the assembler for .asm files on MSVC targets, used instead of ml64.exe, ml.exe, armasm.exe or armasm64.exe. Like AR, it can include arguments, for example llvm-ml -m64. For llvm-ml, cc passes -m64 or -m32 to match the target. When it is not set and ml64.exe or ml.exe can’t be found, as when cross compiling, cc uses llvm-ml from next to clang-cl or from PATH instead.
  • NVCC - the nvcc executable used for CUDA, instead of nvcc from PATH. Its whole value is the path, so unlike CC it can’t include a wrapper or arguments. The C++ compiler (see CXX) is still passed to it with -ccbin, unless Build::ccbin turns that off.
  • CROSS_COMPILE - when cross compiling and CC (or CXX), AR or RANLIB isn’t set, cc names that tool for many targets with a prefix, such as arm-linux-gnueabi-gcc. This variable sets that prefix, for example arm-linux-gnueabi-. If it isn’t set, a RUSTC_LINKER from Cargo that ends in -gcc gives the prefix instead.
  • CRATE_CC_NO_DEFAULTS - the default compiler flags may cause conflicts in some cross compiling scenarios. Setting this variable will disable the generation of default compiler flags.
  • CC_ENABLE_DEBUG_OUTPUT - if set, compiler command invocations and exit codes will be logged to stdout. This is useful for debugging build script issues, but can be overly verbose for normal use.
  • CC_SHELL_ESCAPED_FLAGS - if set, *FLAGS will be parsed as if they were shell arguments (similar to make and cmake) rather than splitting them on each space. For example, with CFLAGS='a "b c"', the compiler will be invoked with 2 arguments - a and b c - rather than 3: a, "b and c".
  • CC_PREFER_CLANG_CL_OVER_MSVC - on MSVC targets, whether cc looks for clang-cl.exe instead of cl.exe when CC (or CXX for C++) is not set. It takes precedence over Build::prefer_clang_cl_over_msvc in every build script, unless it is empty. It counts as true unless set to "0", "no" or "false", and is read without the prefixes and suffixes described below.
  • CXX... - see C++ Support.
  • CC_FORCE_DISABLE - If set, cc will never run any Commands, and methods that would return an Error. This is intended for use by third-party build systems which want to be absolutely sure that they are in control of building all dependencies. Note that operations that return Tools such as Build::get_compiler may produce less accurate results as in some cases cc runs commands in order to locate compilers. Additionally, this does nothing to prevent users from running Tool::to_command and executing the Command themselves.
  • RUSTC_WRAPPER - if its file name is sccache, cachepot, buildcache or kache (ignoring case and the extension, so /usr/bin/sccache and SCCACHE.EXE count), cc prefixes it to the compiler command, unless CC (or CXX for C++) already names a wrapper (nvcc for CUDA gets it either way). This is useful for projects that want to use sccache, buildcache, cachepot, or kache. Other wrappers are ignored, since they usually only work with rustc. The wrapper isn’t used for a compiler set with Build::compiler, or for the default emcc on Emscripten targets.
  • SDKROOT - the Apple SDK path that cc passes with -isysroot, instead of the one from xcrun, on Apple targets other than macOS and on macOS with a compiler from an Xcode toolchain (.xctoolchain). It is ignored if it isn’t an existing absolute path or is clearly for another platform.
  • MACOSX_DEPLOYMENT_TARGET, IPHONEOS_DEPLOYMENT_TARGET, TVOS_DEPLOYMENT_TARGET, WATCHOS_DEPLOYMENT_TARGET and XROS_DEPLOYMENT_TARGET - the minimum OS version that cc compiles for on the matching Apple targets.
  • WASI_SDK_PATH - on WASI targets, the wasi-sdk installation. If it has a bin/<target>-clang, cc uses that as the default compiler.
  • WASI_SYSROOT - on WASI targets, the sysroot that cc passes with --sysroot. For C++ on wasm32, cc also links libc++ and libc++abi from its lib/<target> folder.
  • WASM_MUSL_SYSROOT - required on wasm32 Linux targets such as wasm32-wali-linux-musl: the sysroot that cc passes with --sysroot. For C++, cc also links libc++ and libc++abi from its lib folder.
  • PAUTHTEST_SYSROOT and PAUTHTEST_RESOURCE_DIR - required on pauthtest targets such as aarch64-unknown-linux-pauthtest: the sysroot and the Clang resource folder that cc passes with --sysroot and -resource-dir. For C++, cc also links libc++ and libc++abi from the sysroot’s lib folder.
  • VCINSTALLDIR - set by a Visual Studio developer command prompt. On Windows, cc then takes cl.exe, lib.exe and the other MSVC tools from PATH, or from the Visual Studio in VSINSTALLDIR if the prompt is for another architecture, before it looks for an installed Visual Studio. On other hosts, cc looks for these tools in VCINSTALLDIR and VSINSTALLDIR, which must both be set, and then in PATH.

Furthermore, projects using this crate may specify custom environment variables to be inspected, for example via the Build::try_flags_from_environment function. Consult the project’s own documentation or its use of the cc crate for any additional variables it may use.

CC, CXX, AR, RANLIB, NVCC, CC_MASM_ASM, CXXSTDLIB, CXXSTDLIB_STATIC and the flags variables CFLAGS, CXXFLAGS, ARFLAGS and RANLIBFLAGS can also be supplied with certain prefixes and suffixes, in the following prioritized order:

  1. <var>_<target> - for example, CC_x86_64-unknown-linux-gnu or CC_thumbv8m.main-none-eabi
  2. <var>_<target_with_underscores> - for example, CC_x86_64_unknown_linux_gnu or CC_thumbv8m_main_none_eabi (both dashes and periods are replaced)
  3. <build-kind>_<var> - for example, HOST_CC or TARGET_CFLAGS
  4. <var> - a plain CC, AR as above.

For the flags variables, every form that is set is used, in the reverse of this order, so the more specific flags come last. The other variables listed above are only read as written, without prefixes or suffixes.

If none of these variables exist, cc-rs uses built-in defaults.

The compiler also reads some variables by itself, such as CPATH for GCC and Clang, INCLUDE and CL for MSVC, and SDKROOT on Apple targets. cc tells Cargo to rerun the build script when these change too, see Build::emit_rerun_if_env_changed for the full list.

In addition to the above optional environment variables, cc-rs has some functions with hard requirements on some variables supplied by cargo’s build-script driver that it has the TARGET, OUT_DIR, OPT_LEVEL, and HOST variables. It also reads these variables from Cargo when they are set:

  • DEBUG - the profile’s debug setting. Unless it is off ("", "0", "false" or "none"), cc passes debug info flags such as -g or -Z7 and, with GCC and Clang, keeps frame pointers. Build::debug overrides it.
  • CARGO_CFG_TARGET_FEATURE - if it contains crt-static, cc passes -MT instead of -MD to MSVC and clang-cl, unless Build::static_crt is set, and -static to GCC. This only applies when the build is for Cargo’s TARGET.
  • CARGO_CFG_SANITIZE - the sanitizers rustc instruments the Rust code for, from -Zsanitizer or from a target such as x86_64-unknown-linux-gnuasan. Cargo sets it only with a nightly rustc, since stable rustc leaves this cfg out of --print cfg. If it contains address, cc passes -fsanitize=address too, so that the C code is instrumented as well. It does so only for GCC and Clang on x86_64 Linux with glibc and for cl.exe on x86_64 Windows, the configurations CI tests with rustc’s runtime, not for CUDA, and only when the build is for Cargo’s TARGET. Elsewhere, and for the other sanitizers, add the flags yourself with Build::flag where they work. The flag is one of the default flags, so Build::no_default_flags and CRATE_CC_NO_DEFAULTS leave it out, and with GCC and Clang -fno-sanitize=address in CFLAGS or CXXFLAGS turns it off again.

§Optional features

§Parallel

Currently cc-rs supports parallel compilation (think make -jN) but this feature is turned off by default. To enable cc-rs to compile C/C++ in parallel, you can change your dependency to:

[build-dependencies]
cc = { version = "1.0", features = ["parallel"] }

By default cc-rs will limit parallelism to $NUM_JOBS, or if not present it will limit it to the number of cpus on the machine. If you are using cargo, use -jN option of build, test and run commands as $NUM_JOBS is supplied by cargo.

§Compile-time Requirements

To work properly this crate needs access to a C compiler when the build script is being run. This crate does not ship a C compiler with it. The compiler required varies per platform, but there are three broad categories:

  • Unix platforms require cc to be the C compiler. This can be found by installing cc/clang on Linux distributions and Xcode on macOS, for example.
  • Windows platforms targeting MSVC (e.g. your target name ends in -msvc) require Visual Studio to be installed. cc-rs attempts to locate it, and if it fails, cl.exe is expected to be available in PATH. This can be set up by running the appropriate developer tools shell.
    • When using prefer_clang_cl_over_msvc or CC_PREFER_CLANG_CL_OVER_MSVC, make sure that the C++ Clang compiler for Windows component is installed through the Visual Studio Installer, so that cc-rs can find clang-cl.exe.
  • Windows platforms targeting MinGW (e.g. your target name ends in -gnu) require cc to be available in PATH. We recommend the MinGW-w64 distribution. You may also acquire it via MSYS2, as explained here. Make sure to install the appropriate architecture corresponding to your installation of rustc. GCC from older MinGW project is compatible only with 32-bit rust compiler.

§C++ support

cc-rs supports C++ libraries compilation by using the cpp method on Build:

cc::Build::new()
    .cpp(true) // Switch to C++ library compilation.
    .file("foo.cpp")
    .compile("foo");

For C++ libraries, the CXX and CXXFLAGS environment variables are used instead of CC and CFLAGS.

The C++ standard library may be linked to the crate target. By default it’s libc++ for macOS, FreeBSD, and OpenBSD, libc++_shared for Android, nothing for MSVC, and libstdc++ for anything else.

On those other targets, except wasm32, a Clang compiler can use libc++ instead, when it was built to default to it or is given -stdlib=libc++ through CXXFLAGS or Build::flag. For a Clang compiler, cc therefore preprocesses a file that includes a C++ header with the same command and flags it compiles with, and links libc++ if the header comes from libc++. If this check fails, libstdc++ is linked as before. It is not done for CUDA, when Build::cargo_metadata is off, or when the flags include -nostdinc++ or -nostdinc, since the build then picks the C++ headers itself.

If libc++ is linked statically (see CXXSTDLIB_STATIC below), the build will probably also need c++abi, since libc++.a usually doesn’t contain it. cc doesn’t link it, so link it from the build script, for example with cargo:rustc-link-lib=static:-bundle=c++abi.

The C++ standard library can be changed in one of two ways, which also skip the check above:

  1. by using the cpp_link_stdlib method on Build:
cc::Build::new()
    .cpp(true)
    .file("foo.cpp")
    .cpp_link_stdlib("stdc++") // use libstdc++
    .compile("foo");
  1. by setting the CXXSTDLIB environment variable.

In particular, for Android you may want to use c++_static if you have at most one shared library.

Setting the CXXSTDLIB_STATIC environment variable links the C++ standard library statically, as if Build::cpp_link_stdlib_static were set on every Build, including those of crates that don’t call it. The name of the library is still chosen as above. Like CXXSTDLIB, it can be set per target, for example as CXXSTDLIB_STATIC_x86_64_unknown_linux_gnu. It is on unless set to "", "0", "no" or "false". A build script that links the C++ standard library from two separate Builds may then fail to build, see Build::cpp_link_stdlib_static.

Remember that C++ does name mangling so extern "C" might be required to enable Rust linker to find your functions.

§CUDA C++ support

cc-rs also supports compiling CUDA C++ libraries by using the cuda method on Build:

cc::Build::new()
    // Switch to CUDA C++ library compilation using NVCC.
    .cuda(true)
    .cudart("static")
    // Generate code for Maxwell (GTX 970, 980, 980 Ti, Titan X).
    .flag("-gencode").flag("arch=compute_52,code=sm_52")
    // Generate code for Maxwell (Jetson TX1).
    .flag("-gencode").flag("arch=compute_53,code=sm_53")
    // Generate code for Pascal (GTX 1070, 1080, 1080 Ti, Titan Xp).
    .flag("-gencode").flag("arch=compute_61,code=sm_61")
    // Generate code for Pascal (Tesla P100).
    .flag("-gencode").flag("arch=compute_60,code=sm_60")
    // Generate code for Pascal (Jetson TX2).
    .flag("-gencode").flag("arch=compute_62,code=sm_62")
    // Generate code in parallel
    .flag("-t0")
    .file("bar.cu")
    .compile("bar");

§Speed up compilation with sccache

cc-rs does not handle incremental compilation like make or ninja. It always compiles the all sources, no matter if they have changed or not. This would be time-consuming in large projects. To save compilation time, you can use sccache by setting environment variable RUSTC_WRAPPER=sccache, which will use cached .o files if the sources are unchanged.

Modules§

compile_commands
Record the commands cc compiles with as a JSON compilation database, the compile_commands.json that clangd, clang-tidy and other tools read. Needs the compile-commands feature.
windows_registry
A helper module to looking for windows-specific tools:

Structs§

Build
A builder for compilation of a native library.
CompileCommand
The command that compiles one object file: an entry of a JSON compilation database.
Error
Represents an internal error that occurred, with an explanation.
Tool
Configuration used to represent an invocation of a C compiler.

Enums§

BuildMessage
The text of a message passed to a BuildMessageLogger.
BuildMessageKind
What a BuildMessage is about.

Traits§

BuildMessageLogger
Receives cc’s messages, set with Build::message_logger.

Functions§

emit_link_directives
Emit the cargo: lines that link a static library into the crate being built, the way Build::compile does after creating its archive.
try_emit_link_directives
Emit the cargo: lines that link a static library into the crate being built, the way Build::compile does after creating its archive.