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