Skip to content

Commit

Permalink
Support custom pre-generated and generated include
Browse files Browse the repository at this point in the history
The default patterns are:
- pre-generated in: `{root_dir}/assets/capi/include/**/*`
- generated in: `{out_dir}/capi/include/**/*`

See #184
  • Loading branch information
lu-zero committed Jul 1, 2021
1 parent bd11f6a commit 16ecb60
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 20 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ serde_derive = "1.0"
serde_json = "1.0.62"
anyhow = "1.0"
cc = "1.0"

glob = "0.3"
itertools = "0.10"

[features]
default = []
Expand Down
4 changes: 4 additions & 0 deletions example-project/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@ strip_include_path_components = 1
[package.metadata.capi.library]
rustflags = "-Cpanic=abort"
name = "example-project"

[package.metadata.capi.install.include]
asset = [{from = "include/file.h", to = "otherplace" }]
generated = [{from = "include/other_file.h", to = "otherplace" }]
16 changes: 16 additions & 0 deletions example-project/build.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use cargo_metadata::*;
use std::path::*;

fn main() {
let path = std::env::var("CARGO_MANIFEST_DIR").unwrap();
Expand All @@ -9,4 +10,19 @@ fn main() {
.unwrap();

println!("{:?}", meta);

let out = std::env::var("OUT_DIR").unwrap();
let out = Path::new(&out);

let path = out.join("capi/include/");
let subdir = path.join("subdir");
let include = out.join("include");

std::fs::create_dir_all(&path).unwrap();
std::fs::create_dir_all(&subdir).unwrap();
std::fs::create_dir_all(&include).unwrap();

std::fs::write(path.join("generated.h"), "// Generated").unwrap();
std::fs::write(subdir.join("in_subdir.h"), "// Generated").unwrap();
std::fs::write(include.join("other_file.h"), "// Generated").unwrap();
}
163 changes: 152 additions & 11 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ fn build_implib_file(
struct FingerPrint<'a> {
name: &'a str,
root_output: &'a Path,
build_targets: &'a BuildTargets,
build_targets: BuildTargets,
install_paths: &'a InstallPaths,
static_libs: &'a str,
}
Expand All @@ -319,7 +319,7 @@ impl<'a> FingerPrint<'a> {
fn new(
name: &'a str,
root_output: &'a Path,
build_targets: &'a BuildTargets,
build_targets: BuildTargets,
install_paths: &'a InstallPaths,
) -> Self {
Self {
Expand Down Expand Up @@ -405,6 +405,7 @@ pub struct CApiConfig {
pub header: HeaderCApiConfig,
pub pkg_config: PkgConfigCApiConfig,
pub library: LibraryCApiConfig,
pub install: InstallCApiConfig,
}

pub struct HeaderCApiConfig {
Expand Down Expand Up @@ -432,6 +433,77 @@ pub struct LibraryCApiConfig {
pub rustflags: Vec<String>,
}

pub struct InstallCApiConfig {
pub include: Vec<InstallTarget>,
}

pub enum InstallTarget {
Asset(InstallTargetPaths),
Generated(InstallTargetPaths),
}

#[derive(Clone)]
pub struct InstallTargetPaths {
/// pattern to feed to glob::glob()
///
/// if the InstallTarget is Asset its root is the the root_path
/// if the InstallTarget is Generated its root is the root_output
pub from: String,
/// The path to be joined to the canonical directory to install the files discovered by the
/// glob, e.g. `{includedir}/{to}` for includes.
pub to: String,
}

impl InstallTargetPaths {
pub fn from_value(value: &toml::value::Value, default_to: &str) -> anyhow::Result<Self> {
let from = value
.get("from")
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow::anyhow!("a from field is required"))?;
let to = value
.get("to")
.and_then(|v| v.as_str())
.unwrap_or(default_to);

Ok(InstallTargetPaths {
from: from.to_string(),
to: to.to_string(),
})
}

pub fn install_paths(
&self,
root: &Path,
) -> anyhow::Result<impl Iterator<Item = (PathBuf, PathBuf)>> {
let pattern = root.join(&self.from);
let base_pattern = if self.from.contains("/**") {
pattern
.iter()
.take_while(|&c| c != std::ffi::OsStr::new("**"))
.collect()
} else {
pattern.parent().unwrap().to_path_buf()
};
let pattern = pattern.to_str().unwrap();
let to = PathBuf::from(&self.to);
let g = glob::glob(pattern)?.filter_map(move |p| {
if let Ok(p) = p {
if p.is_file() {
let from = p;
let to = to.join(from.strip_prefix(&base_pattern).unwrap());
Some((from, to))
} else {
None
}
} else {
None
}
});

Ok(g)
}
}

fn load_manifest_capi_config(
name: &str,
root_path: &Path,
Expand Down Expand Up @@ -601,10 +673,49 @@ fn load_manifest_capi_config(
rustflags,
};

let default_assets_include = InstallTargetPaths {
from: "assets/capi/include/**/*".to_string(),
to: header.subdirectory.clone(),
};

let default_generated_include = InstallTargetPaths {
from: "capi/include/**/*".to_string(),
to: header.subdirectory.clone(),
};

let mut include_targets = vec![
InstallTarget::Asset(default_assets_include),
InstallTarget::Generated(default_generated_include),
];

let install = capi.and_then(|v| v.get("install"));
if let Some(ref install) = install {
if let Some(includes) = install.get("include") {
if let Some(assets) = includes.get("asset").and_then(|v| v.as_array()) {
for asset in assets {
let target_paths = InstallTargetPaths::from_value(asset, &header.subdirectory)?;
include_targets.push(InstallTarget::Asset(target_paths));
}
}

if let Some(generated) = includes.get("generated").and_then(|v| v.as_array()) {
for gen in generated {
let target_paths = InstallTargetPaths::from_value(gen, &header.subdirectory)?;
include_targets.push(InstallTarget::Generated(target_paths));
}
}
}
}

let install = InstallCApiConfig {
include: include_targets,
};

Ok(CApiConfig {
header,
pkg_config,
library,
install,
})
}

Expand Down Expand Up @@ -699,7 +810,7 @@ impl Executor for Exec {
}
}

use cargo::core::compiler::{unit_graph, Compilation, UnitInterner};
use cargo::core::compiler::{unit_graph, UnitInterner};
use cargo::ops::create_bcx;
use cargo::util::profile;

Expand All @@ -725,7 +836,7 @@ fn compile_with_exec<'a>(
exec: &Arc<dyn Executor>,
leaf_args: &[String],
global_args: &[String],
) -> CargoResult<Compilation<'a>> {
) -> CargoResult<String> {
ws.emit_warnings()?;
let interner = UnitInterner::new();
let mut bcx = create_bcx(ws, options, &interner)?;
Expand All @@ -741,11 +852,33 @@ fn compile_with_exec<'a>(

if options.build_config.unit_graph {
unit_graph::emit_serialized_unit_graph(&bcx.roots, &bcx.unit_graph, ws.config())?;
return Compilation::new(&bcx);
return Ok(String::new());
}
let _p = profile::start("compiling");
let cx = cargo::core::compiler::Context::new(&bcx)?;
cx.compile(exec)

let r = cx.compile(exec)?;

// NOTE: right now we have only a single cdylib since we have a single package to build.
let out_dir = r
.cdylibs
.iter()
.filter_map(|l| {
l.script_meta.map(|m| {
r.extra_env.get(&m).unwrap().iter().filter_map(|e| {
if e.0 == "OUT_DIR" {
Some(e.1.clone())
} else {
None
}
})
})
})
.flatten()
.next()
.unwrap();

Ok(out_dir)
}

pub fn cbuild(
Expand Down Expand Up @@ -829,10 +962,11 @@ pub fn cbuild(
leaf_args.push("target-feature=+crt-static".into());
}

let build_targets =
BuildTargets::new(&name, &rustc_target, &root_output, &libkinds, &capi_config);
let mut build_targets =
BuildTargets::new(&name, &rustc_target, &root_output, &libkinds, &capi_config)?;

let mut finger_print = FingerPrint::new(&name, &root_output, &build_targets, &install_paths);
let mut finger_print =
FingerPrint::new(&name, &root_output, build_targets.clone(), &install_paths);

let pristine = finger_print.load_previous().is_err();

Expand All @@ -842,14 +976,20 @@ pub fn cbuild(
}

let exec = Arc::new(Exec::default());
let _r = compile_with_exec(
let out_dir = compile_with_exec(
ws,
&compile_opts,
&(exec.clone() as Arc<dyn Executor>),
&leaf_args,
&capi_config.library.rustflags,
)?;

let out_dir = Path::new(&out_dir);

build_targets
.extra
.setup(&capi_config, &root_path, &out_dir)?;

if pristine {
// restore the default to make sure the tests do not trigger a second rebuild.
compile_opts.build_config.force_rebuild = false;
Expand Down Expand Up @@ -903,7 +1043,8 @@ pub fn cbuild(
&root_output,
&libkinds,
&capi_config,
);
)?;

if let (Some(from_static_lib), Some(to_static_lib)) = (
from_build_targets.static_lib.as_ref(),
build_targets.static_lib.as_ref(),
Expand Down
46 changes: 41 additions & 5 deletions src/build_targets.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,43 @@
use std::path::{Path, PathBuf};

use crate::build::CApiConfig;
use crate::build::{CApiConfig, InstallTarget};
use crate::target::Target;

#[derive(Debug)]
#[derive(Debug, Default, Clone)]
pub struct ExtraTargets {
pub include: Vec<(PathBuf, PathBuf)>,
}

impl ExtraTargets {
pub fn setup(
&mut self,
capi_config: &CApiConfig,
root_dir: &Path,
out_dir: &Path,
) -> anyhow::Result<()> {
self.include = extra_targets(&capi_config.install.include, root_dir, out_dir)?;

Ok(())
}
}

fn extra_targets(
targets: &[InstallTarget],
root_path: &Path,
root_output: &Path,
) -> anyhow::Result<Vec<(PathBuf, PathBuf)>> {
use itertools::*;
targets
.iter()
.map(|t| match t {
InstallTarget::Asset(paths) => paths.install_paths(root_path),
InstallTarget::Generated(paths) => paths.install_paths(root_output),
})
.flatten_ok()
.collect()
}

#[derive(Debug, Clone)]
pub struct BuildTargets {
pub include: Option<PathBuf>,
pub static_lib: Option<PathBuf>,
Expand All @@ -12,6 +46,7 @@ pub struct BuildTargets {
pub def: Option<PathBuf>,
pub pc: PathBuf,
pub target: Target,
pub extra: ExtraTargets,
}

impl BuildTargets {
Expand All @@ -21,7 +56,7 @@ impl BuildTargets {
targetdir: &Path,
libkinds: &[&str],
capi_config: &CApiConfig,
) -> BuildTargets {
) -> anyhow::Result<BuildTargets> {
let pc = targetdir.join(&format!("{}.pc", &capi_config.pkg_config.filename));
let include = if capi_config.header.enabled {
let mut header_name = PathBuf::from(&capi_config.header.name);
Expand Down Expand Up @@ -83,14 +118,15 @@ impl BuildTargets {
None
};

BuildTargets {
Ok(BuildTargets {
pc,
include,
static_lib,
shared_lib,
impl_lib,
def,
target: target.clone(),
}
extra: Default::default(),
})
}
}
Loading

0 comments on commit 16ecb60

Please sign in to comment.