From 8b744a05dda5268a8d8d241a054c5cadd3bcdf6e Mon Sep 17 00:00:00 2001 From: Karol Kuczmarski Date: Mon, 17 Apr 2017 19:48:56 +0100 Subject: [PATCH 1/4] Allow cargo:rustc-env in build scripts --- src/cargo/ops/cargo_compile.rs | 1 + src/cargo/ops/cargo_rustc/custom_build.rs | 17 +++++ src/cargo/ops/cargo_rustc/mod.rs | 23 +++++++ src/cargo/util/machine_message.rs | 1 + tests/build-script.rs | 80 ++++++++++++++++++++++- 5 files changed, 120 insertions(+), 2 deletions(-) diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 1256787f4f8..ef5aa927499 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -670,6 +670,7 @@ fn scrape_target_config(config: &Config, triple: &str) library_paths: Vec::new(), library_links: Vec::new(), cfgs: Vec::new(), + env: Vec::new(), metadata: Vec::new(), rerun_if_changed: Vec::new(), warnings: Vec::new(), diff --git a/src/cargo/ops/cargo_rustc/custom_build.rs b/src/cargo/ops/cargo_rustc/custom_build.rs index 4c1dca592fe..64f05f918e8 100644 --- a/src/cargo/ops/cargo_rustc/custom_build.rs +++ b/src/cargo/ops/cargo_rustc/custom_build.rs @@ -21,6 +21,8 @@ pub struct BuildOutput { pub library_links: Vec, /// Various `--cfg` flags to pass to the compiler pub cfgs: Vec, + /// Additional environment variables to run the compiler with. + pub env: Vec<(String, String)>, /// Metadata to pass to the immediate dependencies pub metadata: Vec<(String, String)>, /// Paths to trigger a rerun of this build script. @@ -255,6 +257,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) linked_libs: &parsed_output.library_links, linked_paths: &library_paths, cfgs: &parsed_output.cfgs, + env: &parsed_output.env, }); } @@ -321,6 +324,7 @@ impl BuildOutput { let mut library_paths = Vec::new(); let mut library_links = Vec::new(); let mut cfgs = Vec::new(); + let mut env = Vec::new(); let mut metadata = Vec::new(); let mut rerun_if_changed = Vec::new(); let mut warnings = Vec::new(); @@ -361,6 +365,7 @@ impl BuildOutput { "rustc-link-lib" => library_links.push(value.to_string()), "rustc-link-search" => library_paths.push(PathBuf::from(value)), "rustc-cfg" => cfgs.push(value.to_string()), + "rustc-env" => env.push(BuildOutput::parse_rustc_env(value, &whence)?), "warning" => warnings.push(value.to_string()), "rerun-if-changed" => rerun_if_changed.push(value.to_string()), _ => metadata.push((key.to_string(), value.to_string())), @@ -371,6 +376,7 @@ impl BuildOutput { library_paths: library_paths, library_links: library_links, cfgs: cfgs, + env: env, metadata: metadata, rerun_if_changed: rerun_if_changed, warnings: warnings, @@ -407,6 +413,17 @@ impl BuildOutput { } Ok((library_paths, library_links)) } + + pub fn parse_rustc_env(value: &str, whence: &str) + -> CargoResult<(String, String)> { + let mut iter = value.splitn(2, '='); + let name = iter.next(); + let val = iter.next(); + match (name, val) { + (Some(n), Some(v)) => Ok((n.to_owned(), v.to_owned())), + _ => bail!("Variable rustc-env has no value in {}: {}", whence, value), + } + } } /// Compute the `build_scripts` map in the `Context` which tracks what build diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index 439046eb123..9d9c56d2c19 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -305,12 +305,15 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc) -> CargoResult) -> CargoResult CargoResult<()> { + for key in build_scripts.to_link.iter() { + let output = build_state.get(key).chain_error(|| { + internal(format!("couldn't find build state for {}/{:?}", + key.0, key.1)) + })?; + if key.0 == *current_id { + for &(ref name, ref value) in output.env.iter() { + rustc.env(name, value); + } + } + } + Ok(()) + } } /// Link the compiled target (often of form foo-{metadata_hash}) to the diff --git a/src/cargo/util/machine_message.rs b/src/cargo/util/machine_message.rs index 1d4f33a86da..5f917f85a74 100644 --- a/src/cargo/util/machine_message.rs +++ b/src/cargo/util/machine_message.rs @@ -48,6 +48,7 @@ pub struct BuildScript<'a> { pub linked_libs: &'a [String], pub linked_paths: &'a [String], pub cfgs: &'a [String], + pub env: &'a [(String, String)], } impl<'a> Message for BuildScript<'a> { diff --git a/tests/build-script.rs b/tests/build-script.rs index 8d40474d217..e663f5abc92 100644 --- a/tests/build-script.rs +++ b/tests/build-script.rs @@ -1575,6 +1575,82 @@ fn cfg_override_doc() { assert_that(&p.root().join("target/doc/bar/fn.bar.html"), existing_file()); } +#[test] +fn env_build() { + let build = project("builder") + .file("Cargo.toml", r#" + [package] + name = "builder" + version = "0.0.1" + authors = [] + build = "build.rs" + "#) + .file("src/main.rs", r#" + const FOO: &'static str = env!("FOO"); + fn main() {} + "#) + .file("build.rs", r#" + fn main() { + println!("cargo:rustc-env=FOO=foo"); + } + "#); + assert_that(build.cargo_process("build").arg("-v"), + execs().with_status(0)); +} + +#[test] +fn env_test() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#) + .file("build.rs", r#" + fn main() { + println!("cargo:rustc-env=FOO=foo"); + } + "#) + .file("src/lib.rs", r#" + pub const FOO: &'static str = env!("FOO"); + "#) + .file("tests/test.rs", r#" + extern crate foo; + + #[test] + fn test_foo() { + assert_eq!("foo", foo::FOO); + } + "#); + assert_that(p.cargo_process("test").arg("-v"), + execs().with_stderr(format!("\ +[COMPILING] foo v0.0.1 ({dir}) +[RUNNING] [..] build.rs [..] +[RUNNING] `[..][/]build-script-build` +[RUNNING] [..] --cfg foo[..] +[RUNNING] [..] --cfg foo[..] +[RUNNING] [..] --cfg foo[..] +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `[..][/]foo-[..][EXE]` +[RUNNING] `[..][/]test-[..][EXE]` +[DOCTEST] foo +[RUNNING] [..] --cfg foo[..]", dir = p.url())) + .with_stdout(" +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured + + +running 1 test +test test_foo ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured + +")); +} + #[test] fn flags_go_into_tests() { let p = project("foo") @@ -1816,7 +1892,7 @@ fn fresh_builds_possible_with_link_libs() { rustc-flags = \"-l z -L ./\" ", target)) .file("build.rs", ""); - + assert_that(p.cargo_process("build").arg("-v"), execs().with_status(0).with_stderr("\ [COMPILING] foo v0.5.0 ([..] @@ -1857,7 +1933,7 @@ fn fresh_builds_possible_with_multiple_metadata_overrides() { e = \"\" ", target)) .file("build.rs", ""); - + assert_that(p.cargo_process("build").arg("-v"), execs().with_status(0).with_stderr("\ [COMPILING] foo v0.5.0 ([..] From f1ae9f840fb23e51ef9cbc9d581a3eef33a7e2a9 Mon Sep 17 00:00:00 2001 From: Karol Kuczmarski Date: Fri, 12 May 2017 21:47:28 +0100 Subject: [PATCH 2/4] Fix PR comments --- src/cargo/ops/cargo_compile.rs | 6 ++++++ src/cargo/ops/cargo_rustc/mod.rs | 15 +++++---------- tests/build-script.rs | 6 +++++- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index ef5aa927499..861f95bd726 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -709,6 +709,12 @@ fn scrape_target_config(config: &Config, triple: &str) let list = value.list(&k)?; output.cfgs.extend(list.iter().map(|v| v.0.clone())); } + "rustc-env" => { + for (name, val) in value.table(&k)?.0 { + let val = val.string(name)?.0; + output.env.push((name.clone(), val.to_string())); + } + } "warning" | "rerun-if-changed" => { bail!("`{}` is not supported in build script overrides", k); } diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index 9d9c56d2c19..bf46397c7a7 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -428,17 +428,12 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc) -> CargoResult CargoResult<()> { - for key in build_scripts.to_link.iter() { - let output = build_state.get(key).chain_error(|| { - internal(format!("couldn't find build state for {}/{:?}", - key.0, key.1)) - })?; - if key.0 == *current_id { - for &(ref name, ref value) in output.env.iter() { - rustc.env(name, value); - } + let key = (current_id.clone(), Kind::Host); + if let Some(output) = build_state.get(&key) { + for &(ref name, ref value) in output.env.iter() { + rustc.env(name, value); } } Ok(()) diff --git a/tests/build-script.rs b/tests/build-script.rs index e663f5abc92..31dbd7b466a 100644 --- a/tests/build-script.rs +++ b/tests/build-script.rs @@ -1587,7 +1587,9 @@ fn env_build() { "#) .file("src/main.rs", r#" const FOO: &'static str = env!("FOO"); - fn main() {} + fn main() { + println!("{}", FOO); + } "#) .file("build.rs", r#" fn main() { @@ -1596,6 +1598,8 @@ fn env_build() { "#); assert_that(build.cargo_process("build").arg("-v"), execs().with_status(0)); + assert_that(build.cargo("run").arg("-v"), + execs().with_status(0).with_stdout("foo\n")); } #[test] From cb1e615715a5d52960105a3972a1b7822b901688 Mon Sep 17 00:00:00 2001 From: Karol Kuczmarski Date: Sat, 13 May 2017 16:51:40 +0100 Subject: [PATCH 3/4] Fix build script envvars for `cargo test/doc` --- src/cargo/ops/cargo_rustc/mod.rs | 16 +++++++++--- tests/build-script.rs | 44 ++++++++++++++++++++++++++------ 2 files changed, 48 insertions(+), 12 deletions(-) diff --git a/src/cargo/ops/cargo_rustc/mod.rs b/src/cargo/ops/cargo_rustc/mod.rs index bf46397c7a7..470f700f590 100644 --- a/src/cargo/ops/cargo_rustc/mod.rs +++ b/src/cargo/ops/cargo_rustc/mod.rs @@ -195,6 +195,10 @@ pub fn compile_targets<'a, 'cfg: 'a>(ws: &Workspace<'cfg>, .or_insert_with(HashSet::new) .extend(output.cfgs.iter().cloned()); + cx.compilation.extra_env.entry(pkg.clone()) + .or_insert_with(Vec::new) + .extend(output.env.iter().cloned()); + for dir in output.library_paths.iter() { cx.compilation.native_dirs.insert(dir.clone()); } @@ -265,6 +269,7 @@ fn rustc(cx: &mut Context, unit: &Unit, exec: Arc) -> CargoResult) -> CargoResult) -> CargoResult CargoResult<()> { - let key = (current_id.clone(), Kind::Host); + current_id: &PackageId, + kind: Kind) -> CargoResult<()> { + let key = (current_id.clone(), kind); if let Some(output) = build_state.get(&key) { for &(ref name, ref value) in output.env.iter() { rustc.env(name, value); @@ -617,6 +622,9 @@ fn rustdoc(cx: &mut Context, unit: &Unit) -> CargoResult { for cfg in output.cfgs.iter() { rustdoc.arg("--cfg").arg(cfg); } + for &(ref name, ref value) in output.env.iter() { + rustdoc.env(name, value); + } } state.running(&rustdoc); rustdoc.exec().chain_error(|| { diff --git a/tests/build-script.rs b/tests/build-script.rs index 31dbd7b466a..7356af284c1 100644 --- a/tests/build-script.rs +++ b/tests/build-script.rs @@ -1577,10 +1577,10 @@ fn cfg_override_doc() { #[test] fn env_build() { - let build = project("builder") + let p = project("foo") .file("Cargo.toml", r#" [package] - name = "builder" + name = "foo" version = "0.0.1" authors = [] build = "build.rs" @@ -1596,9 +1596,9 @@ fn env_build() { println!("cargo:rustc-env=FOO=foo"); } "#); - assert_that(build.cargo_process("build").arg("-v"), + assert_that(p.cargo_process("build").arg("-v"), execs().with_status(0)); - assert_that(build.cargo("run").arg("-v"), + assert_that(p.cargo("run").arg("-v"), execs().with_status(0).with_stdout("foo\n")); } @@ -1633,14 +1633,14 @@ fn env_test() { [COMPILING] foo v0.0.1 ({dir}) [RUNNING] [..] build.rs [..] [RUNNING] `[..][/]build-script-build` -[RUNNING] [..] --cfg foo[..] -[RUNNING] [..] --cfg foo[..] -[RUNNING] [..] --cfg foo[..] +[RUNNING] [..] --crate-name foo[..] +[RUNNING] [..] --crate-name foo[..] +[RUNNING] [..] --crate-name test[..] [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] [RUNNING] `[..][/]foo-[..][EXE]` [RUNNING] `[..][/]test-[..][EXE]` [DOCTEST] foo -[RUNNING] [..] --cfg foo[..]", dir = p.url())) +[RUNNING] [..] --crate-name foo[..]", dir = p.url())) .with_stdout(" running 0 tests @@ -1652,9 +1652,37 @@ test test_foo ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured + +running 0 tests + +test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured + ")); } +#[test] +fn env_doc() { + let p = project("foo") + .file("Cargo.toml", r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + build = "build.rs" + "#) + .file("src/main.rs", r#" + const FOO: &'static str = env!("FOO"); + fn main() {} + "#) + .file("build.rs", r#" + fn main() { + println!("cargo:rustc-env=FOO=foo"); + } + "#); + assert_that(p.cargo_process("doc").arg("-v"), + execs().with_status(0)); +} + #[test] fn flags_go_into_tests() { let p = project("foo") From 8e6ffa5cb534ae50b27c79cd64a083772c84c8d1 Mon Sep 17 00:00:00 2001 From: Karol Kuczmarski Date: Sat, 13 May 2017 17:00:52 +0100 Subject: [PATCH 4/4] Document the feature --- src/doc/build-script.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/doc/build-script.md b/src/doc/build-script.md index 586dd29f056..b747ed0c095 100644 --- a/src/doc/build-script.md +++ b/src/doc/build-script.md @@ -51,6 +51,7 @@ All the lines printed to stdout by a build script are written to a file like `ta cargo:rustc-link-lib=static=foo cargo:rustc-link-search=native=/path/to/foo cargo:rustc-cfg=foo +cargo:rustc-env=FOO=bar # arbitrary user-defined metadata cargo:root=/path/to/foo cargo:libdir=/path/to/foo/lib @@ -73,6 +74,12 @@ crate is built: * `rustc-cfg=FEATURE` indicates that the specified feature will be passed as a `--cfg` flag to the compiler. This is often useful for performing compile-time detection of various features. +* `rustc-env=VAR=VALUE` indicates that the specified environment variable + will be added to the environment which the compiler is run within. + The value can be then retrieved by the `env!` macro in the compiled crate. + This is useful for embedding additional metadata in crate's code, + such as the hash of Git HEAD or the unique identifier of a continuous + integration server. * `rerun-if-changed=PATH` is a path to a file or directory which indicates that the build script should be re-run if it changes (detected by a more-recent last-modified timestamp on the file). Normally build scripts are re-run if