The `cc` crate is a powerful and widely used build-time dependency in the Rust ecosystem, specifically designed to facilitate the compilation of C and C++ (and other C-like) source files as part of a Rust project's build process. It acts as a wrapper around native C/C++ compilers (like GCC, Clang, MSVC), integrating their functionality seamlessly into Cargo's build system via a `build.rs` script.
Its primary purpose is to simplify the use of Foreign Function Interface (FFI) when a Rust crate needs to interact with existing C/C++ libraries or custom C/C++ code. Instead of manually invoking compilers, managing flags, include paths, and linking options, the `cc` crate provides a high-level, cross-platform API to define these parameters programmatically within `build.rs`.
Key functionalities of the `cc` crate include:
1. Compiler Detection: Automatically detects an appropriate C/C++ compiler installed on the system based on environment variables and common system paths.
2. Source File Compilation: Allows specifying individual C/C++ source files to be compiled.
3. Configuration Options: Provides methods to set compiler flags (`.flag()`), preprocessor definitions (`.define()`), include paths (`.include()`), optimization levels, and debug symbols.
4. Static Library Generation: Compiles the specified source files into a static library (e.g., `.a` on Linux/macOS, `.lib` on Windows) which Cargo can then link against.
5. Cross-Compilation: Supports cross-compilation by allowing the specification of the target triple and corresponding compiler/toolchain.
6. Environment Variables: Respects standard environment variables like `CC`, `CXX`, `CFLAGS`, `CXXFLAGS` for advanced configuration.
By using the `cc` crate, Rust developers can effectively manage C/C++ dependencies, ensuring that the necessary native code is compiled and linked correctly during the `cargo build` process, making Rust's interoperability with C/C++ much more robust and maintainable.
Example Code
# First, set up your project structure and dependencies:
# Create a new Rust project:
# cargo new my_c_rust_project --bin
# cd my_c_rust_project
# 1. Add `cc` as a build dependency in `Cargo.toml`
# (Make sure to place it under [build-dependencies])
# `Cargo.toml`
# ```toml
# [package]
# name = "my_c_rust_project"
# version = "0.1.0"
# edition = "2021"
#
# [dependencies]
#
# [build-dependencies]
# cc = "1.0"
# ```
# 2. Create `build.rs` to compile the C code
# This file must be placed at the root of your project directory.
# `build.rs`
```rust
extern crate cc;
fn main() {
// Compile `src/c_code/adder.c` into a static library named "adder".
// The `cc` crate will automatically detect the C compiler (e.g., gcc, clang, msvc).
cc::Build::new()
.file("src/c_code/adder.c")
// Add an include path for the C header if needed, relative to the crate root.
.include("src/c_code")
.compile("adder"); // Output will be libadder.a (or adder.lib on Windows)
// Tell Cargo to rerun this build script if `src/c_code/adder.c` changes
println!("cargo:rerun-if-changed=src/c_code/adder.c");
// Tell Cargo to rerun this build script if `src/c_code/adder.h` changes
println!("cargo:rerun-if-changed=src/c_code/adder.h");
}
```
# 3. Create the C source and header files
# Create a new directory `src/c_code` and place these files inside.
# `src/c_code/adder.h`
```c
#ifndef ADDER_H
#define ADDER_H
// Function declaration for the C function
int add(int a, int b);
#endif // ADDER_H
```
# `src/c_code/adder.c`
```c
#include "adder.h"
// Definition of the C function
int add(int a, int b) {
return a + b;
}
```
# 4. Use the C function in your Rust code via FFI
# `src/main.rs`
```rust
// Declare the external C function signature using `extern "C"`
// The `add` function will be linked from the static library built by `build.rs`.
extern "C" {
fn add(a: i32, b: i32) -> i32;
}
fn main() {
let num1 = 10;
let num2 = 20;
// Call the C function. FFI calls are inherently unsafe.
let result = unsafe {
add(num1, num2)
};
println!("Result of adding {} and {} from C function: {}", num1, num2, result);
}
```
# To build and run this example:
# cargo run
# Expected Output:
# Result of adding 10 and 20 from C function: 30
# Explanation:
# 1. `Cargo.toml` declares `cc` as a `build-dependency`, meaning it's used only during the build phase.
# 2. `build.rs` uses the `cc` crate to compile `src/c_code/adder.c`. It tells `cc` to create a static library named `adder` and link it. The `println!("cargo:rerun-if-changed=...")` lines ensure that Cargo rebuilds the C code if the source files change.
# 3. `src/c_code/adder.h` provides the function signature for `add`.
# 4. `src/c_code/adder.c` implements the `add` function.
# 5. `src/main.rs` declares the C function using `extern "C"` and calls it from `unsafe` Rust code. During compilation, Cargo will link against `libadder.a` (or `adder.lib`) generated by `build.rs`.








cc Crate in Rust