From 3fd28143de72d2d0e63186273d68aa890d8297e7 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 27 Apr 2020 12:28:39 -0700 Subject: [PATCH] Support multiple `--target` flags on the CLI This commit refactors the internals of Cargo to no longer have a singular `--target` flag (and singular `requested_target` kind throught) but to instead have a list. The semantics of multiple `--target` flags is to build the selected targets for each of the input `--target` flag inputs. For now this is gated behind `-Zmultitarget` as an unstable features, since I'm not entirely sure this is the interface we want. In general it'd be great if we had a way to simply specify `Unit` structures of what to build on the CLI, but we're in general very far away from that, so I figured that this is probably sufficient at least for testing for now. cc #8156 --- src/bin/cargo/commands/clean.rs | 2 +- src/bin/cargo/commands/fetch.rs | 2 +- src/bin/cargo/commands/metadata.rs | 14 +- src/bin/cargo/commands/package.rs | 2 +- src/bin/cargo/commands/publish.rs | 2 +- src/bin/cargo/commands/tree.rs | 8 +- src/cargo/core/compiler/build_config.rs | 17 +- .../compiler/build_context/target_info.rs | 42 ++-- src/cargo/core/compiler/compilation.rs | 127 +++++++------ src/cargo/core/compiler/compile_kind.rs | 56 ++++-- src/cargo/core/compiler/context/mod.rs | 43 +++-- src/cargo/core/compiler/mod.rs | 4 +- src/cargo/core/compiler/standard_lib.rs | 70 ++++--- src/cargo/core/compiler/timings.rs | 8 +- src/cargo/core/compiler/unit_dependencies.rs | 16 +- src/cargo/core/features.rs | 2 + src/cargo/core/package.rs | 16 +- src/cargo/core/resolver/features.rs | 13 +- src/cargo/ops/cargo_clean.rs | 16 +- src/cargo/ops/cargo_compile.rs | 179 +++++++++--------- src/cargo/ops/cargo_doc.rs | 16 +- src/cargo/ops/cargo_fetch.rs | 43 ++--- src/cargo/ops/cargo_install.rs | 5 +- src/cargo/ops/cargo_output_metadata.rs | 35 ++-- src/cargo/ops/cargo_package.rs | 4 +- src/cargo/ops/cargo_run.rs | 7 +- src/cargo/ops/cargo_test.rs | 79 ++++---- src/cargo/ops/registry.rs | 4 +- src/cargo/ops/resolve.rs | 6 +- src/cargo/ops/tree/graph.rs | 30 +-- src/cargo/ops/tree/mod.rs | 26 +-- src/cargo/util/command_prelude.rs | 8 +- src/doc/src/reference/unstable.md | 19 ++ tests/testsuite/cross_compile.rs | 1 - tests/testsuite/main.rs | 1 + tests/testsuite/multitarget.rs | 144 ++++++++++++++ tests/testsuite/plugins.rs | 2 +- tests/testsuite/run.rs | 1 + tests/testsuite/standard_lib.rs | 2 +- 39 files changed, 659 insertions(+), 413 deletions(-) create mode 100644 tests/testsuite/multitarget.rs diff --git a/src/bin/cargo/commands/clean.rs b/src/bin/cargo/commands/clean.rs index 22ceece0d68..6e767ddfc41 100644 --- a/src/bin/cargo/commands/clean.rs +++ b/src/bin/cargo/commands/clean.rs @@ -28,7 +28,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { let opts = CleanOptions { config, spec: values(args, "package"), - target: args.target(), + targets: args.targets(), requested_profile: args.get_profile_name(config, "dev", ProfileChecking::Checked)?, profile_specified: args.is_present("profile") || args.is_present("release"), doc: args.is_present("doc"), diff --git a/src/bin/cargo/commands/fetch.rs b/src/bin/cargo/commands/fetch.rs index 0bac9153e5d..b4f19685d07 100644 --- a/src/bin/cargo/commands/fetch.rs +++ b/src/bin/cargo/commands/fetch.rs @@ -28,7 +28,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { let opts = FetchOptions { config, - target: args.target(), + targets: args.targets(), }; let _ = ops::fetch(&ws, &opts)?; Ok(()) diff --git a/src/bin/cargo/commands/metadata.rs b/src/bin/cargo/commands/metadata.rs index 9ac181c861f..1130f074e8f 100644 --- a/src/bin/cargo/commands/metadata.rs +++ b/src/bin/cargo/commands/metadata.rs @@ -12,13 +12,11 @@ pub fn cli() -> App { ) .arg(opt("quiet", "No output printed to stdout").short("q")) .arg_features() - .arg( - opt( - "filter-platform", - "Only include resolve dependencies matching the given target-triple", - ) - .value_name("TRIPLE"), - ) + .arg(multi_opt( + "filter-platform", + "TRIPLE", + "Only include resolve dependencies matching the given target-triple", + )) .arg(opt( "no-deps", "Output information only about the workspace members \ @@ -51,7 +49,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { all_features: args.is_present("all-features"), no_default_features: args.is_present("no-default-features"), no_deps: args.is_present("no-deps"), - filter_platform: args.value_of("filter-platform").map(|s| s.to_string()), + filter_platforms: args._values_of("filter-platform"), version, }; diff --git a/src/bin/cargo/commands/package.rs b/src/bin/cargo/commands/package.rs index 772ea21a2c0..7a0550ab3b0 100644 --- a/src/bin/cargo/commands/package.rs +++ b/src/bin/cargo/commands/package.rs @@ -42,7 +42,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { list: args.is_present("list"), check_metadata: !args.is_present("no-metadata"), allow_dirty: args.is_present("allow-dirty"), - target: args.target(), + targets: args.targets(), jobs: args.jobs()?, features: args._values_of("features"), all_features: args.is_present("all-features"), diff --git a/src/bin/cargo/commands/publish.rs b/src/bin/cargo/commands/publish.rs index 51c2532d5b7..cedb1bc466b 100644 --- a/src/bin/cargo/commands/publish.rs +++ b/src/bin/cargo/commands/publish.rs @@ -40,7 +40,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { index, verify: !args.is_present("no-verify"), allow_dirty: args.is_present("allow-dirty"), - target: args.target(), + targets: args.targets(), jobs: args.jobs()?, dry_run: args.is_present("dry-run"), registry, diff --git a/src/bin/cargo/commands/tree.rs b/src/bin/cargo/commands/tree.rs index 95677d81cc9..b98ee5f7aad 100644 --- a/src/bin/cargo/commands/tree.rs +++ b/src/bin/cargo/commands/tree.rs @@ -129,15 +129,15 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult { )?; } - let target = if args.is_present("all-targets") { + let targets = if args.is_present("all-targets") { config .shell() .warn("the --all-targets flag has been changed to --target=all")?; - Some("all") + vec!["all".to_string()] } else { - args.value_of("target") + args._values_of("target") }; - let target = tree::Target::from_cli(target); + let target = tree::Target::from_cli(targets); let edge_kinds = parse_edge_kinds(config, args)?; let graph_features = edge_kinds.contains(&EdgeKind::Feature); diff --git a/src/cargo/core/compiler/build_config.rs b/src/cargo/core/compiler/build_config.rs index 2f07d5d20d5..a05c6e3b7a5 100644 --- a/src/cargo/core/compiler/build_config.rs +++ b/src/cargo/core/compiler/build_config.rs @@ -2,6 +2,7 @@ use crate::core::compiler::CompileKind; use crate::core::interning::InternedString; use crate::util::ProcessBuilder; use crate::util::{CargoResult, Config, RustfixDiagnosticServer}; +use anyhow::bail; use serde::ser; use std::cell::RefCell; use std::path::PathBuf; @@ -10,7 +11,7 @@ use std::path::PathBuf; #[derive(Debug)] pub struct BuildConfig { /// The requested kind of compilation for this session - pub requested_kind: CompileKind, + pub requested_kinds: Vec, /// Number of rustc jobs to run in parallel. pub jobs: u32, /// Build profile @@ -50,12 +51,11 @@ impl BuildConfig { pub fn new( config: &Config, jobs: Option, - requested_target: &Option, + requested_targets: &[String], mode: CompileMode, ) -> CargoResult { let cfg = config.build_config()?; - let requested_kind = - CompileKind::from_requested_target(config, requested_target.as_deref())?; + let requested_kinds = CompileKind::from_requested_targets(config, requested_targets)?; if jobs == Some(0) { anyhow::bail!("jobs must be at least 1") } @@ -69,7 +69,7 @@ impl BuildConfig { let jobs = jobs.or(cfg.jobs).unwrap_or(::num_cpus::get() as u32); Ok(BuildConfig { - requested_kind, + requested_kinds, jobs, requested_profile: InternedString::new("dev"), mode, @@ -95,6 +95,13 @@ impl BuildConfig { pub fn test(&self) -> bool { self.mode == CompileMode::Test || self.mode == CompileMode::Bench } + + pub fn single_requested_kind(&self) -> CargoResult { + match self.requested_kinds.len() { + 1 => Ok(self.requested_kinds[0]), + _ => bail!("only one `--target` argument is supported"), + } + } } #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs index 5b7dac40bab..28d9589e1ef 100644 --- a/src/cargo/core/compiler/build_context/target_info.rs +++ b/src/cargo/core/compiler/build_context/target_info.rs @@ -90,11 +90,18 @@ impl FileType { impl TargetInfo { pub fn new( config: &Config, - requested_kind: CompileKind, + requested_kinds: &[CompileKind], rustc: &Rustc, kind: CompileKind, ) -> CargoResult { - let rustflags = env_args(config, requested_kind, &rustc.host, None, kind, "RUSTFLAGS")?; + let rustflags = env_args( + config, + requested_kinds, + &rustc.host, + None, + kind, + "RUSTFLAGS", + )?; let mut process = rustc.process(); process .arg("-") @@ -180,7 +187,7 @@ impl TargetInfo { // information rustflags: env_args( config, - requested_kind, + requested_kinds, &rustc.host, Some(&cfg), kind, @@ -188,7 +195,7 @@ impl TargetInfo { )?, rustdocflags: env_args( config, - requested_kind, + requested_kinds, &rustc.host, Some(&cfg), kind, @@ -416,7 +423,7 @@ fn output_err_info(cmd: &ProcessBuilder, stdout: &str, stderr: &str) -> String { /// scripts, ...), even if it is the same as the target. fn env_args( config: &Config, - requested_kind: CompileKind, + requested_kinds: &[CompileKind], host_triple: &str, target_cfg: Option<&[Cfg]>, kind: CompileKind, @@ -441,7 +448,7 @@ fn env_args( // This means that, e.g., even if the specified --target is the // same as the host, build scripts in plugins won't get // RUSTFLAGS. - if !requested_kind.is_host() && kind.is_host() { + if requested_kinds != &[CompileKind::Host] && kind.is_host() { // This is probably a build script or plugin and we're // compiling with --target. In this scenario there are // no rustflags we can apply. @@ -526,20 +533,25 @@ pub struct RustcTargetData { } impl RustcTargetData { - pub fn new(ws: &Workspace<'_>, requested_kind: CompileKind) -> CargoResult { + pub fn new( + ws: &Workspace<'_>, + requested_kinds: &[CompileKind], + ) -> CargoResult { let config = ws.config(); let rustc = config.load_global_rustc(Some(ws))?; let host_config = config.target_cfg_triple(&rustc.host)?; - let host_info = TargetInfo::new(config, requested_kind, &rustc, CompileKind::Host)?; + let host_info = TargetInfo::new(config, requested_kinds, &rustc, CompileKind::Host)?; let mut target_config = HashMap::new(); let mut target_info = HashMap::new(); - if let CompileKind::Target(target) = requested_kind { - let tcfg = config.target_cfg_triple(target.short_name())?; - target_config.insert(target, tcfg); - target_info.insert( - target, - TargetInfo::new(config, requested_kind, &rustc, CompileKind::Target(target))?, - ); + for kind in requested_kinds { + if let CompileKind::Target(target) = *kind { + let tcfg = config.target_cfg_triple(target.short_name())?; + target_config.insert(target, tcfg); + target_info.insert( + target, + TargetInfo::new(config, requested_kinds, &rustc, *kind)?, + ); + } } Ok(RustcTargetData { diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index 25a8bdef069..efcaff1355e 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -8,15 +8,14 @@ use semver::Version; use super::BuildContext; use crate::core::compiler::CompileKind; -use crate::core::{Edition, Package, PackageId, Target}; +use crate::core::compiler::Unit; +use crate::core::{Edition, Package, PackageId}; use crate::util::{self, config, join_paths, process, CargoResult, Config, ProcessBuilder}; /// Structure with enough information to run `rustdoc --test`. pub struct Doctest { - /// The package being doc-tested. - pub package: Package, - /// The target being tested (currently always the package's lib). - pub target: Target, + /// What's being doctested + pub unit: Unit, /// Arguments needed to pass to rustdoc to run this test. pub args: Vec, /// Whether or not -Zunstable-options is needed. @@ -26,11 +25,12 @@ pub struct Doctest { /// A structure returning the result of a compilation. pub struct Compilation<'cfg> { /// An array of all tests created during this compilation. - /// `(package, target, path_to_test_exe)` - pub tests: Vec<(Package, Target, PathBuf)>, + /// `(unit, path_to_test_exe)` where `unit` contains information such as the + /// package, compile target, etc. + pub tests: Vec<(Unit, PathBuf)>, /// An array of all binaries created. - pub binaries: Vec, + pub binaries: Vec<(Unit, PathBuf)>, /// All directories for the output of native build commands. /// @@ -41,20 +41,17 @@ pub struct Compilation<'cfg> { pub native_dirs: BTreeSet, /// Root output directory (for the local package's artifacts) - pub root_output: PathBuf, + pub root_output: HashMap, /// Output directory for rust dependencies. /// May be for the host or for a specific target. - pub deps_output: PathBuf, + pub deps_output: HashMap, - /// Output directory for the rust host dependencies. - pub host_deps_output: PathBuf, + /// The path to the host libdir for the compiler used + sysroot_host_libdir: PathBuf, - /// The path to rustc's own libstd - pub host_dylib_path: PathBuf, - - /// The path to libstd for the target - pub target_dylib_path: PathBuf, + /// The path to libstd for each target + sysroot_target_libdir: HashMap, /// Extra environment variables that were passed to compilations and should /// be passed to future invocations of programs. @@ -69,9 +66,6 @@ pub struct Compilation<'cfg> { /// Flags to pass to rustdoc when invoked from cargo test, per package. pub rustdocflags: HashMap>, - pub host: String, - pub target: String, - config: &'cfg Config, /// Rustc process to be used by default @@ -82,7 +76,7 @@ pub struct Compilation<'cfg> { /// rustc_workspace_wrapper_process primary_rustc_process: Option, - target_runner: Option<(PathBuf, Vec)>, + target_runners: HashMap)>>, } impl<'cfg> Compilation<'cfg> { @@ -100,23 +94,28 @@ impl<'cfg> Compilation<'cfg> { } } - let default_kind = bcx.build_config.requested_kind; Ok(Compilation { // TODO: deprecated; remove. native_dirs: BTreeSet::new(), - root_output: PathBuf::from("/"), - deps_output: PathBuf::from("/"), - host_deps_output: PathBuf::from("/"), - host_dylib_path: bcx + root_output: HashMap::new(), + deps_output: HashMap::new(), + sysroot_host_libdir: bcx .target_data .info(CompileKind::Host) .sysroot_host_libdir .clone(), - target_dylib_path: bcx - .target_data - .info(default_kind) - .sysroot_target_libdir - .clone(), + sysroot_target_libdir: bcx + .build_config + .requested_kinds + .iter() + .chain(Some(&CompileKind::Host)) + .map(|kind| { + ( + *kind, + bcx.target_data.info(*kind).sysroot_target_libdir.clone(), + ) + }) + .collect(), tests: Vec::new(), binaries: Vec::new(), extra_env: HashMap::new(), @@ -127,16 +126,20 @@ impl<'cfg> Compilation<'cfg> { rustc_process: rustc, rustc_workspace_wrapper_process, primary_rustc_process, - host: bcx.host_triple().to_string(), - target: bcx.target_data.short_name(&default_kind).to_string(), - target_runner: target_runner(bcx, default_kind)?, + target_runners: bcx + .build_config + .requested_kinds + .iter() + .chain(Some(&CompileKind::Host)) + .map(|kind| Ok((*kind, target_runner(bcx, *kind)?))) + .collect::>>()?, }) } /// See `process`. pub fn rustc_process( &self, - pkg: &Package, + unit: &Unit, is_primary: bool, is_workspace: bool, ) -> CargoResult { @@ -148,17 +151,22 @@ impl<'cfg> Compilation<'cfg> { self.rustc_process.clone() }; - self.fill_env(rustc, pkg, true) + self.fill_env(rustc, &unit.pkg, unit.kind, true) } /// See `process`. - pub fn rustdoc_process(&self, pkg: &Package, target: &Target) -> CargoResult { - let mut p = self.fill_env(process(&*self.config.rustdoc()?), pkg, false)?; - if target.edition() != Edition::Edition2015 { - p.arg(format!("--edition={}", target.edition())); + pub fn rustdoc_process(&self, unit: &Unit) -> CargoResult { + let mut p = self.fill_env( + process(&*self.config.rustdoc()?), + &unit.pkg, + unit.kind, + true, + )?; + if unit.target.edition() != Edition::Edition2015 { + p.arg(format!("--edition={}", unit.target.edition())); } - for crate_type in target.rustc_crate_types() { + for crate_type in unit.target.rustc_crate_types() { p.arg("--crate-type").arg(crate_type); } @@ -171,20 +179,21 @@ impl<'cfg> Compilation<'cfg> { cmd: T, pkg: &Package, ) -> CargoResult { - self.fill_env(process(cmd), pkg, true) + self.fill_env(process(cmd), pkg, CompileKind::Host, false) } - pub fn target_runner(&self) -> &Option<(PathBuf, Vec)> { - &self.target_runner + pub fn target_runner(&self, kind: CompileKind) -> Option<&(PathBuf, Vec)> { + self.target_runners.get(&kind).and_then(|x| x.as_ref()) } /// See `process`. pub fn target_process>( &self, cmd: T, + kind: CompileKind, pkg: &Package, ) -> CargoResult { - let builder = if let Some((ref runner, ref args)) = *self.target_runner() { + let builder = if let Some((runner, args)) = self.target_runner(kind) { let mut builder = process(runner); builder.args(args); builder.arg(cmd); @@ -192,7 +201,7 @@ impl<'cfg> Compilation<'cfg> { } else { process(cmd) }; - self.fill_env(builder, pkg, false) + self.fill_env(builder, pkg, kind, false) } /// Prepares a new process with an appropriate environment to run against @@ -204,26 +213,28 @@ impl<'cfg> Compilation<'cfg> { &self, mut cmd: ProcessBuilder, pkg: &Package, - is_host: bool, + kind: CompileKind, + is_rustc_tool: bool, ) -> CargoResult { - let mut search_path = if is_host { - let mut search_path = vec![self.host_deps_output.clone()]; - search_path.push(self.host_dylib_path.clone()); - search_path + let mut search_path = Vec::new(); + if is_rustc_tool { + search_path.push(self.deps_output[&CompileKind::Host].clone()); + search_path.push(self.sysroot_host_libdir.clone()); } else { - let mut search_path = - super::filter_dynamic_search_path(self.native_dirs.iter(), &self.root_output); - search_path.push(self.deps_output.clone()); - search_path.push(self.root_output.clone()); + search_path.extend(super::filter_dynamic_search_path( + self.native_dirs.iter(), + &self.root_output[&kind], + )); + search_path.push(self.deps_output[&kind].clone()); + search_path.push(self.root_output[&kind].clone()); // For build-std, we don't want to accidentally pull in any shared // libs from the sysroot that ships with rustc. This may not be // required (at least I cannot craft a situation where it // matters), but is here to be safe. if self.config.cli_unstable().build_std.is_none() { - search_path.push(self.target_dylib_path.clone()); + search_path.push(self.sysroot_target_libdir[&kind].clone()); } - search_path - }; + } let dylib_path = util::dylib_path(); let dylib_path_is_empty = dylib_path.is_empty(); diff --git a/src/cargo/core/compiler/compile_kind.rs b/src/cargo/core/compiler/compile_kind.rs index fd5537b4abb..3f8d1e9d819 100644 --- a/src/cargo/core/compiler/compile_kind.rs +++ b/src/cargo/core/compiler/compile_kind.rs @@ -1,7 +1,9 @@ use crate::core::{InternedString, Target}; use crate::util::errors::{CargoResult, CargoResultExt}; use crate::util::Config; +use anyhow::bail; use serde::Serialize; +use std::collections::BTreeSet; use std::path::Path; /// Indicator for how a unit is being compiled. @@ -41,30 +43,42 @@ impl CompileKind { } } - /// Creates a new `CompileKind` based on the requested target. + /// Creates a new list of `CompileKind` based on the requested list of + /// targets. /// - /// If no target is given, this consults the config if the default is set. - /// Otherwise returns `CompileKind::Host`. - pub fn from_requested_target( + /// If no targets are given then this returns a single-element vector with + /// `CompileKind::Host`. + pub fn from_requested_targets( config: &Config, - target: Option<&str>, - ) -> CargoResult { - let kind = match target { - Some(s) => CompileKind::Target(CompileTarget::new(s)?), - None => match &config.build_config()?.target { - Some(val) => { - let value = if val.raw_value().ends_with(".json") { - let path = val.clone().resolve_path(config); - path.to_str().expect("must be utf-8 in toml").to_string() - } else { - val.raw_value().to_string() - }; - CompileKind::Target(CompileTarget::new(&value)?) - } - None => CompileKind::Host, - }, + targets: &[String], + ) -> CargoResult> { + if targets.len() > 1 && !config.cli_unstable().multitarget { + bail!("specifying multiple `--target` flags requires `-Zmultitarget`") + } + if targets.len() != 0 { + return Ok(targets + .iter() + .map(|value| Ok(CompileKind::Target(CompileTarget::new(value)?))) + // First collect into a set to deduplicate any `--target` passed + // more than once... + .collect::>>()? + // ... then generate a flat list for everything else to use. + .into_iter() + .collect()); + } + let kind = match &config.build_config()?.target { + Some(val) => { + let value = if val.raw_value().ends_with(".json") { + let path = val.clone().resolve_path(config); + path.to_str().expect("must be utf-8 in toml").to_string() + } else { + val.raw_value().to_string() + }; + CompileKind::Target(CompileTarget::new(&value)?) + } + None => CompileKind::Host, }; - Ok(kind) + Ok(vec![kind]) } } diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index c1df6c50bf4..0855fc09ce7 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -165,13 +165,13 @@ impl<'a, 'cfg> Context<'a, 'cfg> { let bindst = output.bin_dst(); if unit.mode == CompileMode::Test { - self.compilation.tests.push(( - unit.pkg.clone(), - unit.target.clone(), - output.path.clone(), - )); + self.compilation + .tests + .push((unit.clone(), output.path.clone())); } else if unit.target.is_executable() { - self.compilation.binaries.push(bindst.clone()); + self.compilation + .binaries + .push((unit.clone(), bindst.clone())); } } @@ -199,8 +199,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { let mut unstable_opts = false; let args = compiler::extern_args(&self, unit, &mut unstable_opts)?; self.compilation.to_doc_test.push(compilation::Doctest { - package: unit.pkg.clone(), - target: unit.target.clone(), + unit: unit.clone(), args, unstable_opts, }); @@ -273,9 +272,11 @@ impl<'a, 'cfg> Context<'a, 'cfg> { let dest = self.bcx.profiles.get_dir_name(); let host_layout = Layout::new(self.bcx.ws, None, &dest)?; let mut targets = HashMap::new(); - if let CompileKind::Target(target) = self.bcx.build_config.requested_kind { - let layout = Layout::new(self.bcx.ws, Some(target), &dest)?; - targets.insert(target, layout); + for kind in self.bcx.build_config.requested_kinds.iter() { + if let CompileKind::Target(target) = *kind { + let layout = Layout::new(self.bcx.ws, Some(target), &dest)?; + targets.insert(target, layout); + } } self.primary_packages .extend(self.bcx.roots.iter().map(|u| u.pkg.package_id())); @@ -302,12 +303,22 @@ impl<'a, 'cfg> Context<'a, 'cfg> { .chain_err(|| "couldn't prepare build directories")?; } - self.compilation.host_deps_output = self.files_mut().host.deps().to_path_buf(); - let files = self.files.as_ref().unwrap(); - let layout = files.layout(self.bcx.build_config.requested_kind); - self.compilation.root_output = layout.dest().to_path_buf(); - self.compilation.deps_output = layout.deps().to_path_buf(); + for &kind in self + .bcx + .build_config + .requested_kinds + .iter() + .chain(Some(&CompileKind::Host)) + { + let layout = files.layout(kind); + self.compilation + .root_output + .insert(kind, layout.dest().to_path_buf()); + self.compilation + .deps_output + .insert(kind, layout.deps().to_path_buf()); + } Ok(()) } diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 3fa1e7e6bf9..930d32282e8 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -538,7 +538,7 @@ fn prepare_rustc( let mut base = cx .compilation - .rustc_process(&unit.pkg, is_primary, is_workspace)?; + .rustc_process(unit, is_primary, is_workspace)?; if cx.bcx.config.cli_unstable().jobserver_per_rustc { let client = cx.new_jobserver()?; base.inherit_jobserver(&client); @@ -554,7 +554,7 @@ fn prepare_rustc( fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult { let bcx = cx.bcx; - let mut rustdoc = cx.compilation.rustdoc_process(&unit.pkg, &unit.target)?; + let mut rustdoc = cx.compilation.rustdoc_process(unit)?; rustdoc.inherit_jobserver(&cx.jobserver); rustdoc.arg("--crate-name").arg(&unit.target.crate_name()); add_path_args(bcx, unit, &mut rustdoc); diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 821d42f01d6..0ae0b651546 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -34,7 +34,7 @@ pub fn parse_unstable_flag(value: Option<&str>) -> Vec { pub fn resolve_std<'cfg>( ws: &Workspace<'cfg>, target_data: &RustcTargetData, - requested_target: CompileKind, + requested_targets: &[CompileKind], crates: &[String], ) -> CargoResult<(PackageSet<'cfg>, Resolve, ResolvedFeatures)> { let src_path = detect_sysroot_src_path(target_data)?; @@ -107,7 +107,7 @@ pub fn resolve_std<'cfg>( let resolve = ops::resolve_ws_with_opts( &std_ws, target_data, - requested_target, + requested_targets, &opts, &specs, HasDevUnits::No, @@ -126,11 +126,11 @@ pub fn generate_std_roots( crates: &[String], std_resolve: &Resolve, std_features: &ResolvedFeatures, - kind: CompileKind, + kinds: &[CompileKind], package_set: &PackageSet<'_>, interner: &UnitInterner, profiles: &Profiles, -) -> CargoResult> { +) -> CargoResult>> { // Generate the root Units for the standard library. let std_ids = crates .iter() @@ -138,34 +138,42 @@ pub fn generate_std_roots( .collect::>>()?; // Convert PackageId to Package. let std_pkgs = package_set.get_many(std_ids)?; - // Generate a list of Units. - std_pkgs - .into_iter() - .map(|pkg| { - let lib = pkg - .targets() - .iter() - .find(|t| t.is_lib()) - .expect("std has a lib"); - let unit_for = UnitFor::new_normal(); - // I don't think we need to bother with Check here, the difference - // in time is minimal, and the difference in caching is - // significant. - let mode = CompileMode::Build; - let profile = profiles.get_profile( - pkg.package_id(), - /*is_member*/ false, - /*is_local*/ false, - unit_for, + // Generate a map of Units for each kind requested. + let mut ret = HashMap::new(); + for pkg in std_pkgs { + let lib = pkg + .targets() + .iter() + .find(|t| t.is_lib()) + .expect("std has a lib"); + let unit_for = UnitFor::new_normal(); + // I don't think we need to bother with Check here, the difference + // in time is minimal, and the difference in caching is + // significant. + let mode = CompileMode::Build; + let profile = profiles.get_profile( + pkg.package_id(), + /*is_member*/ false, + /*is_local*/ false, + unit_for, + mode, + ); + let features = std_features.activated_features(pkg.package_id(), FeaturesFor::NormalOrDev); + + for kind in kinds { + let list = ret.entry(*kind).or_insert(Vec::new()); + list.push(interner.intern( + pkg, + lib, + profile, + *kind, mode, - ); - let features = - std_features.activated_features(pkg.package_id(), FeaturesFor::NormalOrDev); - Ok(interner.intern( - pkg, lib, profile, kind, mode, features, /*is_std*/ true, - )) - }) - .collect::>>() + features.clone(), + /*is_std*/ true, + )); + } + } + return Ok(ret); } fn detect_sysroot_src_path(target_data: &RustcTargetData) -> CargoResult { diff --git a/src/cargo/core/compiler/timings.rs b/src/cargo/core/compiler/timings.rs index ce0b0dad5e5..4704eb12813 100644 --- a/src/cargo/core/compiler/timings.rs +++ b/src/cargo/core/compiler/timings.rs @@ -614,7 +614,13 @@ fn render_rustc_info(bcx: &BuildContext<'_, '_>) -> String { .lines() .next() .expect("rustc version"); - let requested_target = bcx.target_data.short_name(&bcx.build_config.requested_kind); + let requested_target = bcx + .build_config + .requested_kinds + .iter() + .map(|kind| bcx.target_data.short_name(kind)) + .collect::>() + .join(", "); format!( "{}
Host: {}
Target: {}", version, diff --git a/src/cargo/core/compiler/unit_dependencies.rs b/src/cargo/core/compiler/unit_dependencies.rs index d5cbed9a48d..57cb6c68d06 100644 --- a/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/cargo/core/compiler/unit_dependencies.rs @@ -55,7 +55,7 @@ pub fn build_unit_dependencies<'a, 'cfg>( features: &'a ResolvedFeatures, std_resolve: Option<&'a (Resolve, ResolvedFeatures)>, roots: &[Unit], - std_roots: &[Unit], + std_roots: &HashMap>, global_mode: CompileMode, target_data: &'a RustcTargetData, profiles: &'a Profiles, @@ -108,14 +108,16 @@ pub fn build_unit_dependencies<'a, 'cfg>( /// Compute all the dependencies for the standard library. fn calc_deps_of_std( mut state: &mut State<'_, '_>, - std_roots: &[Unit], + std_roots: &HashMap>, ) -> CargoResult> { if std_roots.is_empty() { return Ok(None); } // Compute dependencies for the standard library. state.is_std = true; - deps_of_roots(std_roots, &mut state)?; + for roots in std_roots.values() { + deps_of_roots(roots, &mut state)?; + } state.is_std = false; Ok(Some(std::mem::replace( &mut state.unit_dependencies, @@ -124,11 +126,15 @@ fn calc_deps_of_std( } /// Add the standard library units to the `unit_dependencies`. -fn attach_std_deps(state: &mut State<'_, '_>, std_roots: &[Unit], std_unit_deps: UnitGraph) { +fn attach_std_deps( + state: &mut State<'_, '_>, + std_roots: &HashMap>, + std_unit_deps: UnitGraph, +) { // Attach the standard library as a dependency of every target unit. for (unit, deps) in state.unit_dependencies.iter_mut() { if !unit.kind.is_host() && !unit.mode.is_run_custom_build() { - deps.extend(std_roots.iter().map(|unit| UnitDep { + deps.extend(std_roots[&unit.kind].iter().map(|unit| UnitDep { unit: unit.clone(), unit_for: UnitFor::new_normal(), extern_crate_name: unit.pkg.name(), diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 5ff87161bed..1c82a033d49 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -352,6 +352,7 @@ pub struct CliUnstable { pub features: Option>, pub crate_versions: bool, pub separate_nightlies: bool, + pub multitarget: bool, } impl CliUnstable { @@ -430,6 +431,7 @@ impl CliUnstable { "features" => self.features = Some(parse_features(v)), "crate-versions" => self.crate_versions = parse_empty(k, v)?, "separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?, + "multitarget" => self.multitarget = parse_empty(k, v)?, _ => bail!("unknown `-Z` flag specified: {}", k), } diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index a1ab517d04c..14e2a8c28ab 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -472,7 +472,7 @@ impl<'cfg> PackageSet<'cfg> { resolve: &Resolve, root_ids: &[PackageId], has_dev_units: HasDevUnits, - requested_kind: CompileKind, + requested_kinds: &[CompileKind], target_data: &RustcTargetData, ) -> CargoResult<()> { fn collect_used_deps( @@ -480,7 +480,7 @@ impl<'cfg> PackageSet<'cfg> { resolve: &Resolve, pkg_id: PackageId, has_dev_units: HasDevUnits, - requested_kind: CompileKind, + requested_kinds: &[CompileKind], target_data: &RustcTargetData, ) -> CargoResult<()> { if !used.insert(pkg_id) { @@ -495,9 +495,11 @@ impl<'cfg> PackageSet<'cfg> { // dependencies are used both for target and host. To tighten this // up, this function would need to track "for_host" similar to how // unit dependencies handles it. - if !target_data.dep_platform_activated(dep, requested_kind) - && !target_data.dep_platform_activated(dep, CompileKind::Host) - { + let activated = requested_kinds + .iter() + .chain(Some(&CompileKind::Host)) + .any(|kind| target_data.dep_platform_activated(dep, *kind)); + if !activated { return false; } true @@ -509,7 +511,7 @@ impl<'cfg> PackageSet<'cfg> { resolve, dep_id, has_dev_units, - requested_kind, + requested_kinds, target_data, )?; } @@ -527,7 +529,7 @@ impl<'cfg> PackageSet<'cfg> { resolve, *id, has_dev_units, - requested_kind, + requested_kinds, target_data, )?; } diff --git a/src/cargo/core/resolver/features.rs b/src/cargo/core/resolver/features.rs index 58a4005e71a..acbdec424b3 100644 --- a/src/cargo/core/resolver/features.rs +++ b/src/cargo/core/resolver/features.rs @@ -246,8 +246,8 @@ impl ResolvedFeatures { pub struct FeatureResolver<'a, 'cfg> { ws: &'a Workspace<'cfg>, target_data: &'a RustcTargetData, - /// The platform to build for, requested by the user. - requested_target: CompileKind, + /// The platforms to build for, requested by the user. + requested_targets: &'a [CompileKind], resolve: &'a Resolve, package_set: &'a PackageSet<'cfg>, /// Options that change how the feature resolver operates. @@ -269,7 +269,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { package_set: &'a PackageSet<'cfg>, requested_features: &RequestedFeatures, specs: &[PackageIdSpec], - requested_target: CompileKind, + requested_targets: &[CompileKind], has_dev_units: HasDevUnits, ) -> CargoResult { use crate::util::profile; @@ -287,7 +287,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { let mut r = FeatureResolver { ws, target_data, - requested_target, + requested_targets, resolve, package_set, opts, @@ -536,8 +536,9 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> { .dep_platform_activated(dep, CompileKind::Host); } // Not a build dependency, and not for a build script, so must be Target. - self.target_data - .dep_platform_activated(dep, self.requested_target) + self.requested_targets + .iter() + .any(|kind| self.target_data.dep_platform_activated(dep, *kind)) }; self.resolve .deps(pkg_id) diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index 132643271c6..beb9b03cd39 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -23,7 +23,7 @@ pub struct CleanOptions<'a> { /// A list of packages to clean. If empty, everything is cleaned. pub spec: Vec, /// The target arch triple to clean, or None for the host arch - pub target: Option, + pub targets: Vec, /// Whether to clean the release directory pub profile_specified: bool, /// Whether to clean the directory of a certain build profile @@ -61,9 +61,9 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> { if opts.spec.is_empty() { return rm_rf(&target_dir.into_path_unlocked(), config); } - let mut build_config = BuildConfig::new(config, Some(1), &opts.target, CompileMode::Build)?; + let mut build_config = BuildConfig::new(config, Some(1), &opts.targets, CompileMode::Build)?; build_config.requested_profile = opts.requested_profile; - let target_data = RustcTargetData::new(ws, build_config.requested_kind)?; + let target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?; // Resolve for default features. In the future, `cargo clean` should be rewritten // so that it doesn't need to guess filename hashes. let resolve_opts = ResolveOpts::new( @@ -80,7 +80,7 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> { let ws_resolve = ops::resolve_ws_with_opts( ws, &target_data, - build_config.requested_kind, + &build_config.requested_kinds, &resolve_opts, &specs, HasDevUnits::Yes, @@ -102,7 +102,11 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> { // Generate all relevant `Unit` targets for this package for target in pkg.targets() { - for kind in [CompileKind::Host, build_config.requested_kind].iter() { + for kind in build_config + .requested_kinds + .iter() + .chain(Some(&CompileKind::Host)) + { for mode in CompileMode::all_modes() { for unit_for in UnitFor::all_values() { let profile = if mode.is_run_custom_build() { @@ -143,7 +147,7 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> { &features, None, &units, - &[], + &Default::default(), build_config.mode, &target_data, &profiles, diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 8ad07991526..29e3abf018e 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -79,7 +79,7 @@ pub struct CompileOptions { impl<'a> CompileOptions { pub fn new(config: &Config, mode: CompileMode) -> CargoResult { Ok(CompileOptions { - build_config: BuildConfig::new(config, None, &None, mode)?, + build_config: BuildConfig::new(config, None, &[], mode)?, features: Vec::new(), all_features: false, no_default_features: false, @@ -310,7 +310,7 @@ pub fn create_bcx<'a, 'cfg>( } } - let target_data = RustcTargetData::new(ws, build_config.requested_kind)?; + let target_data = RustcTargetData::new(ws, &build_config.requested_kinds)?; let specs = spec.to_package_id_specs(ws)?; let dev_deps = ws.require_optional_deps() || filter.need_dev_deps(build_config.mode); @@ -323,7 +323,7 @@ pub fn create_bcx<'a, 'cfg>( let resolve = ops::resolve_ws_with_opts( ws, &target_data, - build_config.requested_kind, + &build_config.requested_kinds, &opts, &specs, has_dev_units, @@ -341,14 +341,14 @@ pub fn create_bcx<'a, 'cfg>( .shell() .warn("-Zbuild-std does not currently fully support --build-plan")?; } - if build_config.requested_kind.is_host() { + if build_config.requested_kinds[0].is_host() { // TODO: This should eventually be fixed. Unfortunately it is not // easy to get the host triple in BuildConfig. Consider changing // requested_target to an enum, or some other approach. anyhow::bail!("-Zbuild-std requires --target"); } let (std_package_set, std_resolve, std_features) = - standard_lib::resolve_std(ws, &target_data, build_config.requested_kind, crates)?; + standard_lib::resolve_std(ws, &target_data, &build_config.requested_kinds, crates)?; pkg_set.add_set(std_package_set); Some((std_resolve, std_features)) } else { @@ -413,7 +413,7 @@ pub fn create_bcx<'a, 'cfg>( ws, &to_builds, filter, - build_config.requested_kind, + &build_config.requested_kinds, build_config.mode, &resolve, &resolved_features, @@ -440,13 +440,13 @@ pub fn create_bcx<'a, 'cfg>( &crates, std_resolve, std_features, - build_config.requested_kind, + &build_config.requested_kinds, &pkg_set, interner, &profiles, )? } else { - Vec::new() + Default::default() }; let mut extra_compiler_args = HashMap::new(); @@ -694,7 +694,7 @@ fn generate_targets( ws: &Workspace<'_>, packages: &[&Package], filter: &CompileFilter, - default_arch_kind: CompileKind, + requested_kinds: &[CompileKind], mode: CompileMode, resolve: &Resolve, resolved_features: &features::ResolvedFeatures, @@ -703,86 +703,90 @@ fn generate_targets( interner: &UnitInterner, ) -> CargoResult> { let config = ws.config(); - // Helper for creating a `Unit` struct. - let new_unit = |pkg: &Package, target: &Target, target_mode: CompileMode| { - let unit_for = if target_mode.is_any_test() { - // NOTE: the `UnitFor` here is subtle. If you have a profile - // with `panic` set, the `panic` flag is cleared for - // tests/benchmarks and their dependencies. If this - // was `normal`, then the lib would get compiled three - // times (once with panic, once without, and once with - // `--test`). - // - // This would cause a problem for doc tests, which would fail - // because `rustdoc` would attempt to link with both libraries - // at the same time. Also, it's probably not important (or - // even desirable?) for rustdoc to link with a lib with - // `panic` set. - // - // As a consequence, Examples and Binaries get compiled - // without `panic` set. This probably isn't a bad deal. - // - // Forcing the lib to be compiled three times during `cargo - // test` is probably also not desirable. - UnitFor::new_test(config) - } else if target.for_host() { - // Proc macro / plugin should not have `panic` set. - UnitFor::new_compiler() - } else { - UnitFor::new_normal() - }; - // Custom build units are added in `build_unit_dependencies`. - assert!(!target.is_custom_build()); - let target_mode = match target_mode { - CompileMode::Test => { - if target.is_example() && !filter.is_specific() && !target.tested() { - // Examples are included as regular binaries to verify - // that they compile. - CompileMode::Build - } else { - CompileMode::Test + // Helper for creating a list of `Unit` structures + let new_unit = + |units: &mut HashSet, pkg: &Package, target: &Target, target_mode: CompileMode| { + let unit_for = if target_mode.is_any_test() { + // NOTE: the `UnitFor` here is subtle. If you have a profile + // with `panic` set, the `panic` flag is cleared for + // tests/benchmarks and their dependencies. If this + // was `normal`, then the lib would get compiled three + // times (once with panic, once without, and once with + // `--test`). + // + // This would cause a problem for doc tests, which would fail + // because `rustdoc` would attempt to link with both libraries + // at the same time. Also, it's probably not important (or + // even desirable?) for rustdoc to link with a lib with + // `panic` set. + // + // As a consequence, Examples and Binaries get compiled + // without `panic` set. This probably isn't a bad deal. + // + // Forcing the lib to be compiled three times during `cargo + // test` is probably also not desirable. + UnitFor::new_test(config) + } else if target.for_host() { + // Proc macro / plugin should not have `panic` set. + UnitFor::new_compiler() + } else { + UnitFor::new_normal() + }; + // Custom build units are added in `build_unit_dependencies`. + assert!(!target.is_custom_build()); + let target_mode = match target_mode { + CompileMode::Test => { + if target.is_example() && !filter.is_specific() && !target.tested() { + // Examples are included as regular binaries to verify + // that they compile. + CompileMode::Build + } else { + CompileMode::Test + } } + CompileMode::Build => match *target.kind() { + TargetKind::Test => CompileMode::Test, + TargetKind::Bench => CompileMode::Bench, + _ => CompileMode::Build, + }, + // `CompileMode::Bench` is only used to inform `filter_default_targets` + // which command is being used (`cargo bench`). Afterwards, tests + // and benches are treated identically. Switching the mode allows + // de-duplication of units that are essentially identical. For + // example, `cargo build --all-targets --release` creates the units + // (lib profile:bench, mode:test) and (lib profile:bench, mode:bench) + // and since these are the same, we want them to be de-duplicated in + // `unit_dependencies`. + CompileMode::Bench => CompileMode::Test, + _ => target_mode, + }; + + let is_local = pkg.package_id().source_id().is_path(); + let profile = profiles.get_profile( + pkg.package_id(), + ws.is_member(pkg), + is_local, + unit_for, + target_mode, + ); + + // No need to worry about build-dependencies, roots are never build dependencies. + let features_for = FeaturesFor::from_for_host(target.proc_macro()); + let features = resolved_features.activated_features(pkg.package_id(), features_for); + + for kind in requested_kinds { + let unit = interner.intern( + pkg, + target, + profile, + kind.for_target(target), + target_mode, + features.clone(), + /*is_std*/ false, + ); + units.insert(unit); } - CompileMode::Build => match *target.kind() { - TargetKind::Test => CompileMode::Test, - TargetKind::Bench => CompileMode::Bench, - _ => CompileMode::Build, - }, - // `CompileMode::Bench` is only used to inform `filter_default_targets` - // which command is being used (`cargo bench`). Afterwards, tests - // and benches are treated identically. Switching the mode allows - // de-duplication of units that are essentially identical. For - // example, `cargo build --all-targets --release` creates the units - // (lib profile:bench, mode:test) and (lib profile:bench, mode:bench) - // and since these are the same, we want them to be de-duplicated in - // `unit_dependencies`. - CompileMode::Bench => CompileMode::Test, - _ => target_mode, }; - let kind = default_arch_kind.for_target(target); - let is_local = pkg.package_id().source_id().is_path(); - let profile = profiles.get_profile( - pkg.package_id(), - ws.is_member(pkg), - is_local, - unit_for, - target_mode, - ); - - // No need to worry about build-dependencies, roots are never build dependencies. - let features_for = FeaturesFor::from_for_host(target.proc_macro()); - let features = - Vec::from(resolved_features.activated_features(pkg.package_id(), features_for)); - interner.intern( - pkg, - target, - profile, - kind, - target_mode, - features, - /*is_std*/ false, - ) - }; // Create a list of proposed targets. let mut proposals: Vec> = Vec::new(); @@ -927,8 +931,7 @@ fn generate_targets( None => Vec::new(), }; if target.is_lib() || unavailable_features.is_empty() { - let unit = new_unit(pkg, target, mode); - units.insert(unit); + new_unit(&mut units, pkg, target, mode); } else if requires_features { let required_features = target.required_features().unwrap(); let quoted_required_features: Vec = required_features diff --git a/src/cargo/ops/cargo_doc.rs b/src/cargo/ops/cargo_doc.rs index f67035fc7cd..dea302f13be 100644 --- a/src/cargo/ops/cargo_doc.rs +++ b/src/cargo/ops/cargo_doc.rs @@ -25,12 +25,11 @@ pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> { options.compile_opts.all_features, !options.compile_opts.no_default_features, ); - let requested_kind = options.compile_opts.build_config.requested_kind; - let target_data = RustcTargetData::new(ws, requested_kind)?; + let target_data = RustcTargetData::new(ws, &options.compile_opts.build_config.requested_kinds)?; let ws_resolve = ops::resolve_ws_with_opts( ws, &target_data, - requested_kind, + &options.compile_opts.build_config.requested_kinds, &opts, &specs, HasDevUnits::No, @@ -69,15 +68,20 @@ pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> { } } + let open_kind = if options.open_result { + Some(options.compile_opts.build_config.single_requested_kind()?) + } else { + None + }; + let compilation = ops::compile(ws, &options.compile_opts)?; - if options.open_result { + if let Some(kind) = open_kind { let name = match names.first() { Some(s) => s.to_string(), None => return Ok(()), }; - let path = compilation - .root_output + let path = compilation.root_output[&kind] .with_file_name("doc") .join(&name) .join("index.html"); diff --git a/src/cargo/ops/cargo_fetch.rs b/src/cargo/ops/cargo_fetch.rs index 43835374ab8..a21c9443eb0 100644 --- a/src/cargo/ops/cargo_fetch.rs +++ b/src/cargo/ops/cargo_fetch.rs @@ -1,4 +1,4 @@ -use crate::core::compiler::{BuildConfig, CompileMode, TargetInfo}; +use crate::core::compiler::{BuildConfig, CompileMode, RustcTargetData}; use crate::core::{PackageSet, Resolve, Workspace}; use crate::ops; use crate::util::CargoResult; @@ -8,7 +8,7 @@ use std::collections::HashSet; pub struct FetchOptions<'a> { pub config: &'a Config, /// The target arch triple to fetch dependencies for - pub target: Option, + pub targets: Vec, } /// Executes `cargo fetch`. @@ -21,14 +21,8 @@ pub fn fetch<'a>( let jobs = Some(1); let config = ws.config(); - let build_config = BuildConfig::new(config, jobs, &options.target, CompileMode::Build)?; - let rustc = config.load_global_rustc(Some(ws))?; - let target_info = TargetInfo::new( - config, - build_config.requested_kind, - &rustc, - build_config.requested_kind, - )?; + let build_config = BuildConfig::new(config, jobs, &options.targets, CompileMode::Build)?; + let data = RustcTargetData::new(ws, &build_config.requested_kinds)?; let mut fetched_packages = HashSet::new(); let mut deps_to_fetch = ws.members().map(|p| p.package_id()).collect::>(); let mut to_download = Vec::new(); @@ -43,20 +37,21 @@ pub fn fetch<'a>( .deps(id) .filter(|&(_id, deps)| { deps.iter().any(|d| { - // If no target was specified then all dependencies can - // be fetched. - let target = match options.target { - Some(ref t) => t, - None => return true, - }; - // If this dependency is only available for certain - // platforms, make sure we're only fetching it for that - // platform. - let platform = match d.platform() { - Some(p) => p, - None => return true, - }; - platform.matches(target, target_info.cfg()) + // If no target was specified then all dependencies are + // fetched. + if options.targets.len() == 0 { + return true; + } + + // Otherwise we only download this dependency if any of the + // requested platforms would match this dependency. Note + // that this is a bit lossy because not all dependencies are + // always compiled for all platforms, but it should be + // "close enough" for now. + build_config + .requested_kinds + .iter() + .any(|kind| data.dep_platform_activated(d, *kind)) }) }) .map(|(id, _deps)| id); diff --git a/src/cargo/ops/cargo_install.rs b/src/cargo/ops/cargo_install.rs index 052ac7deb90..fd343f85465 100644 --- a/src/cargo/ops/cargo_install.rs +++ b/src/cargo/ops/cargo_install.rs @@ -270,7 +270,8 @@ fn install_one( // anything if we're gonna throw it away anyway. let dst = root.join("bin").into_path_unlocked(); let rustc = config.load_global_rustc(Some(&ws))?; - let target = match &opts.build_config.requested_kind { + let requested_kind = opts.build_config.single_requested_kind()?; + let target = match &requested_kind { CompileKind::Host => rustc.host.as_str(), CompileKind::Target(target) => target.short_name(), }; @@ -335,7 +336,7 @@ fn install_one( let mut binaries: Vec<(&str, &Path)> = compile .binaries .iter() - .map(|bin| { + .map(|(_, bin)| { let name = bin.file_name().unwrap(); if let Some(s) = name.to_str() { Ok((s, bin.as_ref())) diff --git a/src/cargo/ops/cargo_output_metadata.rs b/src/cargo/ops/cargo_output_metadata.rs index da7fe77acfb..d10a6a4f3c9 100644 --- a/src/cargo/ops/cargo_output_metadata.rs +++ b/src/cargo/ops/cargo_output_metadata.rs @@ -1,4 +1,4 @@ -use crate::core::compiler::{CompileKind, CompileTarget, RustcTargetData}; +use crate::core::compiler::{CompileKind, RustcTargetData}; use crate::core::dependency::DepKind; use crate::core::resolver::{HasDevUnits, Resolve, ResolveOpts}; use crate::core::{Dependency, InternedString, Package, PackageId, Workspace}; @@ -17,7 +17,7 @@ pub struct OutputMetadataOptions { pub all_features: bool, pub no_deps: bool, pub version: u32, - pub filter_platform: Option, + pub filter_platforms: Vec, } /// Loads the manifest, resolves the dependencies of the package to the concrete @@ -105,11 +105,9 @@ fn build_resolve_graph( ) -> CargoResult<(Vec, MetadataResolve)> { // TODO: Without --filter-platform, features are being resolved for `host` only. // How should this work? - let requested_kind = match &metadata_opts.filter_platform { - Some(t) => CompileKind::Target(CompileTarget::new(t)?), - None => CompileKind::Host, - }; - let target_data = RustcTargetData::new(ws, requested_kind)?; + let requested_kinds = + CompileKind::from_requested_targets(ws.config(), &metadata_opts.filter_platforms)?; + let target_data = RustcTargetData::new(ws, &requested_kinds)?; // Resolve entire workspace. let specs = Packages::All.to_package_id_specs(ws)?; let resolve_opts = ResolveOpts::new( @@ -121,7 +119,7 @@ fn build_resolve_graph( let ws_resolve = ops::resolve_ws_with_opts( ws, &target_data, - requested_kind, + &requested_kinds, &resolve_opts, &specs, HasDevUnits::Yes, @@ -147,7 +145,7 @@ fn build_resolve_graph( &ws_resolve.targeted_resolve, &package_map, &target_data, - requested_kind, + &requested_kinds, ); } // Get a Vec of Packages. @@ -168,7 +166,7 @@ fn build_resolve_graph_r( resolve: &Resolve, package_map: &HashMap, target_data: &RustcTargetData, - requested_kind: CompileKind, + requested_kinds: &[CompileKind], ) { if node_map.contains_key(&pkg_id) { return; @@ -177,12 +175,15 @@ fn build_resolve_graph_r( let deps: Vec = resolve .deps(pkg_id) - .filter(|(_dep_id, deps)| match requested_kind { - CompileKind::Target(_) => deps - .iter() - .any(|dep| target_data.dep_platform_activated(dep, requested_kind)), - // No --filter-platform is interpreted as "all platforms". - CompileKind::Host => true, + .filter(|(_dep_id, deps)| { + if requested_kinds == &[CompileKind::Host] { + true + } else { + requested_kinds.iter().any(|kind| { + deps.iter() + .any(|dep| target_data.dep_platform_activated(dep, *kind)) + }) + } }) .filter_map(|(dep_id, deps)| { let dep_kinds: Vec<_> = deps.iter().map(DepKindInfo::from).collect(); @@ -213,7 +214,7 @@ fn build_resolve_graph_r( resolve, package_map, target_data, - requested_kind, + requested_kinds, ); } } diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index b9aa42fc4cb..b84e6b3caf1 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -29,7 +29,7 @@ pub struct PackageOpts<'cfg> { pub allow_dirty: bool, pub verify: bool, pub jobs: Option, - pub target: Option, + pub targets: Vec, pub features: Vec, pub all_features: bool, pub no_default_features: bool, @@ -716,7 +716,7 @@ fn run_verify(ws: &Workspace<'_>, tar: &FileLock, opts: &PackageOpts<'_>) -> Car ops::compile_with_exec( &ws, &ops::CompileOptions { - build_config: BuildConfig::new(config, opts.jobs, &opts.target, CompileMode::Build)?, + build_config: BuildConfig::new(config, opts.jobs, &opts.targets, CompileMode::Build)?, features: opts.features.clone(), no_default_features: opts.no_default_features, all_features: opts.all_features, diff --git a/src/cargo/ops/cargo_run.rs b/src/cargo/ops/cargo_run.rs index 0c207482117..86c23e919ec 100644 --- a/src/cargo/ops/cargo_run.rs +++ b/src/cargo/ops/cargo_run.rs @@ -70,16 +70,19 @@ pub fn run( } } + // `cargo run` is only compatible with one `--target` flag at most + options.build_config.single_requested_kind()?; + let compile = ops::compile(ws, options)?; assert_eq!(compile.binaries.len(), 1); - let exe = &compile.binaries[0]; + let (unit, exe) = &compile.binaries[0]; let exe = match exe.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(), }; let pkg = bins[0].0; - let mut process = compile.target_process(exe, pkg)?; + let mut process = compile.target_process(exe, unit.kind, pkg)?; 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 ff71d0b1aa9..af7c5983ffc 100644 --- a/src/cargo/ops/cargo_test.rs +++ b/src/cargo/ops/cargo_test.rs @@ -64,9 +64,7 @@ pub fn run_benches( fn compile_tests<'a>(ws: &Workspace<'a>, options: &TestOptions) -> CargoResult> { let mut compilation = ops::compile(ws, &options.compile_opts)?; - compilation - .tests - .sort_by(|a, b| (a.0.package_id(), &a.1, &a.2).cmp(&(b.0.package_id(), &b.1, &b.2))); + compilation.tests.sort(); Ok(compilation) } @@ -78,16 +76,14 @@ fn run_unit_tests( compilation: &Compilation<'_>, ) -> CargoResult<(Test, Vec)> { let cwd = config.cwd(); - let mut errors = Vec::new(); - for &(ref pkg, ref target, ref exe) in &compilation.tests { - let kind = target.kind(); - let test = target.name().to_string(); + for (unit, exe) 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, pkg)?; + let mut cmd = compilation.target_process(exe, unit.kind, &unit.pkg)?; cmd.args(test_args); - if target.harness() && config.shell().verbosity() == Verbosity::Quiet { + if unit.target.harness() && config.shell().verbosity() == Verbosity::Quiet { cmd.arg("--quiet"); } config @@ -102,7 +98,12 @@ fn run_unit_tests( match result { Err(e) => { let e = e.downcast::()?; - errors.push((kind.clone(), test.clone(), pkg.name().to_string(), e)); + errors.push(( + unit.target.kind().clone(), + test.clone(), + unit.pkg.name().to_string(), + e, + )); if !options.no_fail_fast { break; } @@ -137,48 +138,44 @@ fn run_doc_tests( ) -> CargoResult<(Test, Vec)> { let mut errors = Vec::new(); - // The unstable doctest-xcompile feature enables both per-target-ignores and - // cross-compiling doctests. As a side effect, this feature also gates running - // doctests with runtools when target == host. - let doctest_xcompile = config.cli_unstable().doctest_xcompile; - let mut runtool: &Option<(std::path::PathBuf, Vec)> = &None; - if doctest_xcompile { - runtool = compilation.target_runner(); - } else if compilation.host != compilation.target { - return Ok((Test::Doc, errors)); - } - for doctest_info in &compilation.to_doc_test { let Doctest { - package, - target, args, unstable_opts, + unit, } = doctest_info; - config.shell().status("Doc-tests", target.name())?; - let mut p = compilation.rustdoc_process(package, target)?; + + // Skip any `--target` tests unless `doctest-xcompile` is specified. + if !config.cli_unstable().doctest_xcompile && !unit.kind.is_host() { + continue; + } + + config.shell().status("Doc-tests", unit.target.name())?; + let mut p = compilation.rustdoc_process(unit)?; p.arg("--test") - .arg(target.src_path().path().unwrap()) + .arg(unit.target.src_path().path().unwrap()) .arg("--crate-name") - .arg(&target.crate_name()); + .arg(&unit.target.crate_name()); - if doctest_xcompile { - if let CompileKind::Target(target) = options.compile_opts.build_config.requested_kind { + if config.cli_unstable().doctest_xcompile { + if let CompileKind::Target(target) = unit.kind { // use `rustc_target()` to properly handle JSON target paths p.arg("--target").arg(target.rustc_target()); } p.arg("-Zunstable-options"); p.arg("--enable-per-target-ignores"); - } - - if let Some((runtool, runtool_args)) = runtool { - p.arg("--runtool").arg(runtool); - for arg in runtool_args { - p.arg("--runtool-arg").arg(arg); + if let Some((runtool, runtool_args)) = compilation.target_runner(unit.kind) { + p.arg("--runtool").arg(runtool); + for arg in runtool_args { + p.arg("--runtool-arg").arg(arg); + } } } - for &rust_dep in &[&compilation.deps_output] { + for &rust_dep in &[ + &compilation.deps_output[&unit.kind], + &compilation.deps_output[&CompileKind::Host], + ] { let mut arg = OsString::from("dependency="); arg.push(rust_dep); p.arg("-L").arg(arg); @@ -188,17 +185,11 @@ fn run_doc_tests( p.arg("-L").arg(native_dep); } - for &host_rust_dep in &[&compilation.host_deps_output] { - let mut arg = OsString::from("dependency="); - arg.push(host_rust_dep); - p.arg("-L").arg(arg); - } - for arg in test_args { p.arg("--test-args").arg(arg); } - if let Some(cfgs) = compilation.cfgs.get(&package.package_id()) { + if let Some(cfgs) = compilation.cfgs.get(&unit.pkg.package_id()) { for cfg in cfgs.iter() { p.arg("--cfg").arg(cfg); } @@ -212,7 +203,7 @@ fn run_doc_tests( p.arg("-Zunstable-options"); } - if let Some(flags) = compilation.rustdocflags.get(&package.package_id()) { + if let Some(flags) = compilation.rustdocflags.get(&unit.pkg.package_id()) { p.args(flags); } config diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index 9de9c41477f..4d47bc45342 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -42,7 +42,7 @@ pub struct PublishOpts<'cfg> { pub verify: bool, pub allow_dirty: bool, pub jobs: Option, - pub target: Option, + pub targets: Vec, pub dry_run: bool, pub registry: Option, pub features: Vec, @@ -88,7 +88,7 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> { list: false, check_metadata: true, allow_dirty: opts.allow_dirty, - target: opts.target.clone(), + targets: opts.targets.clone(), jobs: opts.jobs, features: opts.features.clone(), all_features: opts.all_features, diff --git a/src/cargo/ops/resolve.rs b/src/cargo/ops/resolve.rs index 220946ea2c1..ec12f67862f 100644 --- a/src/cargo/ops/resolve.rs +++ b/src/cargo/ops/resolve.rs @@ -75,7 +75,7 @@ pub fn resolve_ws<'a>(ws: &Workspace<'a>) -> CargoResult<(PackageSet<'a>, Resolv pub fn resolve_ws_with_opts<'cfg>( ws: &Workspace<'cfg>, target_data: &RustcTargetData, - requested_target: CompileKind, + requested_targets: &[CompileKind], opts: &ResolveOpts, specs: &[PackageIdSpec], has_dev_units: HasDevUnits, @@ -135,7 +135,7 @@ pub fn resolve_ws_with_opts<'cfg>( &resolved_with_overrides, &member_ids, has_dev_units, - requested_target, + requested_targets, target_data, )?; @@ -146,7 +146,7 @@ pub fn resolve_ws_with_opts<'cfg>( &pkg_set, &opts.features, specs, - requested_target, + requested_targets, has_dev_units, )?; diff --git a/src/cargo/ops/tree/graph.rs b/src/cargo/ops/tree/graph.rs index a221da9ea7f..5698cf04beb 100644 --- a/src/cargo/ops/tree/graph.rs +++ b/src/cargo/ops/tree/graph.rs @@ -251,7 +251,7 @@ pub fn build<'a>( specs: &[PackageIdSpec], requested_features: &RequestedFeatures, target_data: &RustcTargetData, - requested_kind: CompileKind, + requested_kinds: &[CompileKind], package_map: HashMap, opts: &TreeOptions, ) -> CargoResult> { @@ -261,19 +261,21 @@ pub fn build<'a>( for (member, requested_features) in members_with_features { let member_id = member.package_id(); let features_for = FeaturesFor::from_for_host(member.proc_macro()); - let member_index = add_pkg( - &mut graph, - resolve, - resolved_features, - member_id, - features_for, - target_data, - requested_kind, - opts, - ); - if opts.graph_features { - let fmap = resolve.summary(member_id).features(); - add_cli_features(&mut graph, member_index, &requested_features, fmap); + for kind in requested_kinds { + let member_index = add_pkg( + &mut graph, + resolve, + resolved_features, + member_id, + features_for, + target_data, + *kind, + opts, + ); + if opts.graph_features { + let fmap = resolve.summary(member_id).features(); + add_cli_features(&mut graph, member_index, &requested_features, fmap); + } } } if opts.graph_features { diff --git a/src/cargo/ops/tree/mod.rs b/src/cargo/ops/tree/mod.rs index fe4f56d1f25..53ba973143c 100644 --- a/src/cargo/ops/tree/mod.rs +++ b/src/cargo/ops/tree/mod.rs @@ -49,16 +49,16 @@ pub struct TreeOptions { #[derive(PartialEq)] pub enum Target { Host, - Specific(String), + Specific(Vec), All, } impl Target { - pub fn from_cli(target: Option<&str>) -> Target { - match target { - None => Target::Host, - Some("all") => Target::All, - Some(target) => Target::Specific(target.to_string()), + pub fn from_cli(targets: Vec) -> Target { + match targets.len() { + 0 => Target::Host, + 1 if targets[0] == "all" => Target::All, + _ => Target::Specific(targets), } } } @@ -126,14 +126,14 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<() if opts.graph_features && opts.duplicates { bail!("the `-e features` flag does not support `--duplicates`"); } - let requested_target = match &opts.target { - Target::All | Target::Host => None, - Target::Specific(t) => Some(t.as_ref()), + let requested_targets = match &opts.target { + Target::All | Target::Host => Vec::new(), + Target::Specific(t) => t.clone(), }; // TODO: Target::All is broken with -Zfeatures=itarget. To handle that properly, // `FeatureResolver` will need to be taught what "all" means. - let requested_kind = CompileKind::from_requested_target(ws.config(), requested_target)?; - let target_data = RustcTargetData::new(ws, requested_kind)?; + let requested_kinds = CompileKind::from_requested_targets(ws.config(), &requested_targets)?; + let target_data = RustcTargetData::new(ws, &requested_kinds)?; let specs = opts.packages.to_package_id_specs(ws)?; let resolve_opts = ResolveOpts::new( /*dev_deps*/ true, @@ -152,7 +152,7 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<() let ws_resolve = ops::resolve_ws_with_opts( ws, &target_data, - requested_kind, + &requested_kinds, &resolve_opts, &specs, has_dev, @@ -172,7 +172,7 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<() &specs, &resolve_opts.features, &target_data, - requested_kind, + &requested_kinds, package_map, opts, )?; diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index 8b92c040892..60df3de3bfe 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -139,7 +139,7 @@ pub trait AppExt: Sized { } fn arg_target_triple(self, target: &'static str) -> Self { - self._arg(opt("target", target).value_name("TRIPLE")) + self._arg(multi_opt("target", target, "TRIPLE")) } fn arg_target_dir(self) -> Self { @@ -321,8 +321,8 @@ pub trait ArgMatchesExt { self.value_of_u32("jobs") } - fn target(&self) -> Option { - self._value_of("target").map(|s| s.to_string()) + fn targets(&self) -> Vec { + self._values_of("target") } fn get_profile_name( @@ -454,7 +454,7 @@ pub trait ArgMatchesExt { } } - let mut build_config = BuildConfig::new(config, self.jobs()?, &self.target(), mode)?; + let mut build_config = BuildConfig::new(config, self.jobs()?, &self.targets(), mode)?; build_config.message_format = message_format.unwrap_or(MessageFormat::Human); build_config.requested_profile = self.get_profile_name(config, "dev", profile_checking)?; build_config.build_plan = self._is_present("build-plan"); diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index eaf05b70c43..deb312a31d5 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -99,6 +99,25 @@ information from `.cargo/config.toml`. See the rustc issue for more information. cargo test --target foo -Zdoctest-xcompile ``` +### multitarget +* Tracking Issue: [#8176](https://github.com/rust-lang/cargo/issues/8176) + +This flag allows passing multiple `--target` flags to the `cargo` subcommand +selected. When multiple `--target` flags are passed the selected build targets +will be built for each of the selected architectures. + +For example to compile a library for both 32 and 64-bit: + +``` +cargo build --target x86_64-unknown-linux-gnu --target i686-unknown-linux-gnu +``` + +or running tests for both targets: + +``` +cargo test --target x86_64-unknown-linux-gnu --target i686-unknown-linux-gnu +``` + ### Custom named profiles * Tracking Issue: [rust-lang/cargo#6988](https://github.com/rust-lang/cargo/issues/6988) diff --git a/tests/testsuite/cross_compile.rs b/tests/testsuite/cross_compile.rs index a781729a7b0..14dbd7f0cfd 100644 --- a/tests/testsuite/cross_compile.rs +++ b/tests/testsuite/cross_compile.rs @@ -396,7 +396,6 @@ fn no_cross_doctests() { [COMPILING] foo v0.0.1 ([CWD]) [FINISHED] test [unoptimized + debuginfo] target(s) in [..] [RUNNING] target/{triple}/debug/deps/foo-[..][EXE] -[DOCTEST] foo ", triple = target )) diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index a6260145162..fe337440aed 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -67,6 +67,7 @@ mod message_format; mod metabuild; mod metadata; mod minimal_versions; +mod multitarget; mod net_config; mod new; mod offline; diff --git a/tests/testsuite/multitarget.rs b/tests/testsuite/multitarget.rs new file mode 100644 index 00000000000..1235642cb56 --- /dev/null +++ b/tests/testsuite/multitarget.rs @@ -0,0 +1,144 @@ +//! Tests for multiple `--target` flags to subcommands + +use cargo_test_support::{basic_manifest, cross_compile, project, rustc_host}; + +#[cargo_test] +fn double_target_rejected() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build --target a --target b") + .with_stderr("error: specifying multiple `--target` flags requires `-Zmultitarget`") + .with_status(101) + .run(); +} + +#[cargo_test] +fn simple_build() { + if cross_compile::disabled() { + return; + } + let t1 = cross_compile::alternate(); + let t2 = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -Z multitarget") + .arg("--target") + .arg(&t1) + .arg("--target") + .arg(&t2) + .masquerade_as_nightly_cargo() + .run(); + + assert!(p.target_bin(&t1, "foo").is_file()); + assert!(p.target_bin(&t2, "foo").is_file()); +} + +#[cargo_test] +fn simple_test() { + if !cross_compile::can_run_on_host() { + return; + } + let t1 = cross_compile::alternate(); + let t2 = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/lib.rs", "fn main() {}") + .build(); + + p.cargo("test -Z multitarget") + .arg("--target") + .arg(&t1) + .arg("--target") + .arg(&t2) + .masquerade_as_nightly_cargo() + .with_stderr_contains(&format!("[RUNNING] [..]{}[..]", t1)) + .with_stderr_contains(&format!("[RUNNING] [..]{}[..]", t2)) + .run(); +} + +#[cargo_test] +fn simple_run() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("run -Z multitarget --target a --target b") + .with_stderr("error: only one `--target` argument is supported") + .with_status(101) + .masquerade_as_nightly_cargo() + .run(); +} + +#[cargo_test] +fn simple_doc() { + if cross_compile::disabled() { + return; + } + let t1 = cross_compile::alternate(); + let t2 = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/lib.rs", "//! empty lib") + .build(); + + p.cargo("doc -Z multitarget") + .arg("--target") + .arg(&t1) + .arg("--target") + .arg(&t2) + .masquerade_as_nightly_cargo() + .run(); + + assert!(p.build_dir().join(&t1).join("doc/foo/index.html").is_file()); + assert!(p.build_dir().join(&t2).join("doc/foo/index.html").is_file()); +} + +#[cargo_test] +fn simple_check() { + if cross_compile::disabled() { + return; + } + let t1 = cross_compile::alternate(); + let t2 = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("check -Z multitarget") + .arg("--target") + .arg(&t1) + .arg("--target") + .arg(&t2) + .masquerade_as_nightly_cargo() + .run(); +} + +#[cargo_test] +fn same_value_twice() { + if cross_compile::disabled() { + return; + } + let t = rustc_host(); + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -Z multitarget") + .arg("--target") + .arg(&t) + .arg("--target") + .arg(&t) + .masquerade_as_nightly_cargo() + .run(); + + assert!(p.target_bin(&t, "foo").is_file()); +} diff --git a/tests/testsuite/plugins.rs b/tests/testsuite/plugins.rs index 0035e20315e..79dcc9cd4c4 100644 --- a/tests/testsuite/plugins.rs +++ b/tests/testsuite/plugins.rs @@ -435,5 +435,5 @@ fn shared_panic_abort_plugins() { .file("baz/src/lib.rs", "") .build(); - p.cargo("build").run(); + p.cargo("build -v").run(); } diff --git a/tests/testsuite/run.rs b/tests/testsuite/run.rs index 06e0a6ea596..c323decf6c6 100644 --- a/tests/testsuite/run.rs +++ b/tests/testsuite/run.rs @@ -849,6 +849,7 @@ fn run_with_library_paths() { fn main() {{ let search_path = std::env::var_os("{}").unwrap(); let paths = std::env::split_paths(&search_path).collect::>(); + println!("{{:#?}}", paths); assert!(paths.contains(&r#"{}"#.into())); assert!(paths.contains(&r#"{}"#.into())); }} diff --git a/tests/testsuite/standard_lib.rs b/tests/testsuite/standard_lib.rs index aa33ff3e307..953bd66fef5 100644 --- a/tests/testsuite/standard_lib.rs +++ b/tests/testsuite/standard_lib.rs @@ -518,7 +518,7 @@ fn doctest() { ) .build(); - p.cargo("test --doc -v") + p.cargo("test --doc -v -Zdoctest-xcompile") .build_std(&setup) .with_stdout_contains("test src/lib.rs - f [..] ... ok") .target_host()