• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::fmt::{self, Debug};
2 use std::marker::PhantomData;
3 use std::path::Path;
4 
5 /// Build configuration. See [CFG].
6 pub 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)]
309 pub 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 
318 impl<'a> Debug for Cfg<'a> {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result319     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))]
340 pub use self::r#impl::Cfg::CFG;
341 
342 #[cfg(not(doc))]
343 mod 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 once_cell::sync::Lazy;
348     use std::cell::RefCell;
349     use std::fmt::{self, Debug};
350     use std::marker::PhantomData;
351     use std::ops::{Deref, DerefMut};
352     use std::sync::{PoisonError, RwLock};
353 
354     struct CurrentCfg {
355         include_prefix: InternedString,
356         exported_header_dirs: Vec<InternedString>,
357         exported_header_prefixes: Vec<InternedString>,
358         exported_header_links: Vec<InternedString>,
359         doxygen: bool,
360     }
361 
362     impl CurrentCfg {
default() -> Self363         fn default() -> Self {
364             let include_prefix = crate::env_os("CARGO_PKG_NAME")
365                 .map(|pkg| intern(&pkg.to_string_lossy()))
366                 .unwrap_or_default();
367             let exported_header_dirs = Vec::new();
368             let exported_header_prefixes = Vec::new();
369             let exported_header_links = Vec::new();
370             let doxygen = false;
371             CurrentCfg {
372                 include_prefix,
373                 exported_header_dirs,
374                 exported_header_prefixes,
375                 exported_header_links,
376                 doxygen,
377             }
378         }
379     }
380 
381     static CURRENT: Lazy<RwLock<CurrentCfg>> = Lazy::new(|| RwLock::new(CurrentCfg::default()));
382 
383     thread_local! {
384         // FIXME: If https://github.com/rust-lang/rust/issues/77425 is resolved,
385         // we can delete this thread local side table and instead make each CFG
386         // instance directly own the associated super::Cfg.
387         //
388         //     #[allow(const_item_mutation)]
389         //     pub const CFG: Cfg = Cfg {
390         //         cfg: AtomicPtr::new(ptr::null_mut()),
391         //     };
392         //     pub struct Cfg {
393         //         cfg: AtomicPtr<super::Cfg>,
394         //     }
395         //
396         static CONST_DEREFS: RefCell<Map<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
397     }
398 
399     #[derive(Eq, PartialEq, Hash)]
400     struct Handle(*const Cfg<'static>);
401 
402     impl<'a> Cfg<'a> {
current() -> super::Cfg<'a>403         fn current() -> super::Cfg<'a> {
404             let current = CURRENT.read().unwrap_or_else(PoisonError::into_inner);
405             let include_prefix = current.include_prefix.str();
406             let exported_header_dirs = current.exported_header_dirs.vec();
407             let exported_header_prefixes = current.exported_header_prefixes.vec();
408             let exported_header_links = current.exported_header_links.vec();
409             let doxygen = current.doxygen;
410             super::Cfg {
411                 include_prefix,
412                 exported_header_dirs,
413                 exported_header_prefixes,
414                 exported_header_links,
415                 doxygen,
416                 marker: PhantomData,
417             }
418         }
419 
handle(self: &Cfg<'a>) -> Handle420         const fn handle(self: &Cfg<'a>) -> Handle {
421             Handle(<*const Cfg>::cast(self))
422         }
423     }
424 
425     // Since super::Cfg is !Send and !Sync, all Cfg are thread local and will
426     // drop on the same thread where they were created.
427     pub enum Cfg<'a> {
428         Mut(super::Cfg<'a>),
429         CFG,
430     }
431 
432     impl<'a> Debug for Cfg<'a> {
fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result433         fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
434             if let Cfg::Mut(cfg) = self {
435                 Debug::fmt(cfg, formatter)
436             } else {
437                 Debug::fmt(&Cfg::current(), formatter)
438             }
439         }
440     }
441 
442     impl<'a> Deref for Cfg<'a> {
443         type Target = super::Cfg<'a>;
444 
deref(&self) -> &Self::Target445         fn deref(&self) -> &Self::Target {
446             if let Cfg::Mut(cfg) = self {
447                 cfg
448             } else {
449                 let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg {
450                     &mut **derefs
451                         .borrow_mut()
452                         .entry(self.handle())
453                         .or_insert_with(|| Box::new(Cfg::current()))
454                 });
455                 unsafe { &mut *cfg }
456             }
457         }
458     }
459 
460     impl<'a> DerefMut for Cfg<'a> {
deref_mut(&mut self) -> &mut Self::Target461         fn deref_mut(&mut self) -> &mut Self::Target {
462             if let Cfg::CFG = self {
463                 CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
464                 *self = Cfg::Mut(Cfg::current());
465             }
466             match self {
467                 Cfg::Mut(cfg) => cfg,
468                 Cfg::CFG => unreachable!(),
469             }
470         }
471     }
472 
473     impl<'a> Drop for Cfg<'a> {
drop(&mut self)474         fn drop(&mut self) {
475             if let Cfg::Mut(cfg) = self {
476                 let super::Cfg {
477                     include_prefix,
478                     exported_header_dirs,
479                     exported_header_prefixes,
480                     exported_header_links,
481                     doxygen,
482                     marker: _,
483                 } = cfg;
484                 let mut current = CURRENT.write().unwrap_or_else(PoisonError::into_inner);
485                 current.include_prefix = intern(include_prefix);
486                 current.exported_header_dirs = vec::intern(exported_header_dirs);
487                 current.exported_header_prefixes = vec::intern(exported_header_prefixes);
488                 current.exported_header_links = vec::intern(exported_header_links);
489                 current.doxygen = *doxygen;
490             } else {
491                 CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
492             }
493         }
494     }
495 }
496