cxx_build/
cfg.rs

1use std::fmt::{self, Debug};
2use std::marker::PhantomData;
3use std::path::Path;
4
5/// Build configuration. See [CFG].
6pub struct Cfg<'a> {
7    /// See [`CFG.include_prefix`][CFG#cfginclude_prefix].
8    pub include_prefix: &'a str,
9    /// See [`CFG.exported_header_dirs`][CFG#cfgexported_header_dirs].
10    pub exported_header_dirs: Vec<&'a Path>,
11    /// See [`CFG.exported_header_prefixes`][CFG#cfgexported_header_prefixes].
12    pub exported_header_prefixes: Vec<&'a str>,
13    /// See [`CFG.exported_header_links`][CFG#cfgexported_header_links].
14    pub exported_header_links: Vec<&'a str>,
15    /// See [`CFG.doxygen`][CFG#cfgdoxygen].
16    pub doxygen: bool,
17    marker: PhantomData<*const ()>, // !Send + !Sync
18}
19
20/// Global configuration of the current build.
21///
22/// <br>
23///
24/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;opacity:.73"><strong>&amp;str</strong></div>
25///
26/// ## **`CFG.include_prefix`**
27///
28/// The prefix at which C++ code from your crate as well as directly dependent
29/// crates can access the code generated during this build.
30///
31/// By default, the `include_prefix` is equal to the name of the current crate.
32/// That means if your crate is called `demo` and has Rust source files in a
33/// *src/* directory and maybe some handwritten C++ header files in an
34/// *include/* directory, then the current crate as well as downstream crates
35/// might include them as follows:
36///
37/// ```
38/// # const _: &str = stringify! {
39///   // include one of the handwritten headers:
40/// #include "demo/include/wow.h"
41///
42///   // include a header generated from Rust cxx::bridge:
43/// #include "demo/src/lib.rs.h"
44/// # };
45/// ```
46///
47/// By modifying `CFG.include_prefix` we can substitute a prefix that is
48/// different from the crate name if desired. Here we'll change it to
49/// `"path/to"` which will make import paths take the form
50/// `"path/to/include/wow.h"` and `"path/to/src/lib.rs.h"`.
51///
52/// ```no_run
53/// // build.rs
54///
55/// use cxx_build::CFG;
56///
57/// fn main() {
58///     CFG.include_prefix = "path/to";
59///
60///     cxx_build::bridge("src/lib.rs")
61///         .file("src/demo.cc") // probably contains `#include "path/to/src/lib.rs.h"`
62///         /* ... */
63///         .compile("demo");
64/// }
65/// ```
66///
67/// Note that cross-crate imports are only made available between **direct
68/// dependencies**. Another crate must directly depend on your crate in order to
69/// #include its headers; a transitive dependency is not sufficient.
70/// Additionally, headers from a direct dependency are only importable if the
71/// dependency's Cargo.toml manifest contains a `links` key. If not, its headers
72/// will not be importable from outside of the same crate.
73///
74/// <br>
75///
76/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;opacity:.73"><strong>Vec&lt;&amp;Path&gt;</strong></div>
77///
78/// ## **`CFG.exported_header_dirs`**
79///
80/// A vector of absolute paths. The current crate, directly dependent crates,
81/// and further crates to which this crate's headers are exported (see below)
82/// will be able to `#include` headers from these directories.
83///
84/// Adding a directory to `exported_header_dirs` is similar to adding it to the
85/// current build via the `cc` crate's [`Build::include`][cc::Build::include],
86/// but *also* makes the directory available to downstream crates that want to
87/// `#include` one of the headers from your crate. If the dir were added only
88/// using `Build::include`, the downstream crate including your header would
89/// need to manually add the same directory to their own build as well.
90///
91/// When using `exported_header_dirs`, your crate must also set a `links` key
92/// for itself in Cargo.toml. See [*the `links` manifest key*][links]. The
93/// reason is that Cargo imposes no ordering on the execution of build scripts
94/// without a `links` key, which means the downstream crate's build script might
95/// execute before yours decides what to put into `exported_header_dirs`.
96///
97/// [links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
98///
99/// ### Example
100///
101/// One of your crate's headers wants to include a system library, such as
102/// `#include "Python.h"`.
103///
104/// ```no_run
105/// // build.rs
106///
107/// use cxx_build::CFG;
108/// use std::path::PathBuf;
109///
110/// fn main() {
111///     let python3 = pkg_config::probe_library("python3").unwrap();
112///     let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path);
113///     CFG.exported_header_dirs.extend(python_include_paths);
114///
115///     cxx_build::bridge("src/bridge.rs").compile("demo");
116/// }
117/// ```
118///
119/// ### Example
120///
121/// Your crate wants to rearrange the headers that it exports vs how they're
122/// laid out locally inside the crate's source directory.
123///
124/// Suppose the crate as published contains a file at `./include/myheader.h` but
125/// wants it available to downstream crates as `#include "foo/v1/public.h"`.
126///
127/// ```no_run
128/// // build.rs
129///
130/// use cxx_build::CFG;
131/// use std::path::Path;
132/// use std::{env, fs};
133///
134/// fn main() {
135///     let out_dir = env::var_os("OUT_DIR").unwrap();
136///     let headers = Path::new(&out_dir).join("headers");
137///     CFG.exported_header_dirs.push(&headers);
138///
139///     // We contain `include/myheader.h` locally, but
140///     // downstream will use `#include "foo/v1/public.h"`
141///     let foo = headers.join("foo").join("v1");
142///     fs::create_dir_all(&foo).unwrap();
143///     fs::copy("include/myheader.h", foo.join("public.h")).unwrap();
144///
145///     cxx_build::bridge("src/bridge.rs").compile("demo");
146/// }
147/// ```
148///
149/// <p style="margin:0"><br><br></p>
150///
151/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;opacity:.73"><strong>Vec&lt;&amp;str&gt;</strong></div>
152///
153/// ## **`CFG.exported_header_prefixes`**
154///
155/// Vector of strings. These each refer to the `include_prefix` of one of your
156/// direct dependencies, or a prefix thereof. They describe which of your
157/// dependencies participate in your crate's C++ public API, as opposed to
158/// private use by your crate's implementation.
159///
160/// As a general rule, if one of your headers `#include`s something from one of
161/// your dependencies, you need to put that dependency's `include_prefix` into
162/// `CFG.exported_header_prefixes` (*or* their `links` key into
163/// `CFG.exported_header_links`; see below). On the other hand if only your C++
164/// implementation files and *not* your headers are importing from the
165/// dependency, you do not export that dependency.
166///
167/// The significance of exported headers is that if downstream code (crate 𝒜)
168/// contains an `#include` of a header from your crate (ℬ) and your header
169/// contains an `#include` of something from your dependency (𝒞), the exported
170/// dependency 𝒞 becomes available during the downstream crate 𝒜's build.
171/// Otherwise the downstream crate 𝒜 doesn't know about 𝒞 and wouldn't be able
172/// to find what header your header is referring to, and would fail to build.
173///
174/// When using `exported_header_prefixes`, your crate must also set a `links`
175/// key for itself in Cargo.toml.
176///
177/// ### Example
178///
179/// Suppose you have a crate with 5 direct dependencies and the `include_prefix`
180/// for each one are:
181///
182/// - "crate0"
183/// - "group/api/crate1"
184/// - "group/api/crate2"
185/// - "group/api/contrib/crate3"
186/// - "detail/crate4"
187///
188/// Your header involves types from the first four so we re-export those as part
189/// of your public API, while crate4 is only used internally by your cc file not
190/// your header, so we do not export:
191///
192/// ```no_run
193/// // build.rs
194///
195/// use cxx_build::CFG;
196///
197/// fn main() {
198///     CFG.exported_header_prefixes = vec!["crate0", "group/api"];
199///
200///     cxx_build::bridge("src/bridge.rs")
201///         .file("src/impl.cc")
202///         .compile("demo");
203/// }
204/// ```
205///
206/// <p style="margin:0"><br><br></p>
207///
208/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;opacity:.73"><strong>Vec&lt;&amp;str&gt;</strong></div>
209///
210/// ## **`CFG.exported_header_links`**
211///
212/// Vector of strings. These each refer to the `links` attribute ([*the `links`
213/// manifest key*][links]) of one of your crate's direct dependencies.
214///
215/// This achieves an equivalent result to `CFG.exported_header_prefixes` by
216/// re-exporting a dependency as part of your crate's public API, except with
217/// finer grained control for cases when multiple crates might be sharing the
218/// same `include_prefix` and you'd like to export some but not others. Links
219/// attributes are guaranteed to be unique identifiers by Cargo.
220///
221/// When using `exported_header_links`, your crate must also set a `links` key
222/// for itself in Cargo.toml.
223///
224/// ### Example
225///
226/// ```no_run
227/// // build.rs
228///
229/// use cxx_build::CFG;
230///
231/// fn main() {
232///     CFG.exported_header_links.push("git2");
233///
234///     cxx_build::bridge("src/bridge.rs").compile("demo");
235/// }
236/// ```
237///
238/// <p style="margin:0"><br><br></p>
239///
240/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;opacity:.73"><strong>bool</strong></div>
241///
242/// ## **`CFG.doxygen`**
243///
244/// Boolean. Whether to propagate Rust documentation from inside the cxx::bridge
245/// module as Doxygen-style comments in the generated C++ header.
246///
247/// Documentation on the following are supported:
248///
249/// - shared structs, and fields of shared structs
250/// - shared enums, and their variants
251/// - extern "Rust" opaque types
252/// - extern "Rust" functions, including methods/member functions
253///
254/// ### Example
255///
256/// ```no_run
257/// // build.rs
258///
259/// use cxx_build::CFG;
260///
261/// fn main() {
262///     CFG.doxygen = true;
263///
264///     cxx_build::bridge("src/bridge.rs").compile("demo");
265/// }
266/// ```
267///
268/// ```rust
269/// // src/bridge.rs
270///
271/// #[cxx::bridge]
272/// mod ffi {
273///     /// documentation of MyStruct
274///     pub struct MyStruct {
275///         /// documentation of the struct field
276///         lol: String,
277///     }
278///
279///     extern "Rust" {
280///         /// documentation of MyType
281///         type MyType;
282///
283///         /// function documentation
284///         fn asdf() -> bool;
285///     }
286/// }
287/// #
288/// # pub struct MyType;
289/// # fn asdf() -> bool { true }
290/// # fn main() {}
291/// ```
292///
293/// With `CFG.doxygen` enabled, the generated C++ header through which
294/// downstream C++ code will be able to access these shared structs and extern
295/// "Rust" signatures will have the Rust documentation comments propagated as
296/// Doxygen-style comments:
297///
298/// ```cpp
299/// /// documentation of MyStruct
300/// struct MyStruct final {
301///   /// documentation of the struct field
302///   ::rust::String lol;
303///   …
304/// };
305/// ```
306///
307/// Otherwise by default (without `CFG.doxygen`) they'll just be `//` comments.
308#[cfg(doc)]
309pub static mut CFG: Cfg = Cfg {
310    include_prefix: "",
311    exported_header_dirs: Vec::new(),
312    exported_header_prefixes: Vec::new(),
313    exported_header_links: Vec::new(),
314    doxygen: false,
315    marker: PhantomData,
316};
317
318impl<'a> Debug for Cfg<'a> {
319    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
320        let Self {
321            include_prefix,
322            exported_header_dirs,
323            exported_header_prefixes,
324            exported_header_links,
325            doxygen,
326            marker: _,
327        } = self;
328        formatter
329            .debug_struct("Cfg")
330            .field("include_prefix", include_prefix)
331            .field("exported_header_dirs", exported_header_dirs)
332            .field("exported_header_prefixes", exported_header_prefixes)
333            .field("exported_header_links", exported_header_links)
334            .field("doxygen", doxygen)
335            .finish()
336    }
337}
338
339#[cfg(not(doc))]
340pub use self::r#impl::Cfg::CFG;
341
342#[cfg(not(doc))]
343mod r#impl {
344    use crate::intern::{intern, InternedString};
345    use crate::syntax::map::UnorderedMap as Map;
346    use crate::vec::{self, InternedVec as _};
347    use std::cell::RefCell;
348    use std::fmt::{self, Debug};
349    use std::marker::PhantomData;
350    use std::ops::{Deref, DerefMut};
351    use std::sync::{OnceLock, PoisonError, RwLock};
352
353    struct CurrentCfg {
354        include_prefix: InternedString,
355        exported_header_dirs: Vec<InternedString>,
356        exported_header_prefixes: Vec<InternedString>,
357        exported_header_links: Vec<InternedString>,
358        doxygen: bool,
359    }
360
361    impl CurrentCfg {
362        fn default() -> Self {
363            let include_prefix = crate::env_os("CARGO_PKG_NAME")
364                .map(|pkg| intern(&pkg.to_string_lossy()))
365                .unwrap_or_default();
366            let exported_header_dirs = Vec::new();
367            let exported_header_prefixes = Vec::new();
368            let exported_header_links = Vec::new();
369            let doxygen = false;
370            CurrentCfg {
371                include_prefix,
372                exported_header_dirs,
373                exported_header_prefixes,
374                exported_header_links,
375                doxygen,
376            }
377        }
378    }
379
380    fn current() -> &'static RwLock<CurrentCfg> {
381        static CURRENT: OnceLock<RwLock<CurrentCfg>> = OnceLock::new();
382        CURRENT.get_or_init(|| RwLock::new(CurrentCfg::default()))
383    }
384
385    thread_local! {
386        // FIXME: If https://github.com/rust-lang/rust/issues/77425 is resolved,
387        // we can delete this thread local side table and instead make each CFG
388        // instance directly own the associated super::Cfg.
389        //
390        //     #[allow(const_item_mutation)]
391        //     pub const CFG: Cfg = Cfg {
392        //         cfg: AtomicPtr::new(ptr::null_mut()),
393        //     };
394        //     pub struct Cfg {
395        //         cfg: AtomicPtr<super::Cfg>,
396        //     }
397        //
398        static CONST_DEREFS: RefCell<Map<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
399    }
400
401    #[derive(Eq, PartialEq, Hash)]
402    struct Handle(*const Cfg<'static>);
403
404    impl<'a> Cfg<'a> {
405        fn current() -> super::Cfg<'a> {
406            let current = current().read().unwrap_or_else(PoisonError::into_inner);
407            let include_prefix = current.include_prefix.str();
408            let exported_header_dirs = current.exported_header_dirs.vec();
409            let exported_header_prefixes = current.exported_header_prefixes.vec();
410            let exported_header_links = current.exported_header_links.vec();
411            let doxygen = current.doxygen;
412            super::Cfg {
413                include_prefix,
414                exported_header_dirs,
415                exported_header_prefixes,
416                exported_header_links,
417                doxygen,
418                marker: PhantomData,
419            }
420        }
421
422        const fn handle(self: &Cfg<'a>) -> Handle {
423            Handle(<*const Cfg>::cast(self))
424        }
425    }
426
427    // Since super::Cfg is !Send and !Sync, all Cfg are thread local and will
428    // drop on the same thread where they were created.
429    pub enum Cfg<'a> {
430        Mut(super::Cfg<'a>),
431        CFG,
432    }
433
434    impl<'a> Debug for Cfg<'a> {
435        fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
436            if let Cfg::Mut(cfg) = self {
437                Debug::fmt(cfg, formatter)
438            } else {
439                Debug::fmt(&Cfg::current(), formatter)
440            }
441        }
442    }
443
444    impl<'a> Deref for Cfg<'a> {
445        type Target = super::Cfg<'a>;
446
447        fn deref(&self) -> &Self::Target {
448            if let Cfg::Mut(cfg) = self {
449                cfg
450            } else {
451                let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg {
452                    &mut **derefs
453                        .borrow_mut()
454                        .entry(self.handle())
455                        .or_insert_with(|| Box::new(Cfg::current()))
456                });
457                unsafe { &mut *cfg }
458            }
459        }
460    }
461
462    impl<'a> DerefMut for Cfg<'a> {
463        fn deref_mut(&mut self) -> &mut Self::Target {
464            if let Cfg::CFG = self {
465                CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
466                *self = Cfg::Mut(Cfg::current());
467            }
468            match self {
469                Cfg::Mut(cfg) => cfg,
470                Cfg::CFG => unreachable!(),
471            }
472        }
473    }
474
475    impl<'a> Drop for Cfg<'a> {
476        fn drop(&mut self) {
477            if let Cfg::Mut(cfg) = self {
478                let super::Cfg {
479                    include_prefix,
480                    exported_header_dirs,
481                    exported_header_prefixes,
482                    exported_header_links,
483                    doxygen,
484                    marker: _,
485                } = cfg;
486                let mut current = current().write().unwrap_or_else(PoisonError::into_inner);
487                current.include_prefix = intern(include_prefix);
488                current.exported_header_dirs = vec::intern(exported_header_dirs);
489                current.exported_header_prefixes = vec::intern(exported_header_prefixes);
490                current.exported_header_links = vec::intern(exported_header_links);
491                current.doxygen = *doxygen;
492            } else {
493                CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
494            }
495        }
496    }
497}