1{{#title Cargo-based setup — Rust ♡ C++}} 2# Cargo-based builds 3 4As one aspect of delivering a good Rust–C++ interop experience, CXX turns 5Cargo into a quite usable build system for C++ projects published as a 6collection of crates.io packages, including a consistent and frictionless 7experience `#include`-ing C++ headers across dependencies. 8 9## Canonical setup 10 11CXX's integration with Cargo is handled through the [cxx-build] crate. 12 13[cxx-build]: https://docs.rs/cxx-build 14 15```toml,hidelines 16## Cargo.toml 17# [package] 18# name = "..." 19# version = "..." 20# edition = "2018" 21 22[dependencies] 23cxx = "1.0" 24 25[build-dependencies] 26cxx-build = "1.0" 27``` 28 29The canonical build script is as follows. The indicated line returns a 30[`cc::Build`] instance (from the usual widely used `cc` crate) on which you can 31set up any additional source files and compiler flags as normal. 32 33[`cc::Build`]: https://docs.rs/cc/1.0/cc/struct.Build.html 34 35```rust,noplayground 36// build.rs 37 38fn main() { 39 cxx_build::bridge("src/main.rs") // returns a cc::Build 40 .file("src/demo.cc") 41 .flag_if_supported("-std=c++11") 42 .compile("cxxbridge-demo"); 43 44 println!("cargo:rerun-if-changed=src/main.rs"); 45 println!("cargo:rerun-if-changed=src/demo.cc"); 46 println!("cargo:rerun-if-changed=include/demo.h"); 47} 48``` 49 50The `rerun-if-changed` lines are optional but make it so that Cargo does not 51spend time recompiling your C++ code when only non-C++ code has changed since 52the previous Cargo build. By default without any `rerun-if-changed`, Cargo will 53re-execute the build script after *any* file changed in the project. 54 55If stuck, try comparing what you have against the *demo/* directory of the CXX 56GitHub repo, which maintains a working Cargo-based setup for the blobstore 57tutorial (chapter 3). 58 59## Header include paths 60 61With cxx-build, by default your include paths always start with the crate name. 62This applies to both `#include` within your C++ code, and `include!` in the 63`extern "C++"` section of your Rust cxx::bridge. 64 65Your crate name is determined by the `name` entry in Cargo.toml. 66 67For example if your crate is named `yourcratename` and contains a C++ header 68file `path/to/header.h` relative to Cargo.toml, that file will be includable as: 69 70```cpp 71#include "yourcratename/path/to/header.h" 72``` 73 74A crate can choose a prefix for its headers that is different from the crate 75name by modifying **[`CFG.include_prefix`][CFG]** from build.rs: 76 77[CFG]: https://docs.rs/cxx-build/*/cxx_build/static.CFG.html 78 79```rust,noplayground 80// build.rs 81 82use cxx_build::CFG; 83 84fn main() { 85 CFG.include_prefix = "my/project"; 86 87 cxx_build::bridge(...)... 88} 89``` 90 91Subsequently the header located at `path/to/header.h` would now be includable 92as: 93 94```cpp 95#include "my/project/path/to/header.h" 96``` 97 98The empty string `""` is a valid include prefix and will make it possible to 99have `#include "path/to/header.h"`. However, if your crate is a library, be 100considerate of possible name collisions that may occur in downstream crates. If 101using an empty include prefix, you'll want to make sure your headers' local path 102within the crate is sufficiently namespaced or unique. 103 104## Including generated code 105 106If your `#[cxx::bridge]` module contains an `extern "Rust"` block i.e. types or 107functions exposed from Rust to C++, or any shared data structures, the 108CXX-generated C++ header declaring those things is available using a `.rs.h` 109extension on the Rust source file's name. 110 111```cpp 112// the header generated from path/to/lib.rs 113#include "yourcratename/path/to/lib.rs.h" 114``` 115 116For giggles, it's also available using just a plain `.rs` extension as if you 117were including the Rust file directly. Use whichever you find more palatable. 118 119```cpp 120#include "yourcratename/path/to/lib.rs" 121``` 122 123## Including headers from dependencies 124 125You get to include headers from your dependencies, both handwritten ones 126contained as `.h` files in their Cargo package, as well as CXX-generated ones. 127 128It works the same as an include of a local header: use the crate name (or their 129include\_prefix if their crate changed it) followed by the relative path of the 130header within the crate. 131 132```cpp 133#include "dependencycratename/path/to/their/header.h` 134``` 135 136Note that cross-crate imports are only made available between **direct 137dependencies**. You must directly depend on the other crate in order to #include 138its headers; a transitive dependency is not sufficient. 139 140Additionally, headers from a direct dependency are only importable if the 141dependency's Cargo.toml manifest contains a `links` key. If not, its headers 142will not be importable from outside of the same crate. See *[the `links` 143manifest key][links]* in the Cargo reference. 144 145[links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key 146 147<br><br><br> 148 149# Advanced features 150 151The following CFG settings are only relevant to you if you are writing a library 152that needs to support downstream crates `#include`-ing its C++ public headers. 153 154## Publicly exporting header directories 155 156**[`CFG.exported_header_dirs`][CFG]** (vector of absolute paths) defines a set 157of additional directories from which the current crate, directly dependent 158crates, and further crates to which this crate's headers are exported (more 159below) will be able to `#include` headers. 160 161Adding a directory to `exported_header_dirs` is similar to adding it to the 162current build via the `cc` crate's [`Build::include`], but *also* makes the 163directory available to downstream crates that want to `#include` one of the 164headers from your crate. If the dir were added only using `Build::include`, the 165downstream crate including your header would need to manually add the same 166directory to their own build as well. 167 168[`Build::include`]: https://docs.rs/cc/1/cc/struct.Build.html#method.include 169 170When using `exported_header_dirs`, your crate must also set a `links` key for 171itself in Cargo.toml. See [*the `links` manifest key*][links]. The reason is 172that Cargo imposes no ordering on the execution of build scripts without a 173`links` key, which means the downstream crate's build script might otherwise 174execute before yours decides what to put into `exported_header_dirs`. 175 176### Example 177 178One of your crate's headers wants to include a system library, such as `#include 179"Python.h"`. 180 181```rust,noplayground 182// build.rs 183 184use cxx_build::CFG; 185use std::path::PathBuf; 186 187fn main() { 188 let python3 = pkg_config::probe_library("python3").unwrap(); 189 let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path); 190 CFG.exported_header_dirs.extend(python_include_paths); 191 192 cxx_build::bridge("src/bridge.rs").compile("demo"); 193} 194``` 195 196### Example 197 198Your crate wants to rearrange the headers that it exports vs how they're laid 199out locally inside the crate's source directory. 200 201Suppose the crate as published contains a file at `./include/myheader.h` but 202wants it available to downstream crates as `#include "foo/v1/public.h"`. 203 204```rust,noplayground 205// build.rs 206 207use cxx_build::CFG; 208use std::path::Path; 209use std::{env, fs}; 210 211fn main() { 212 let out_dir = env::var_os("OUT_DIR").unwrap(); 213 let headers = Path::new(&out_dir).join("headers"); 214 CFG.exported_header_dirs.push(&headers); 215 216 // We contain `include/myheader.h` locally, but 217 // downstream will use `#include "foo/v1/public.h"` 218 let foo = headers.join("foo").join("v1"); 219 fs::create_dir_all(&foo).unwrap(); 220 fs::copy("include/myheader.h", foo.join("public.h")).unwrap(); 221 222 cxx_build::bridge("src/bridge.rs").compile("demo"); 223} 224``` 225 226## Publicly exporting dependencies 227 228**[`CFG.exported_header_prefixes`][CFG]** (vector of strings) each refer to the 229`include_prefix` of one of your direct dependencies, or a prefix thereof. They 230describe which of your dependencies participate in your crate's C++ public API, 231as opposed to private use by your crate's implementation. 232 233As a general rule, if one of your headers `#include`s something from one of your 234dependencies, you need to put that dependency's `include_prefix` into 235`CFG.exported_header_prefixes` (*or* their `links` key into 236`CFG.exported_header_links`; see below). On the other hand if only your C++ 237implementation files and *not* your headers are importing from the dependency, 238you do not export that dependency. 239 240The significance of exported headers is that if downstream code (crate ****) 241contains an `#include` of a header from your crate (**ℬ**) and your header 242contains an `#include` of something from your dependency (****), the exported 243dependency **** becomes available during the downstream crate ****'s build. 244Otherwise the downstream crate **** doesn't know about **** and wouldn't be 245able to find what header your header is referring to, and would fail to build. 246 247When using `exported_header_prefixes`, your crate must also set a `links` key 248for itself in Cargo.toml. 249 250### Example 251 252Suppose you have a crate with 5 direct dependencies and the `include_prefix` for 253each one are: 254 255- "crate0" 256- "group/api/crate1" 257- "group/api/crate2" 258- "group/api/contrib/crate3" 259- "detail/crate4" 260 261Your header involves types from the first four so we re-export those as part of 262your public API, while crate4 is only used internally by your cc file not your 263header, so we do not export: 264 265```rust,noplayground 266// build.rs 267 268use cxx_build::CFG; 269 270fn main() { 271 CFG.exported_header_prefixes = vec!["crate0", "group/api"]; 272 273 cxx_build::bridge("src/bridge.rs") 274 .file("src/impl.cc") 275 .compile("demo"); 276} 277``` 278 279<br> 280 281For more fine grained control, there is **[`CFG.exported_header_links`][CFG]** 282(vector of strings) which each refer to the `links` attribute ([*the `links` 283manifest key*][links]) of one of your crate's direct dependencies. 284 285This achieves an equivalent result to `CFG.exported_header_prefixes` by 286re-exporting a C++ dependency as part of your crate's public API, except with 287finer control for cases when multiple crates might be sharing the same 288`include_prefix` and you'd like to export some but not others. Links attributes 289are guaranteed to be unique identifiers by Cargo. 290 291When using `exported_header_links`, your crate must also set a `links` key for 292itself in Cargo.toml. 293 294### Example 295 296```rust,noplayground 297// build.rs 298 299use cxx_build::CFG; 300 301fn main() { 302 CFG.exported_header_links.push("git2"); 303 304 cxx_build::bridge("src/bridge.rs").compile("demo"); 305} 306``` 307