blob: 6818bdaa4f63fa3f7f6188c483b439c5407594fc [file] [log] [blame]
David Tolnaye5098cb2020-10-07 16:04:48 -07001use std::fmt::{self, Debug};
2use std::marker::PhantomData;
David Tolnaybd54b6f2020-11-04 22:49:41 -08003use std::path::Path;
David Tolnaye5098cb2020-10-07 16:04:48 -07004
David Tolnay983ccf82020-10-08 17:03:35 -07005/// Build configuration. See [CFG].
David Tolnaye5098cb2020-10-07 16:04:48 -07006pub struct Cfg<'a> {
David Tolnay0b1a6b32020-11-05 17:31:06 -08007 /// See [`CFG.include_prefix`][CFG#cfginclude_prefix].
David Tolnaye5098cb2020-10-07 16:04:48 -07008 pub include_prefix: &'a str,
David Tolnay0b1a6b32020-11-05 17:31:06 -08009 /// See [`CFG.exported_header_dirs`][CFG#cfgexported_header_dirs].
David Tolnaybd54b6f2020-11-04 22:49:41 -080010 pub exported_header_dirs: Vec<&'a Path>,
David Tolnay0b1a6b32020-11-05 17:31:06 -080011 /// See [`CFG.exported_header_prefixes`][CFG#cfgexported_header_prefixes].
David Tolnaybd54b6f2020-11-04 22:49:41 -080012 pub exported_header_prefixes: Vec<&'a str>,
David Tolnay0b1a6b32020-11-05 17:31:06 -080013 /// See [`CFG.exported_header_links`][CFG#cfgexported_header_links].
David Tolnaybd54b6f2020-11-04 22:49:41 -080014 pub exported_header_links: Vec<&'a str>,
David Tolnaye5098cb2020-10-07 16:04:48 -070015 marker: PhantomData<*const ()>, // !Send + !Sync
16}
17
David Tolnay983ccf82020-10-08 17:03:35 -070018/// Global configuration of the current build.
19///
20/// <br>
21///
David Tolnaycac5a832020-11-05 20:00:03 -080022/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;color:#444"><strong>&amp;str</strong></div>
23///
David Tolnay983ccf82020-10-08 17:03:35 -070024/// ## **`CFG.include_prefix`**
25///
David Tolnay8249c832020-11-05 16:37:04 -080026/// The prefix at which C++ code from your crate as well as directly dependent
27/// crates can access the code generated during this build.
David Tolnay983ccf82020-10-08 17:03:35 -070028///
29/// By default, the `include_prefix` is equal to the name of the current crate.
David Tolnay8249c832020-11-05 16:37:04 -080030/// That means if your crate is called `demo` and has Rust source files in a
David Tolnay983ccf82020-10-08 17:03:35 -070031/// *src/* directory and maybe some handwritten C++ header files in an
32/// *include/* directory, then the current crate as well as downstream crates
33/// might include them as follows:
34///
35/// ```
36/// # const _: &str = stringify! {
37/// // include one of the handwritten headers:
38/// #include "demo/include/wow.h"
39///
40/// // include a header generated from Rust cxx::bridge:
41/// #include "demo/src/lib.rs.h"
42/// # };
43/// ```
44///
45/// By modifying `CFG.include_prefix` we can substitute a prefix that is
46/// different from the crate name if desired. Here we'll change it to
47/// `"path/to"` which will make import paths take the form
48/// `"path/to/include/wow.h"` and `"path/to/src/lib.rs.h"`.
49///
50/// ```no_run
51/// // build.rs
52///
53/// use cxx_build::CFG;
54///
55/// fn main() {
56/// CFG.include_prefix = "path/to";
57///
58/// cxx_build::bridge("src/lib.rs")
59/// .file("src/demo.cc") // probably contains `#include "path/to/src/lib.rs.h"`
60/// /* ... */
61/// .compile("demo");
62/// }
63/// ```
64///
65/// Note that cross-crate imports are only made available between **direct
66/// dependencies**. Another crate must directly depend on your crate in order to
67/// #include its headers; a transitive dependency is not sufficient.
68/// Additionally, headers from a direct dependency are only importable if the
69/// dependency's Cargo.toml manifest contains a `links` key. If not, its headers
70/// will not be importable from outside of the same crate.
David Tolnay8249c832020-11-05 16:37:04 -080071///
72/// <br>
73///
David Tolnaycac5a832020-11-05 20:00:03 -080074/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;color:#444"><strong>Vec&lt;&amp;Path&gt;</strong></div>
75///
David Tolnay8249c832020-11-05 16:37:04 -080076/// ## **`CFG.exported_header_dirs`**
77///
78/// A vector of absolute paths. The current crate, directly dependent crates,
79/// and further crates to which this crate's headers are exported (see below)
80/// will be able to `#include` headers from these directories.
81///
82/// Adding a directory to `exported_header_dirs` is similar to adding it to the
83/// current build via the `cc` crate's [`Build::include`][cc::Build::include],
84/// but *also* makes the directory available to downstream crates that want to
85/// `#include` one of the headers from your crate. If the dir were added only
86/// using `Build::include`, the downstream crate including your header would
87/// need to manually add the same directory to their own build as well.
88///
89/// When using `exported_header_dirs`, your crate must also set a `links` key
90/// for itself in Cargo.toml. See [*the `links` manifest key*][links]. The
91/// reason is that Cargo imposes no ordering on the execution of build scripts
92/// without a `links` key, which means the downstream crate's build script might
93/// execute before yours decides what to put into `exported_header_dirs`.
94///
95/// [links]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
96///
97/// ### Example
98///
99/// One of your crate's headers wants to include a system library, such as
100/// `#include "Python.h"`.
101///
102/// ```no_run
103/// // build.rs
104///
105/// use cxx_build::CFG;
106/// use std::path::PathBuf;
107///
108/// fn main() {
109/// let python3 = pkg_config::probe_library("python3").unwrap();
110/// let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path);
111/// CFG.exported_header_dirs.extend(python_include_paths);
112///
113/// cxx_build::bridge("src/bridge.rs").compile("demo");
114/// }
115/// ```
116///
117/// ### Example
118///
119/// Your crate wants to rearrange the headers that it exports vs how they're
120/// laid out locally inside the crate's source directory.
121///
122/// Suppose the crate as published contains a file at `./include/myheader.h` but
123/// wants it available to downstream crates as `#include "foo/v1/public.h"`.
124///
125/// ```no_run
126/// // build.rs
127///
128/// use cxx_build::CFG;
129/// use std::path::Path;
130/// use std::{env, fs};
131///
132/// fn main() {
133/// let out_dir = env::var_os("OUT_DIR").unwrap();
134/// let headers = Path::new(&out_dir).join("headers");
135/// CFG.exported_header_dirs.push(&headers);
136///
137/// // We contain `include/myheader.h` locally, but
138/// // downstream will use `#include "foo/v1/public.h"`
139/// let foo = headers.join("foo").join("v1");
140/// fs::create_dir_all(&foo).unwrap();
141/// fs::copy("include/myheader.h", foo.join("public.h")).unwrap();
142///
143/// cxx_build::bridge("src/bridge.rs").compile("demo");
144/// }
145/// ```
146///
David Tolnaycac5a832020-11-05 20:00:03 -0800147/// <p style="margin:0"><br><br></p>
148///
149/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;color:#444"><strong>Vec&lt;&amp;str&gt;</strong></div>
David Tolnay8249c832020-11-05 16:37:04 -0800150///
151/// ## **`CFG.exported_header_prefixes`**
152///
153/// Vector of strings. These each refer to the `include_prefix` of one of your
154/// direct dependencies, or a prefix thereof. They describe which of your
155/// dependencies participate in your crate's C++ public API, as opposed to
156/// private use by your crate's implementation.
157///
158/// As a general rule, if one of your headers `#include`s something from one of
159/// your dependencies, you need to put that dependency's `include_prefix` into
160/// `CFG.exported_header_prefixes` (*or* their `links` key into
161/// `CFG.exported_header_links`; see below). On the other hand if only your C++
162/// implementation files and *not* your headers are importing from the
163/// dependency, you do not export that dependency.
164///
165/// The significance of exported headers is that if downstream code (crate 𝒜)
166/// contains an `#include` of a header from your crate (ℬ) and your header
167/// contains an `#include` of something from your dependency (𝒞), the exported
168/// dependency 𝒞 becomes available during the downstream crate 𝒜's build.
169/// Otherwise the downstream crate 𝒜 doesn't know about 𝒞 and wouldn't be able
170/// to find what header your header is referring to, and would fail to build.
171///
172/// When using `exported_header_prefixes`, your crate must also set a `links`
173/// key for itself in Cargo.toml.
174///
175/// ### Example
176///
177/// Suppose you have a crate with 5 direct dependencies and the `include_prefix`
178/// for each one are:
179///
180/// - "crate0"
181/// - "group/api/crate1"
182/// - "group/api/crate2"
183/// - "group/api/contrib/crate3"
184/// - "detail/crate4"
185///
186/// Your header involves types from the first four so we re-export those as part
187/// of your public API, while crate4 is only used internally by your cc file not
188/// your header, so we do not export:
189///
190/// ```no_run
191/// // build.rs
192///
193/// use cxx_build::CFG;
194///
195/// fn main() {
196/// CFG.exported_header_prefixes = vec!["crate0", "group/api"];
197///
198/// cxx_build::bridge("src/bridge.rs")
199/// .file("src/impl.cc")
200/// .compile("demo");
201/// }
202/// ```
203///
David Tolnaycac5a832020-11-05 20:00:03 -0800204/// <p style="margin:0"><br><br></p>
205///
206/// <div style="float:right;margin:22px 50px 0;font-size:1.15em;color:#444"><strong>Vec&lt;&amp;str&gt;</strong></div>
David Tolnay8249c832020-11-05 16:37:04 -0800207///
208/// ## **`CFG.exported_header_links`**
209///
210/// Vector of strings. These each refer to the `links` attribute ([*the `links`
211/// manifest key*][links]) of one of your crate's direct dependencies.
212///
213/// This achieves an equivalent result to `CFG.exported_header_prefixes` by
214/// re-exporting a dependency as part of your crate's public API, except with
215/// finer grained control for cases when multiple crates might be sharing the
216/// same `include_prefix` and you'd like to export some but not others. Links
217/// attributes are guaranteed to be unique identifiers by Cargo.
218///
219/// When using `exported_header_links`, your crate must also set a `links` key
220/// for itself in Cargo.toml.
221///
222/// ### Example
223///
224/// ```no_run
225/// // build.rs
226///
227/// use cxx_build::CFG;
228///
229/// fn main() {
230/// CFG.exported_header_links.push("git2");
231///
232/// cxx_build::bridge("src/bridge.rs").compile("demo");
233/// }
234/// ```
David Tolnaye5098cb2020-10-07 16:04:48 -0700235#[cfg(doc)]
236pub static mut CFG: Cfg = Cfg {
237 include_prefix: "",
David Tolnaybd54b6f2020-11-04 22:49:41 -0800238 exported_header_dirs: Vec::new(),
239 exported_header_prefixes: Vec::new(),
240 exported_header_links: Vec::new(),
David Tolnaye5098cb2020-10-07 16:04:48 -0700241 marker: PhantomData,
242};
243
244impl<'a> Debug for Cfg<'a> {
245 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
David Tolnay5346b212020-11-05 16:42:30 -0800246 let Self {
247 include_prefix,
248 exported_header_dirs,
249 exported_header_prefixes,
250 exported_header_links,
251 marker: _,
252 } = self;
David Tolnaye5098cb2020-10-07 16:04:48 -0700253 formatter
254 .debug_struct("Cfg")
David Tolnay5346b212020-11-05 16:42:30 -0800255 .field("include_prefix", include_prefix)
256 .field("exported_header_dirs", exported_header_dirs)
257 .field("exported_header_prefixes", exported_header_prefixes)
258 .field("exported_header_links", exported_header_links)
David Tolnaye5098cb2020-10-07 16:04:48 -0700259 .finish()
260 }
261}
262
263#[cfg(not(doc))]
264pub use self::r#impl::Cfg::CFG;
265
266#[cfg(not(doc))]
267mod r#impl {
David Tolnay676196b2020-11-04 13:23:08 -0800268 use crate::intern::{intern, InternedString};
David Tolnaye352c1e2020-12-31 16:41:05 -0800269 use crate::syntax::map::UnorderedMap as Map;
David Tolnay95208212020-11-04 23:06:10 -0800270 use crate::vec::{self, InternedVec as _};
David Tolnaye5098cb2020-10-07 16:04:48 -0700271 use lazy_static::lazy_static;
272 use std::cell::RefCell;
David Tolnaye5098cb2020-10-07 16:04:48 -0700273 use std::fmt::{self, Debug};
274 use std::marker::PhantomData;
275 use std::ops::{Deref, DerefMut};
276 use std::sync::{PoisonError, RwLock};
277
David Tolnay39e9f912020-11-04 23:03:29 -0800278 struct CurrentCfg {
279 include_prefix: InternedString,
280 exported_header_dirs: Vec<InternedString>,
281 exported_header_prefixes: Vec<InternedString>,
282 exported_header_links: Vec<InternedString>,
283 }
284
285 impl CurrentCfg {
286 fn default() -> Self {
287 let include_prefix = crate::env_os("CARGO_PKG_NAME")
David Tolnay676196b2020-11-04 13:23:08 -0800288 .map(|pkg| intern(&pkg.to_string_lossy()))
David Tolnay39e9f912020-11-04 23:03:29 -0800289 .unwrap_or_default();
290 let exported_header_dirs = Vec::new();
291 let exported_header_prefixes = Vec::new();
292 let exported_header_links = Vec::new();
293 CurrentCfg {
294 include_prefix,
295 exported_header_dirs,
296 exported_header_prefixes,
297 exported_header_links,
298 }
299 }
300 }
301
302 lazy_static! {
303 static ref CURRENT: RwLock<CurrentCfg> = RwLock::new(CurrentCfg::default());
David Tolnaye5098cb2020-10-07 16:04:48 -0700304 }
305
306 thread_local! {
David Tolnay03932022020-10-08 19:24:53 -0700307 // FIXME: If https://github.com/rust-lang/rust/issues/77425 is resolved,
308 // we can delete this thread local side table and instead make each CFG
309 // instance directly own the associated super::Cfg.
310 //
311 // #[allow(const_item_mutation)]
312 // pub const CFG: Cfg = Cfg {
313 // cfg: AtomicPtr::new(ptr::null_mut()),
314 // };
315 // pub struct Cfg {
316 // cfg: AtomicPtr<super::Cfg>,
317 // }
318 //
David Tolnaye352c1e2020-12-31 16:41:05 -0800319 static CONST_DEREFS: RefCell<Map<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
David Tolnaye5098cb2020-10-07 16:04:48 -0700320 }
321
322 #[derive(Eq, PartialEq, Hash)]
323 struct Handle(*const Cfg<'static>);
324
325 impl<'a> Cfg<'a> {
326 fn current() -> super::Cfg<'a> {
David Tolnay39e9f912020-11-04 23:03:29 -0800327 let current = CURRENT.read().unwrap_or_else(PoisonError::into_inner);
328 let include_prefix = current.include_prefix.str();
David Tolnay95208212020-11-04 23:06:10 -0800329 let exported_header_dirs = current.exported_header_dirs.vec();
330 let exported_header_prefixes = current.exported_header_prefixes.vec();
331 let exported_header_links = current.exported_header_links.vec();
David Tolnaye5098cb2020-10-07 16:04:48 -0700332 super::Cfg {
333 include_prefix,
David Tolnay306f1262020-11-04 22:54:48 -0800334 exported_header_dirs,
335 exported_header_prefixes,
336 exported_header_links,
David Tolnaye5098cb2020-10-07 16:04:48 -0700337 marker: PhantomData,
338 }
339 }
340
341 const fn handle(self: &Cfg<'a>) -> Handle {
342 Handle(<*const Cfg>::cast(self))
343 }
344 }
345
346 // Since super::Cfg is !Send and !Sync, all Cfg are thread local and will
347 // drop on the same thread where they were created.
348 pub enum Cfg<'a> {
349 Mut(super::Cfg<'a>),
350 CFG,
351 }
352
353 impl<'a> Debug for Cfg<'a> {
354 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
355 if let Cfg::Mut(cfg) = self {
356 Debug::fmt(cfg, formatter)
357 } else {
358 Debug::fmt(&Cfg::current(), formatter)
359 }
360 }
361 }
362
363 impl<'a> Deref for Cfg<'a> {
364 type Target = super::Cfg<'a>;
365
366 fn deref(&self) -> &Self::Target {
367 if let Cfg::Mut(cfg) = self {
368 cfg
369 } else {
370 let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg {
371 &mut **derefs
372 .borrow_mut()
373 .entry(self.handle())
374 .or_insert_with(|| Box::new(Cfg::current()))
375 });
376 unsafe { &mut *cfg }
377 }
378 }
379 }
380
381 impl<'a> DerefMut for Cfg<'a> {
382 fn deref_mut(&mut self) -> &mut Self::Target {
383 if let Cfg::CFG = self {
384 CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
385 *self = Cfg::Mut(Cfg::current());
386 }
387 match self {
388 Cfg::Mut(cfg) => cfg,
389 Cfg::CFG => unreachable!(),
390 }
391 }
392 }
393
394 impl<'a> Drop for Cfg<'a> {
395 fn drop(&mut self) {
396 if let Cfg::Mut(cfg) = self {
David Tolnay39e9f912020-11-04 23:03:29 -0800397 let mut current = CURRENT.write().unwrap_or_else(PoisonError::into_inner);
398 current.include_prefix = intern(cfg.include_prefix);
David Tolnay95208212020-11-04 23:06:10 -0800399 current.exported_header_dirs = vec::intern(&cfg.exported_header_dirs);
400 current.exported_header_prefixes = vec::intern(&cfg.exported_header_prefixes);
401 current.exported_header_links = vec::intern(&cfg.exported_header_links);
David Tolnaye5098cb2020-10-07 16:04:48 -0700402 } else {
403 CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
404 }
405 }
406 }
407}