Skip to content

Commit

Permalink
Assorted cargo build script fixes (#422)
Browse files Browse the repository at this point in the history
* Rustfmt

* Derive CARGO_CFG env vars from rustc

This is what Cargo does - it means we'll automatically populate all
(most) of the values cargo would.

This fixes up a couple of things:
1. CARGO_CFG_TARGET_OS mismatches - Rustc treats macos as "macos" whereas
   the current code derives "darwin" from the target triple.
2. This sets additional env vars, such as CARGO_CFG_TARGET_ENV,
   CARGO_CFG_TARGET_FAMILY, etc.

* Do exec_root replacement on rustc env vars

Some crates, such as typenum, set rustc env vars to the paths where they
generated output in $OUT_DIR, and use things like:
```rust
include!(env!(...))
```

to read them. This supports that use-case.
  • Loading branch information
illicitonion committed Oct 9, 2020
1 parent 169476f commit e64700d
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 30 deletions.
1 change: 0 additions & 1 deletion cargo/cargo_build_script.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ def _cargo_build_script_run(ctx, script):
cc_toolchain = find_cpp_toolchain(ctx)

env = {
"CARGO_CFG_TARGET_ARCH": toolchain.target_arch,
"CARGO_MANIFEST_DIR": manifest_dir,
"CARGO_PKG_NAME": crate_name,
"HOST": toolchain.exec_triple,
Expand Down
44 changes: 43 additions & 1 deletion cargo/cargo_build_script_runner/bin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
extern crate cargo_build_script_output_parser;

use cargo_build_script_output_parser::{BuildScriptOutput, CompileAndLinkFlags};
use std::collections::BTreeMap;
use std::env;
use std::ffi::OsString;
use std::fs::{create_dir_all, read_to_string, write};
Expand All @@ -43,9 +44,12 @@ fn main() -> Result<(), String> {
// For some reason Google's RBE does not create the output directory, force create it.
create_dir_all(&out_dir_abs).expect(&format!("Failed to make output directory: {:?}", out_dir_abs));

let target_env_vars = get_target_env_vars(&rustc_env).expect("Error getting target env vars from rustc");

let mut command = Command::new(exec_root.join(&progname));
command
.current_dir(manifest_dir.clone())
.envs(target_env_vars)
.env("OUT_DIR", out_dir_abs)
.env("CARGO_MANIFEST_DIR", manifest_dir)
.env("RUSTC", rustc)
Expand Down Expand Up @@ -99,7 +103,7 @@ fn main() -> Result<(), String> {
)
})?;

write(&envfile, BuildScriptOutput::to_env(&output).as_bytes())
write(&envfile, BuildScriptOutput::to_env(&output, &exec_root.to_string_lossy()).as_bytes())
.expect(&format!("Unable to write file {:?}", envfile));
write(&depenvfile, BuildScriptOutput::to_dep_env(&output, &crate_name).as_bytes())
.expect(&format!("Unable to write file {:?}", depenvfile));
Expand All @@ -118,6 +122,44 @@ fn main() -> Result<(), String> {
}
}

fn get_target_env_vars<P: AsRef<Path>>(rustc: &P) -> Result<BTreeMap<String, String>, String> {
// As done by Cargo when constructing a cargo::core::compiler::build_context::target_info::TargetInfo.
let output = Command::new(rustc.as_ref())
.arg("--print=cfg")
.output()
.map_err(|err| format!("Error running rustc to get target information: {}", err))?;
if !output.status.success() {
return Err(format!(
"Error running rustc to get target information: {:?}",
output
));
}
let stdout = std::str::from_utf8(&output.stdout)
.map_err(|err| format!("Non-UTF8 stdout from rustc: {:?}", err))?;

let mut values = BTreeMap::new();

for line in stdout.lines() {
if line.starts_with("target_") && line.contains('=') {
let mut parts = line.splitn(2, '=');
// UNWRAP: Verified that line contains = and split into exactly 2 parts.
let key = parts.next().unwrap();
let value = parts.next().unwrap();
if value.starts_with('"') && value.ends_with('"') && value.len() >= 2 {
values
.entry(key)
.or_insert(vec![])
.push(value[1..(value.len() - 1)].to_owned());
}
}
}

Ok(values
.into_iter()
.map(|(key, value)| (format!("CARGO_CFG_{}", key.to_uppercase()), value.join(",")))
.collect())
}

fn absolutify(root: &Path, maybe_relative: OsString) -> OsString {
let path = Path::new(&maybe_relative);
if path.is_relative() {
Expand Down
81 changes: 53 additions & 28 deletions cargo/cargo_build_script_runner/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl BuildScriptOutput {
if key_split.len() <= 1 || key_split[0] != "cargo" {
// Not a cargo directive.
print!("{}", line);
return None
return None;
}
match key_split[1] {
"rustc-link-lib" => Some(BuildScriptOutput::LinkLib(param)),
Expand All @@ -68,21 +68,30 @@ impl BuildScriptOutput {
"rustc-flags" => Some(BuildScriptOutput::Flags(param)),
"rustc-env" => Some(BuildScriptOutput::Env(param)),
"rerun-if-changed" | "rerun-if-env-changed" =>
// Ignored because Bazel will re-run if those change all the time.
None,
// Ignored because Bazel will re-run if those change all the time.
{
None
}
"warning" => {
eprintln!("Build Script Warning: {}", split[1]);
None
},
}
"rustc-cdylib-link-arg" => {
// cargo:rustc-cdylib-link-arg=FLAG — Passes custom flags to a linker for cdylib crates.
eprintln!("Warning: build script returned unsupported directive `{}`", split[0]);
eprintln!(
"Warning: build script returned unsupported directive `{}`",
split[0]
);
None
},
}
_ => {
// cargo:KEY=VALUE — Metadata, used by links scripts.
Some(BuildScriptOutput::DepEnv(format!("{}={}", key_split[1].to_uppercase(), param)))
},
Some(BuildScriptOutput::DepEnv(format!(
"{}={}",
key_split[1].to_uppercase(),
param
)))
}
}
}

Expand All @@ -101,29 +110,26 @@ impl BuildScriptOutput {

/// Take a [Command], execute it and converts its input into a vector of [BuildScriptOutput]
pub fn from_command(cmd: &mut Command) -> Result<Vec<BuildScriptOutput>, Option<i32>> {
let mut child = cmd.stdout(Stdio::piped()).spawn().expect("Unable to start binary");
let mut child = cmd
.stdout(Stdio::piped())
.spawn()
.expect("Unable to start binary");
let ecode = child.wait().expect("failed to wait on child");
let reader = BufReader::new(
child
.stdout
.as_mut()
.expect("Failed to open stdout"),
);
let reader = BufReader::new(child.stdout.as_mut().expect("Failed to open stdout"));
let output = Self::from_reader(reader);
if ecode.success() {
Ok(output)
} else {
Err(ecode.code())
}

}

/// Convert a vector of [BuildScriptOutput] into a list of environment variables.
pub fn to_env(v: &Vec<BuildScriptOutput>) -> String {
pub fn to_env(v: &Vec<BuildScriptOutput>, exec_root: &str) -> String {
v.iter()
.filter_map(|x| {
if let BuildScriptOutput::Env(env) = x {
Some(env.to_owned())
Some(Self::redact_exec_root(env, exec_root))
} else {
None
}
Expand All @@ -137,7 +143,9 @@ impl BuildScriptOutput {
// TODO: make use of `strip_suffix`.
const SYS_CRATE_SUFFIX: &str = "-sys";
let name = if crate_name.ends_with(SYS_CRATE_SUFFIX) {
crate_name.split_at(crate_name.rfind(SYS_CRATE_SUFFIX).unwrap()).0
crate_name
.split_at(crate_name.rfind(SYS_CRATE_SUFFIX).unwrap())
.0
} else {
crate_name
};
Expand Down Expand Up @@ -165,14 +173,18 @@ impl BuildScriptOutput {
BuildScriptOutput::Flags(e) => compile_flags.push(e.to_owned()),
BuildScriptOutput::LinkLib(e) => link_flags.push(format!("-l{}", e)),
BuildScriptOutput::LinkSearch(e) => link_flags.push(format!("-L{}", e)),
_ => { },
_ => {}
}
}
CompileAndLinkFlags {
compile_flags: compile_flags.join("\n"),
link_flags: link_flags.join("\n").replace(exec_root, "${pwd}"),
link_flags: Self::redact_exec_root(&link_flags.join("\n"), exec_root),
}
}

fn redact_exec_root(value: &str, exec_root: &str) -> String {
value.replace(exec_root, "${pwd}")
}
}

#[cfg(test)]
Expand All @@ -193,30 +205,44 @@ cargo:rerun-if-changed=ignored
cargo:rustc-cfg=feature=awesome
cargo:version=123
cargo:version_number=1010107f
cargo:rustc-env=SOME_PATH=/some/absolute/path/beep
",
);
let reader = BufReader::new(buff);
let result = BuildScriptOutput::from_reader(reader);
assert_eq!(result.len(), 8);
assert_eq!(result.len(), 9);
assert_eq!(result[0], BuildScriptOutput::LinkLib("sdfsdf".to_owned()));
assert_eq!(result[1], BuildScriptOutput::Env("FOO=BAR".to_owned()));
assert_eq!(result[2], BuildScriptOutput::LinkSearch("/some/absolute/path/bleh".to_owned()));
assert_eq!(
result[2],
BuildScriptOutput::LinkSearch("/some/absolute/path/bleh".to_owned())
);
assert_eq!(result[3], BuildScriptOutput::Env("BAR=FOO".to_owned()));
assert_eq!(result[4], BuildScriptOutput::Flags("-Lblah".to_owned()));
assert_eq!(
result[5],
BuildScriptOutput::Cfg("feature=awesome".to_owned())
);
assert_eq!(result[6], BuildScriptOutput::DepEnv("VERSION=123".to_owned()));
assert_eq!(result[7], BuildScriptOutput::DepEnv("VERSION_NUMBER=1010107f".to_owned()));
assert_eq!(
result[6],
BuildScriptOutput::DepEnv("VERSION=123".to_owned())
);
assert_eq!(
result[7],
BuildScriptOutput::DepEnv("VERSION_NUMBER=1010107f".to_owned())
);
assert_eq!(
result[8],
BuildScriptOutput::Env("SOME_PATH=/some/absolute/path/beep".to_owned())
);

assert_eq!(
BuildScriptOutput::to_dep_env(&result, "my-crate-sys"),
"DEP_MY_CRATE_VERSION=123\nDEP_MY_CRATE_VERSION_NUMBER=1010107f".to_owned()
);
assert_eq!(
BuildScriptOutput::to_env(&result),
"FOO=BAR\nBAR=FOO".to_owned()
BuildScriptOutput::to_env(&result, "/some/absolute/path"),
"FOO=BAR\nBAR=FOO\nSOME_PATH=${pwd}/beep".to_owned()
);
assert_eq!(
BuildScriptOutput::to_flags(&result, "/some/absolute/path"),
Expand All @@ -228,5 +254,4 @@ cargo:version_number=1010107f
}
);
}

}

0 comments on commit e64700d

Please sign in to comment.