Expose control of import prefix to Cargo builds

This commit is contained in:
David Tolnay 2020-10-07 16:04:48 -07:00
parent 4563fb1c74
commit e5098cb4a7
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
2 changed files with 146 additions and 8 deletions

131
gen/build/src/cfg.rs Normal file
View File

@ -0,0 +1,131 @@
use std::fmt::{self, Debug};
use std::marker::PhantomData;
pub struct Cfg<'a> {
pub include_prefix: &'a str,
marker: PhantomData<*const ()>, // !Send + !Sync
}
#[cfg(doc)]
pub static mut CFG: Cfg = Cfg {
include_prefix: "",
marker: PhantomData,
};
impl<'a> Debug for Cfg<'a> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter
.debug_struct("Cfg")
.field("include_prefix", &self.include_prefix)
.finish()
}
}
#[cfg(not(doc))]
pub use self::r#impl::Cfg::CFG;
#[cfg(not(doc))]
mod r#impl {
use lazy_static::lazy_static;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::sync::{PoisonError, RwLock};
lazy_static! {
static ref PACKAGE_NAME: Box<str> = {
crate::env_os("CARGO_PKG_NAME")
.map(|pkg| pkg.to_string_lossy().into_owned().into_boxed_str())
.unwrap_or_default()
};
static ref INCLUDE_PREFIX: RwLock<Vec<&'static str>> = RwLock::new(vec![&PACKAGE_NAME]);
}
thread_local! {
static CONST_DEREFS: RefCell<HashMap<Handle, Box<super::Cfg<'static>>>> = RefCell::default();
}
#[derive(Eq, PartialEq, Hash)]
struct Handle(*const Cfg<'static>);
impl<'a> Cfg<'a> {
fn current() -> super::Cfg<'a> {
let include_prefix = *INCLUDE_PREFIX
.read()
.unwrap_or_else(PoisonError::into_inner)
.last()
.unwrap();
super::Cfg {
include_prefix,
marker: PhantomData,
}
}
const fn handle(self: &Cfg<'a>) -> Handle {
Handle(<*const Cfg>::cast(self))
}
}
// Since super::Cfg is !Send and !Sync, all Cfg are thread local and will
// drop on the same thread where they were created.
pub enum Cfg<'a> {
Mut(super::Cfg<'a>),
CFG,
}
impl<'a> Debug for Cfg<'a> {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
if let Cfg::Mut(cfg) = self {
Debug::fmt(cfg, formatter)
} else {
Debug::fmt(&Cfg::current(), formatter)
}
}
}
impl<'a> Deref for Cfg<'a> {
type Target = super::Cfg<'a>;
fn deref(&self) -> &Self::Target {
if let Cfg::Mut(cfg) = self {
cfg
} else {
let cfg = CONST_DEREFS.with(|derefs| -> *mut super::Cfg {
&mut **derefs
.borrow_mut()
.entry(self.handle())
.or_insert_with(|| Box::new(Cfg::current()))
});
unsafe { &mut *cfg }
}
}
}
impl<'a> DerefMut for Cfg<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
if let Cfg::CFG = self {
CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
*self = Cfg::Mut(Cfg::current());
}
match self {
Cfg::Mut(cfg) => cfg,
Cfg::CFG => unreachable!(),
}
}
}
impl<'a> Drop for Cfg<'a> {
fn drop(&mut self) {
if let Cfg::Mut(cfg) = self {
INCLUDE_PREFIX
.write()
.unwrap_or_else(PoisonError::into_inner)
.push(Box::leak(Box::from(cfg.include_prefix)));
} else {
CONST_DEREFS.with(|derefs| derefs.borrow_mut().remove(&self.handle()));
}
}
}
}

View File

@ -54,6 +54,7 @@
)]
mod cargo;
mod cfg;
mod error;
mod gen;
mod out;
@ -73,6 +74,8 @@ use std::iter;
use std::path::{Path, PathBuf};
use std::process;
pub use crate::cfg::{Cfg, CFG};
/// This returns a [`cc::Build`] on which you should continue to set up any
/// additional source files or compiler flags, and lastly call its [`compile`]
/// method to execute the C++ build.
@ -103,7 +106,7 @@ pub fn bridges(rust_source_files: impl IntoIterator<Item = impl AsRef<Path>>) ->
}
struct Project {
package_name: OsString,
include_prefix: PathBuf,
manifest_dir: PathBuf,
// Output directory as received from Cargo.
out_dir: PathBuf,
@ -124,7 +127,11 @@ struct Project {
impl Project {
fn init() -> Result<Self> {
let package_name = env_os("CARGO_PKG_NAME")?;
let include_prefix = Path::new(CFG.include_prefix);
assert!(include_prefix.is_relative());
assert!(!include_prefix.as_os_str().is_empty());
let include_prefix = include_prefix.components().collect();
let manifest_dir = paths::manifest_dir()?;
let out_dir = paths::out_dir()?;
@ -141,7 +148,7 @@ impl Project {
};
Ok(Project {
package_name,
include_prefix,
manifest_dir,
out_dir,
shared_dir,
@ -240,7 +247,7 @@ fn env_include_dirs() -> impl Iterator<Item = PathBuf> {
fn make_crate_dir(prj: &Project) -> Option<PathBuf> {
let crate_dir = prj.out_dir.join("cxxbridge").join("crate");
let link = crate_dir.join(&prj.package_name);
let link = crate_dir.join(&prj.include_prefix);
if out::symlink_dir(&prj.manifest_dir, link).is_ok() {
println!("cargo:CXXBRIDGE_CRATE={}", crate_dir.to_string_lossy());
Some(crate_dir)
@ -270,8 +277,8 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) ->
let ref rel_path = paths::local_relative_path(rust_source_file);
let cxxbridge = prj.out_dir.join("cxxbridge");
let include_dir = cxxbridge.join("include").join(&prj.package_name);
let sources_dir = cxxbridge.join("sources").join(&prj.package_name);
let include_dir = cxxbridge.join("include").join(&prj.include_prefix);
let sources_dir = cxxbridge.join("sources").join(&prj.include_prefix);
let ref rel_path_h = rel_path.with_appended_extension(".h");
let ref header_path = include_dir.join(rel_path_h);
@ -285,8 +292,8 @@ fn generate_bridge(prj: &Project, build: &mut Build, rust_source_file: &Path) ->
out::write(implementation_path, &generated.implementation)?;
build.file(implementation_path);
let shared_h = prj.shared_dir.join(&prj.package_name).join(rel_path_h);
let shared_cc = prj.shared_dir.join(&prj.package_name).join(rel_path_cc);
let shared_h = prj.shared_dir.join(&prj.include_prefix).join(rel_path_h);
let shared_cc = prj.shared_dir.join(&prj.include_prefix).join(rel_path_cc);
let _ = out::symlink_file(header_path, shared_h);
let _ = out::symlink_file(implementation_path, shared_cc);
Ok(())