diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index 461196f6076..ba4f0f8f576 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeSet, HashMap}; use std::env; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; @@ -7,9 +7,8 @@ use cargo_platform::CfgExpr; use semver::Version; use super::BuildContext; -use crate::core::compiler::CompileKind; -use crate::core::compiler::Unit; -use crate::core::{Edition, Package, PackageId}; +use crate::core::compiler::{CompileKind, Metadata, Unit}; +use crate::core::{Edition, Package}; use crate::util::{self, config, join_paths, process, CargoResult, Config, ProcessBuilder}; /// Structure with enough information to run `rustdoc --test`. @@ -22,20 +21,35 @@ pub struct Doctest { pub unstable_opts: bool, /// The -Clinker value to use. pub linker: Option, + /// The script metadata, if this unit's package has a build script. + /// + /// This is used for indexing [`Compilation::extra_env`]. + pub script_meta: Option, +} + +/// Information about the output of a unit. +#[derive(Ord, PartialOrd, Eq, PartialEq)] +pub struct UnitOutput { + /// The unit that generated this output. + pub unit: Unit, + /// Path to the unit's primary output (an executable or cdylib). + pub path: PathBuf, + /// The script metadata, if this unit's package has a build script. + /// + /// This is used for indexing [`Compilation::extra_env`]. + pub script_meta: Option, } /// A structure returning the result of a compilation. pub struct Compilation<'cfg> { /// An array of all tests created during this compilation. - /// `(unit, path_to_test_exe)` where `unit` contains information such as the - /// package, compile target, etc. - pub tests: Vec<(Unit, PathBuf)>, + pub tests: Vec, /// An array of all binaries created. - pub binaries: Vec<(Unit, PathBuf)>, + pub binaries: Vec, /// An array of all cdylibs created. - pub cdylibs: Vec<(Unit, PathBuf)>, + pub cdylibs: Vec, /// All directories for the output of native build commands. /// @@ -60,17 +74,14 @@ pub struct Compilation<'cfg> { /// Extra environment variables that were passed to compilations and should /// be passed to future invocations of programs. - pub extra_env: HashMap>, + /// + /// The key is the build script metadata for uniquely identifying the + /// `RunCustomBuild` unit that generated these env vars. + pub extra_env: HashMap>, /// Libraries to test with rustdoc. pub to_doc_test: Vec, - /// Features per package enabled during this compilation. - pub cfgs: HashMap>, - - /// Flags to pass to rustdoc when invoked from cargo test, per package. - pub rustdocflags: HashMap>, - /// The target host triple. pub host: String, @@ -127,8 +138,6 @@ impl<'cfg> Compilation<'cfg> { cdylibs: Vec::new(), extra_env: HashMap::new(), to_doc_test: Vec::new(), - cfgs: HashMap::new(), - rustdocflags: HashMap::new(), config: bcx.config, host: bcx.host_triple().to_string(), rustc_process: rustc, @@ -144,7 +153,13 @@ impl<'cfg> Compilation<'cfg> { }) } - /// See `process`. + /// Returns a [`ProcessBuilder`] for running `rustc`. + /// + /// `is_primary` is true if this is a "primary package", which means it + /// was selected by the user on the command-line (such as with a `-p` + /// flag), see [`crate::core::compiler::Context::primary_packages`]. + /// + /// `is_workspace` is true if this is a workspace member. pub fn rustc_process( &self, unit: &Unit, @@ -160,14 +175,18 @@ impl<'cfg> Compilation<'cfg> { }; let cmd = fill_rustc_tool_env(rustc, unit); - self.fill_env(cmd, &unit.pkg, unit.kind, true) + self.fill_env(cmd, &unit.pkg, None, unit.kind, true) } - /// See `process`. - pub fn rustdoc_process(&self, unit: &Unit) -> CargoResult { + /// Returns a [`ProcessBuilder`] for running `rustdoc`. + pub fn rustdoc_process( + &self, + unit: &Unit, + script_meta: Option, + ) -> CargoResult { let rustdoc = process(&*self.config.rustdoc()?); let cmd = fill_rustc_tool_env(rustdoc, unit); - let mut p = self.fill_env(cmd, &unit.pkg, unit.kind, true)?; + let mut p = self.fill_env(cmd, &unit.pkg, script_meta, unit.kind, true)?; if unit.target.edition() != Edition::Edition2015 { p.arg(format!("--edition={}", unit.target.edition())); } @@ -179,25 +198,37 @@ impl<'cfg> Compilation<'cfg> { Ok(p) } - /// See `process`. + /// Returns a [`ProcessBuilder`] appropriate for running a process for the + /// host platform. + /// + /// This is currently only used for running build scripts. If you use this + /// for anything else, please be extra careful on how environment + /// variables are set! pub fn host_process>( &self, cmd: T, pkg: &Package, ) -> CargoResult { - self.fill_env(process(cmd), pkg, CompileKind::Host, false) + self.fill_env(process(cmd), pkg, None, CompileKind::Host, false) } pub fn target_runner(&self, kind: CompileKind) -> Option<&(PathBuf, Vec)> { self.target_runners.get(&kind).and_then(|x| x.as_ref()) } - /// See `process`. + /// Returns a [`ProcessBuilder`] appropriate for running a process for the + /// target platform. This is typically used for `cargo run` and `cargo + /// test`. + /// + /// `script_meta` is the metadata for the `RunCustomBuild` unit that this + /// unit used for its build script. Use `None` if the package did not have + /// a build script. pub fn target_process>( &self, cmd: T, kind: CompileKind, pkg: &Package, + script_meta: Option, ) -> CargoResult { let builder = if let Some((runner, args)) = self.target_runner(kind) { let mut builder = process(runner); @@ -207,7 +238,7 @@ impl<'cfg> Compilation<'cfg> { } else { process(cmd) }; - self.fill_env(builder, pkg, kind, false) + self.fill_env(builder, pkg, script_meta, kind, false) } /// Prepares a new process with an appropriate environment to run against @@ -219,6 +250,7 @@ impl<'cfg> Compilation<'cfg> { &self, mut cmd: ProcessBuilder, pkg: &Package, + script_meta: Option, kind: CompileKind, is_rustc_tool: bool, ) -> CargoResult { @@ -258,9 +290,11 @@ impl<'cfg> Compilation<'cfg> { let search_path = join_paths(&search_path, util::dylib_path_envvar())?; cmd.env(util::dylib_path_envvar(), &search_path); - if let Some(env) = self.extra_env.get(&pkg.package_id()) { - for &(ref k, ref v) in env { - cmd.env(k, v); + if let Some(meta) = script_meta { + if let Some(env) = self.extra_env.get(&meta) { + for (k, v) in env { + cmd.env(k, v); + } } } diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index c3a3cb78f66..c1ed092f966 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -1,11 +1,12 @@ use std::collections::{BTreeSet, HashMap, HashSet}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; use filetime::FileTime; use jobserver::Client; -use crate::core::compiler::{self, compilation, Unit}; +use crate::core::compiler::compilation::{self, UnitOutput}; +use crate::core::compiler::{self, Unit}; use crate::core::PackageId; use crate::util::errors::{CargoResult, CargoResultExt}; use crate::util::profile; @@ -174,16 +175,16 @@ impl<'a, 'cfg> Context<'a, 'cfg> { if unit.mode == CompileMode::Test { self.compilation .tests - .push((unit.clone(), output.path.clone())); + .push(self.unit_output(unit, &output.path)); } else if unit.target.is_executable() { self.compilation .binaries - .push((unit.clone(), bindst.clone())); + .push(self.unit_output(unit, bindst)); } else if unit.target.is_cdylib() { - if !self.compilation.cdylibs.iter().any(|(u, _)| u == unit) { + if !self.compilation.cdylibs.iter().any(|uo| uo.unit == *unit) { self.compilation .cdylibs - .push((unit.clone(), bindst.clone())); + .push(self.unit_output(unit, bindst)); } } } @@ -198,9 +199,10 @@ impl<'a, 'cfg> Context<'a, 'cfg> { .build_script_out_dir(&dep.unit) .display() .to_string(); + let script_meta = self.get_run_build_script_metadata(&dep.unit); self.compilation .extra_env - .entry(dep.unit.pkg.package_id()) + .entry(script_meta) .or_insert_with(Vec::new) .push(("OUT_DIR".to_string(), out_dir)); } @@ -212,50 +214,36 @@ impl<'a, 'cfg> Context<'a, 'cfg> { let mut unstable_opts = false; let mut args = compiler::extern_args(&self, unit, &mut unstable_opts)?; args.extend(compiler::lto_args(&self, unit)); + for feature in &unit.features { + args.push("--cfg".into()); + args.push(format!("feature=\"{}\"", feature).into()); + } + let script_meta = self.find_build_script_metadata(unit); + if let Some(meta) = script_meta { + if let Some(output) = self.build_script_outputs.lock().unwrap().get(meta) { + for cfg in &output.cfgs { + args.push("--cfg".into()); + args.push(cfg.into()); + } + } + } + args.extend(self.bcx.rustdocflags_args(unit).iter().map(Into::into)); self.compilation.to_doc_test.push(compilation::Doctest { unit: unit.clone(), args, unstable_opts, linker: self.bcx.linker(unit.kind), + script_meta, }); } - // Collect the enabled features. - let feats = &unit.features; - if !feats.is_empty() { - self.compilation - .cfgs - .entry(unit.pkg.package_id()) - .or_insert_with(|| { - feats - .iter() - .map(|feat| format!("feature=\"{}\"", feat)) - .collect() - }); - } - - // Collect rustdocflags. - let rustdocflags = self.bcx.rustdocflags_args(unit); - if !rustdocflags.is_empty() { - self.compilation - .rustdocflags - .entry(unit.pkg.package_id()) - .or_insert_with(|| rustdocflags.to_vec()); - } - super::output_depinfo(&mut self, unit)?; } - for (pkg_id, output) in self.build_script_outputs.lock().unwrap().iter() { - self.compilation - .cfgs - .entry(pkg_id) - .or_insert_with(HashSet::new) - .extend(output.cfgs.iter().cloned()); - + for (script_meta, output) in self.build_script_outputs.lock().unwrap().iter() { self.compilation .extra_env - .entry(pkg_id) + .entry(*script_meta) .or_insert_with(Vec::new) .extend(output.env.iter().cloned()); @@ -352,11 +340,11 @@ impl<'a, 'cfg> Context<'a, 'cfg> { /// Returns the RunCustomBuild Unit associated with the given Unit. /// /// If the package does not have a build script, this returns None. - pub fn find_build_script_unit(&self, unit: Unit) -> Option { + pub fn find_build_script_unit(&self, unit: &Unit) -> Option { if unit.mode.is_run_custom_build() { - return Some(unit); + return Some(unit.clone()); } - self.bcx.unit_graph[&unit] + self.bcx.unit_graph[unit] .iter() .find(|unit_dep| { unit_dep.unit.mode.is_run_custom_build() @@ -369,7 +357,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { /// the given unit. /// /// If the package does not have a build script, this returns None. - pub fn find_build_script_metadata(&self, unit: Unit) -> Option { + pub fn find_build_script_metadata(&self, unit: &Unit) -> Option { let script_unit = self.find_build_script_unit(unit)?; Some(self.get_run_build_script_metadata(&script_unit)) } @@ -398,6 +386,17 @@ impl<'a, 'cfg> Context<'a, 'cfg> { Ok(inputs.into_iter().collect()) } + /// Returns a [`UnitOutput`] which represents some information about the + /// output of a unit. + pub fn unit_output(&self, unit: &Unit, path: &Path) -> UnitOutput { + let script_meta = self.find_build_script_metadata(unit); + UnitOutput { + unit: unit.clone(), + path: path.to_path_buf(), + script_meta, + } + } + fn check_collisions(&self) -> CargoResult<()> { let mut output_collisions = HashMap::new(); let describe_collision = |unit: &Unit, other_unit: &Unit, path: &PathBuf| -> String { diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index 3a32f02df3d..320f4f7ae70 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -51,7 +51,7 @@ pub struct BuildOutput { /// `Unit` because this structure needs to be shareable between threads. #[derive(Default)] pub struct BuildScriptOutputs { - outputs: HashMap<(PackageId, Metadata), BuildOutput>, + outputs: HashMap, } /// Linking information for a `Unit`. @@ -114,7 +114,7 @@ pub fn prepare(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult { .build_script_outputs .lock() .unwrap() - .contains_key(unit.pkg.package_id(), metadata) + .contains_key(metadata) { // The output is already set, thus the build script is overridden. fingerprint::prepare_target(cx, unit, false) @@ -312,15 +312,12 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult { if !build_plan { let build_script_outputs = build_script_outputs.lock().unwrap(); for (name, dep_id, dep_metadata) in lib_deps { - let script_output = - build_script_outputs - .get(dep_id, dep_metadata) - .ok_or_else(|| { - internal(format!( - "failed to locate build state for env vars: {}/{}", - dep_id, dep_metadata - )) - })?; + let script_output = build_script_outputs.get(dep_metadata).ok_or_else(|| { + internal(format!( + "failed to locate build state for env vars: {}/{}", + dep_id, dep_metadata + )) + })?; let data = &script_output.metadata; for &(ref key, ref value) in data.iter() { cmd.env( @@ -743,7 +740,7 @@ pub fn build_map(cx: &mut Context<'_, '_>) -> CargoResult<()> { // If a package has a build script, add itself as something to inspect for linking. if !unit.target.is_custom_build() && unit.pkg.has_custom_build() { let script_meta = cx - .find_build_script_metadata(unit.clone()) + .find_build_script_metadata(unit) .expect("has_custom_build should have RunCustomBuild"); add_to_link(&mut ret, unit.pkg.package_id(), script_meta); } @@ -829,7 +826,7 @@ fn prev_build_output(cx: &mut Context<'_, '_>, unit: &Unit) -> (Option { entry.insert(parsed_output); } @@ -845,17 +842,17 @@ impl BuildScriptOutputs { } /// Returns `true` if the given key already exists. - fn contains_key(&self, pkg_id: PackageId, metadata: Metadata) -> bool { - self.outputs.contains_key(&(pkg_id, metadata)) + fn contains_key(&self, metadata: Metadata) -> bool { + self.outputs.contains_key(&metadata) } /// Gets the build output for the given key. - pub fn get(&self, pkg_id: PackageId, meta: Metadata) -> Option<&BuildOutput> { - self.outputs.get(&(pkg_id, meta)) + pub fn get(&self, meta: Metadata) -> Option<&BuildOutput> { + self.outputs.get(&meta) } /// Returns an iterator over all entries. - pub fn iter(&self) -> impl Iterator { - self.outputs.iter().map(|(key, value)| (key.0, value)) + pub fn iter(&self) -> impl Iterator { + self.outputs.iter() } } diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index 5077c33645f..76f1a3b02fd 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -441,14 +441,13 @@ pub fn prepare_target(cx: &mut Context<'_, '_>, unit: &Unit, force: bool) -> Car // using the `build_script_local_fingerprints` function which returns a // thunk we can invoke on a foreign thread to calculate this. let build_script_outputs = Arc::clone(&cx.build_script_outputs); - let pkg_id = unit.pkg.package_id(); let metadata = cx.get_run_build_script_metadata(unit); let (gen_local, _overridden) = build_script_local_fingerprints(cx, unit); let output_path = cx.build_explicit_deps[unit].build_script_output.clone(); Work::new(move |_| { let outputs = build_script_outputs.lock().unwrap(); let output = outputs - .get(pkg_id, metadata) + .get(metadata) .expect("output must exist after running"); let deps = BuildDeps::new(&output_path, Some(output)); @@ -1510,7 +1509,7 @@ fn build_script_override_fingerprint( let build_script_outputs = cx.build_script_outputs.lock().unwrap(); let metadata = cx.get_run_build_script_metadata(unit); // Returns None if it is not overridden. - let output = build_script_outputs.get(unit.pkg.package_id(), metadata)?; + let output = build_script_outputs.get(metadata)?; let s = format!( "overridden build state with hash: {}", util::hash_u64(output) diff --git a/src/cargo/core/compiler/job_queue.rs b/src/cargo/core/compiler/job_queue.rs index aa78ef341f3..e6a1977ee42 100644 --- a/src/cargo/core/compiler/job_queue.rs +++ b/src/cargo/core/compiler/job_queue.rs @@ -945,12 +945,12 @@ impl<'cfg> DrainState<'cfg> { cx: &mut Context<'_, '_>, ) -> CargoResult<()> { let outputs = cx.build_script_outputs.lock().unwrap(); - let metadata = match cx.find_build_script_metadata(unit.clone()) { + let metadata = match cx.find_build_script_metadata(unit) { Some(metadata) => metadata, None => return Ok(()), }; let bcx = &mut cx.bcx; - if let Some(output) = outputs.get(unit.pkg.package_id(), metadata) { + if let Some(output) = outputs.get(metadata) { if !output.warnings.is_empty() { if let Some(msg) = msg { writeln!(bcx.config.shell().err(), "{}\n", msg)?; diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 34e92afba62..f670024f981 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -34,7 +34,7 @@ use log::debug; pub use self::build_config::{BuildConfig, CompileMode, MessageFormat}; pub use self::build_context::{BuildContext, FileFlavor, FileType, RustcTargetData, TargetInfo}; use self::build_plan::BuildPlan; -pub use self::compilation::{Compilation, Doctest}; +pub use self::compilation::{Compilation, Doctest, UnitOutput}; pub use self::compile_kind::{CompileKind, CompileTarget}; pub use self::context::{Context, Metadata}; pub use self::crate_type::CrateType; @@ -252,7 +252,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc) -> Car .unwrap_or_else(|| cx.bcx.config.cwd()) .to_path_buf(); let fingerprint_dir = cx.files().fingerprint_dir(unit); - let script_metadata = cx.find_build_script_metadata(unit.clone()); + let script_metadata = cx.find_build_script_metadata(unit); let is_local = unit.is_local(); return Ok(Work::new(move |state| { @@ -276,7 +276,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc) -> Car )?; add_plugin_deps(&mut rustc, &script_outputs, &build_scripts, &root_output)?; } - add_custom_env(&mut rustc, &script_outputs, current_id, script_metadata); + add_custom_env(&mut rustc, &script_outputs, script_metadata); } for output in outputs.iter() { @@ -357,7 +357,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc) -> Car current_id: PackageId, ) -> CargoResult<()> { for key in build_scripts.to_link.iter() { - let output = build_script_outputs.get(key.0, key.1).ok_or_else(|| { + let output = build_script_outputs.get(key.1).ok_or_else(|| { internal(format!( "couldn't find build script output for {}/{}", key.0, key.1 @@ -394,11 +394,10 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc) -> Car fn add_custom_env( rustc: &mut ProcessBuilder, build_script_outputs: &BuildScriptOutputs, - current_id: PackageId, metadata: Option, ) { if let Some(metadata) = metadata { - if let Some(output) = build_script_outputs.get(current_id, metadata) { + if let Some(output) = build_script_outputs.get(metadata) { for &(ref name, ref value) in output.env.iter() { rustc.env(name, value); } @@ -495,7 +494,7 @@ fn add_plugin_deps( let mut search_path = env::split_paths(&search_path).collect::>(); for (pkg_id, metadata) in &build_scripts.plugins { let output = build_script_outputs - .get(*pkg_id, *metadata) + .get(*metadata) .ok_or_else(|| internal(format!("couldn't find libs for plugin dep {}", pkg_id)))?; search_path.append(&mut filter_dynamic_search_path( output.library_paths.iter(), @@ -577,7 +576,8 @@ fn prepare_rustc( fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult { let bcx = cx.bcx; - let mut rustdoc = cx.compilation.rustdoc_process(unit)?; + // script_metadata is not needed here, it is only for tests. + let mut rustdoc = cx.compilation.rustdoc_process(unit, None)?; rustdoc.inherit_jobserver(&cx.jobserver); rustdoc.arg("--crate-name").arg(&unit.target.crate_name()); add_path_args(bcx, unit, &mut rustdoc); @@ -620,16 +620,11 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult { let package_id = unit.pkg.package_id(); let target = Target::clone(&unit.target); let mut output_options = OutputOptions::new(cx, unit); - let pkg_id = unit.pkg.package_id(); - let script_metadata = cx.find_build_script_metadata(unit.clone()); + let script_metadata = cx.find_build_script_metadata(unit); Ok(Work::new(move |state| { if let Some(script_metadata) = script_metadata { - if let Some(output) = build_script_outputs - .lock() - .unwrap() - .get(pkg_id, script_metadata) - { + if let Some(output) = build_script_outputs.lock().unwrap().get(script_metadata) { for cfg in output.cfgs.iter() { rustdoc.arg("--cfg").arg(cfg); } diff --git a/src/cargo/core/compiler/output_depinfo.rs b/src/cargo/core/compiler/output_depinfo.rs index c8c10a516ee..9c1cd3cf942 100644 --- a/src/cargo/core/compiler/output_depinfo.rs +++ b/src/cargo/core/compiler/output_depinfo.rs @@ -79,13 +79,8 @@ fn add_deps_for_unit( } // Add rerun-if-changed dependencies - if let Some(metadata) = cx.find_build_script_metadata(unit.clone()) { - if let Some(output) = cx - .build_script_outputs - .lock() - .unwrap() - .get(unit.pkg.package_id(), metadata) - { + if let Some(metadata) = cx.find_build_script_metadata(unit) { + if let Some(output) = cx.build_script_outputs.lock().unwrap().get(metadata) { for path in &output.rerun_if_changed { deps.insert(path.into()); } diff --git a/src/cargo/ops/cargo_install.rs b/src/cargo/ops/cargo_install.rs index d631838012e..c15c73a215e 100644 --- a/src/cargo/ops/cargo_install.rs +++ b/src/cargo/ops/cargo_install.rs @@ -7,8 +7,7 @@ use anyhow::{bail, format_err}; use semver::VersionReq; use tempfile::Builder as TempFileBuilder; -use crate::core::compiler::Freshness; -use crate::core::compiler::{CompileKind, DefaultExecutor, Executor}; +use crate::core::compiler::{CompileKind, DefaultExecutor, Executor, Freshness, UnitOutput}; use crate::core::{Dependency, Edition, Package, PackageId, Source, SourceId, Workspace}; use crate::ops::common_for_install_and_uninstall::*; use crate::sources::{GitSource, PathSource, SourceConfigMap}; @@ -364,10 +363,10 @@ fn install_one( let mut binaries: Vec<(&str, &Path)> = compile .binaries .iter() - .map(|(_, bin)| { - let name = bin.file_name().unwrap(); + .map(|UnitOutput { path, .. }| { + let name = path.file_name().unwrap(); if let Some(s) = name.to_str() { - Ok((s, bin.as_ref())) + Ok((s, path.as_ref())) } else { bail!("Binary `{:?}` name can't be serialized into string", name) } diff --git a/src/cargo/ops/cargo_run.rs b/src/cargo/ops/cargo_run.rs index 6e8712ff09e..69bae2c5912 100644 --- a/src/cargo/ops/cargo_run.rs +++ b/src/cargo/ops/cargo_run.rs @@ -2,6 +2,7 @@ use std::ffi::OsString; use std::iter; use std::path::Path; +use crate::core::compiler::UnitOutput; use crate::core::{TargetKind, Workspace}; use crate::ops; use crate::util::CargoResult; @@ -80,14 +81,18 @@ pub fn run( let compile = ops::compile(ws, options)?; assert_eq!(compile.binaries.len(), 1); - let (unit, exe) = &compile.binaries[0]; - let exe = match exe.strip_prefix(config.cwd()) { + let UnitOutput { + unit, + path, + script_meta, + } = &compile.binaries[0]; + let exe = match path.strip_prefix(config.cwd()) { Ok(path) if path.file_name() == Some(path.as_os_str()) => Path::new(".").join(path), Ok(path) => path.to_path_buf(), - Err(_) => exe.to_path_buf(), + Err(_) => path.to_path_buf(), }; let pkg = bins[0].0; - let mut process = compile.target_process(exe, unit.kind, pkg)?; + let mut process = compile.target_process(exe, unit.kind, pkg, *script_meta)?; process.args(args).cwd(config.cwd()); config.shell().status("Running", process.to_string())?; diff --git a/src/cargo/ops/cargo_test.rs b/src/cargo/ops/cargo_test.rs index de1a46585ee..a9f50168ca4 100644 --- a/src/cargo/ops/cargo_test.rs +++ b/src/cargo/ops/cargo_test.rs @@ -1,6 +1,6 @@ use std::ffi::OsString; -use crate::core::compiler::{Compilation, CompileKind, Doctest}; +use crate::core::compiler::{Compilation, CompileKind, Doctest, UnitOutput}; use crate::core::shell::Verbosity; use crate::core::Workspace; use crate::ops; @@ -78,10 +78,15 @@ fn run_unit_tests( let cwd = config.cwd(); let mut errors = Vec::new(); - for (unit, exe) in compilation.tests.iter() { + for UnitOutput { + unit, + path, + script_meta, + } in compilation.tests.iter() + { let test = unit.target.name().to_string(); - let exe_display = exe.strip_prefix(cwd).unwrap_or(exe).display(); - let mut cmd = compilation.target_process(exe, unit.kind, &unit.pkg)?; + let exe_display = path.strip_prefix(cwd).unwrap_or(path).display(); + let mut cmd = compilation.target_process(path, unit.kind, &unit.pkg, *script_meta)?; cmd.args(test_args); if unit.target.harness() && config.shell().verbosity() == Verbosity::Quiet { cmd.arg("--quiet"); @@ -145,6 +150,7 @@ fn run_doc_tests( unstable_opts, unit, linker, + script_meta, } = doctest_info; if !doctest_xcompile { @@ -160,7 +166,7 @@ fn run_doc_tests( } config.shell().status("Doc-tests", unit.target.name())?; - let mut p = compilation.rustdoc_process(unit)?; + let mut p = compilation.rustdoc_process(unit, *script_meta)?; p.arg("--test") .arg(unit.target.src_path().path().unwrap()) .arg("--crate-name") @@ -203,23 +209,12 @@ fn run_doc_tests( p.arg("--test-args").arg(arg); } - if let Some(cfgs) = compilation.cfgs.get(&unit.pkg.package_id()) { - for cfg in cfgs.iter() { - p.arg("--cfg").arg(cfg); - } - } - - for arg in args { - p.arg(arg); - } + p.args(args); if *unstable_opts { p.arg("-Zunstable-options"); } - if let Some(flags) = compilation.rustdocflags.get(&unit.pkg.package_id()) { - p.args(flags); - } config .shell() .verbose(|shell| shell.status("Running", p.to_string()))?; diff --git a/src/doc/src/reference/build-scripts.md b/src/doc/src/reference/build-scripts.md index 6e4083a7a3c..7d5805ba703 100644 --- a/src/doc/src/reference/build-scripts.md +++ b/src/doc/src/reference/build-scripts.md @@ -192,6 +192,12 @@ unique identifier of a continuous integration server. See also the [environment variables automatically included by Cargo][env-cargo]. +> **Note**: These environment variables are also set when running an +> executable with `cargo run` or `cargo test`. However, this usage is +> discouraged since it ties the executable to Cargo's execution environment. +> Normally, these environment variables should only be checked at compile-time +> with the `env!` macro. + [env-macro]: ../../std/macro.env.html [env-cargo]: environment-variables.md#environment-variables-cargo-sets-for-crates diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs index cbbcf46cb91..0d5b49a46a9 100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@ -438,18 +438,18 @@ fn cargo_compile_api_exposes_artifact_paths() { let result = cargo::ops::compile(&ws, &compile_options).unwrap(); assert_eq!(1, result.binaries.len()); - assert!(result.binaries[0].1.exists()); + assert!(result.binaries[0].path.exists()); assert!(result.binaries[0] - .1 + .path .to_str() .unwrap() .contains("the_foo_bin")); assert_eq!(1, result.cdylibs.len()); // The exact library path varies by platform, but should certainly exist at least - assert!(result.cdylibs[0].1.exists()); + assert!(result.cdylibs[0].path.exists()); assert!(result.cdylibs[0] - .1 + .path .to_str() .unwrap() .contains("the_foo_lib")); diff --git a/tests/testsuite/build_script.rs b/tests/testsuite/build_script.rs index fcb96bf2442..2965ba98e26 100644 --- a/tests/testsuite/build_script.rs +++ b/tests/testsuite/build_script.rs @@ -4203,3 +4203,96 @@ fn test_with_dep_metadata() { .build(); p.cargo("test --lib").run(); } + +#[cargo_test] +fn duplicate_script_with_extra_env() { + // Test where a build script is run twice, that emits different rustc-env + // and rustc-cfg values. In this case, one is run for host, the other for + // target. + if !cross_compile::can_run_on_host() { + return; + } + + let target = cross_compile::alternate(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "pm"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + pm = { path = "../pm" } + "#, + ) + .file( + "foo/src/lib.rs", + &r#" + //! ```rust + //! #[cfg(not(mycfg="{target}"))] + //! compile_error!{"expected mycfg set"} + //! assert_eq!(env!("CRATE_TARGET"), "{target}"); + //! assert_eq!(std::env::var("CRATE_TARGET").unwrap(), "{target}"); + //! ``` + + #[test] + fn check_target() { + #[cfg(not(mycfg="{target}"))] + compile_error!{"expected mycfg set"} + // Compile-time assertion. + assert_eq!(env!("CRATE_TARGET"), "{target}"); + // Run-time assertion. + assert_eq!(std::env::var("CRATE_TARGET").unwrap(), "{target}"); + } + "# + .replace("{target}", target), + ) + .file( + "foo/build.rs", + r#" + fn main() { + println!("cargo:rustc-env=CRATE_TARGET={}", std::env::var("TARGET").unwrap()); + println!("cargo:rustc-cfg=mycfg=\"{}\"", std::env::var("TARGET").unwrap()); + } + "#, + ) + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + + [lib] + proc-macro = true + # This is just here to speed things up. + doctest = false + + [dev-dependencies] + foo = { path = "../foo" } + "#, + ) + .file("pm/src/lib.rs", "") + .build(); + + p.cargo("test --workspace --target") + .arg(&target) + .with_stdout_contains("test check_target ... ok") + .run(); + + if cargo_test_support::is_nightly() { + p.cargo("test --workspace -Z doctest-xcompile --doc --target") + .arg(&target) + .masquerade_as_nightly_cargo() + .with_stdout_contains("test src/lib.rs - (line 2) ... ok") + .run(); + } +}