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>&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<&Path></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<&str></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<&str></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