• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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