diff --git a/src/bin/cargo/command_prelude.rs b/src/bin/cargo/command_prelude.rs index 2f6a0462430..3ef518e57ef 100644 --- a/src/bin/cargo/command_prelude.rs +++ b/src/bin/cargo/command_prelude.rs @@ -4,13 +4,14 @@ use std::fs; use clap::{self, SubCommand}; use cargo::CargoResult; use cargo::core::Workspace; -use cargo::ops::{CompileFilter, CompileMode, CompileOptions, MessageFormat, NewOptions, Packages, - VersionControl}; +use cargo::core::compiler::{BuildConfig, MessageFormat}; +use cargo::ops::{CompileFilter, CompileOptions, NewOptions, Packages, VersionControl}; use cargo::util::paths; use cargo::util::important_paths::find_root_manifest_for_wd; pub use clap::{AppSettings, Arg, ArgMatches}; pub use cargo::{CliError, CliResult, Config}; +pub use cargo::core::compiler::CompileMode; pub type App = clap::App<'static, 'static>; @@ -271,16 +272,17 @@ pub trait ArgMatchesExt { } }; + let mut build_config = BuildConfig::new(config, self.jobs()?, &self.target(), mode)?; + build_config.message_format = message_format; + build_config.release = self._is_present("release"); + let opts = CompileOptions { config, - jobs: self.jobs()?, - target: self.target(), + build_config, features: self._values_of("features"), all_features: self._is_present("all-features"), no_default_features: self._is_present("no-default-features"), spec, - mode, - release: self._is_present("release"), filter: CompileFilter::new( self._is_present("lib"), self._values_of("bin"), @@ -293,7 +295,6 @@ pub trait ArgMatchesExt { self._is_present("benches"), self._is_present("all-targets"), ), - message_format, target_rustdoc_args: None, target_rustc_args: None, export_dir: None, diff --git a/src/bin/cargo/commands/bench.rs b/src/bin/cargo/commands/bench.rs index 83e3ab5af53..ea12a45754a 100644 --- a/src/bin/cargo/commands/bench.rs +++ b/src/bin/cargo/commands/bench.rs @@ -1,6 +1,6 @@ use command_prelude::*; -use cargo::ops::{self, CompileMode, TestOptions}; +use cargo::ops::{self, TestOptions}; pub fn cli() -> App { subcommand("bench") @@ -73,7 +73,7 @@ Compilation can be customized with the `bench` profile in the manifest. pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let ws = args.workspace(config)?; let mut compile_opts = args.compile_options(config, CompileMode::Bench)?; - compile_opts.release = true; + compile_opts.build_config.release = true; let ops = TestOptions { no_run: args.is_present("no-run"), diff --git a/src/bin/cargo/commands/build.rs b/src/bin/cargo/commands/build.rs index 0ab86aa3a3a..5dbc3850a4e 100644 --- a/src/bin/cargo/commands/build.rs +++ b/src/bin/cargo/commands/build.rs @@ -1,6 +1,6 @@ use command_prelude::*; -use cargo::ops::{self, CompileMode}; +use cargo::ops; pub fn cli() -> App { subcommand("build") diff --git a/src/bin/cargo/commands/check.rs b/src/bin/cargo/commands/check.rs index e4295c5c32b..72cebfda8f4 100644 --- a/src/bin/cargo/commands/check.rs +++ b/src/bin/cargo/commands/check.rs @@ -1,6 +1,6 @@ use command_prelude::*; -use cargo::ops::{self, CompileMode}; +use cargo::ops; pub fn cli() -> App { subcommand("check") diff --git a/src/bin/cargo/commands/doc.rs b/src/bin/cargo/commands/doc.rs index 54ba9679794..7bb19fd5ed9 100644 --- a/src/bin/cargo/commands/doc.rs +++ b/src/bin/cargo/commands/doc.rs @@ -1,6 +1,6 @@ use command_prelude::*; -use cargo::ops::{self, CompileMode, DocOptions}; +use cargo::ops::{self, DocOptions}; pub fn cli() -> App { subcommand("doc") diff --git a/src/bin/cargo/commands/install.rs b/src/bin/cargo/commands/install.rs index 0acdd0269f9..f0c65515d2f 100644 --- a/src/bin/cargo/commands/install.rs +++ b/src/bin/cargo/commands/install.rs @@ -1,7 +1,7 @@ use command_prelude::*; use cargo::core::{GitReference, SourceId}; -use cargo::ops::{self, CompileMode}; +use cargo::ops; use cargo::util::ToUrl; pub fn cli() -> App { @@ -73,7 +73,7 @@ continuous integration systems.", pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let mut compile_opts = args.compile_options(config, CompileMode::Build)?; - compile_opts.release = !args.is_present("debug"); + compile_opts.build_config.release = !args.is_present("debug"); let krates = args.values_of("crate") .unwrap_or_default() diff --git a/src/bin/cargo/commands/run.rs b/src/bin/cargo/commands/run.rs index 763263d5a4b..683b47b885c 100644 --- a/src/bin/cargo/commands/run.rs +++ b/src/bin/cargo/commands/run.rs @@ -1,7 +1,7 @@ use command_prelude::*; use cargo::core::Verbosity; -use cargo::ops::{self, CompileFilter, CompileMode}; +use cargo::ops::{self, CompileFilter}; pub fn cli() -> App { subcommand("run") diff --git a/src/bin/cargo/commands/rustc.rs b/src/bin/cargo/commands/rustc.rs index 35fb59e8e12..a5ca4deff02 100644 --- a/src/bin/cargo/commands/rustc.rs +++ b/src/bin/cargo/commands/rustc.rs @@ -1,6 +1,6 @@ use command_prelude::*; -use cargo::ops::{self, CompileMode}; +use cargo::ops; pub fn cli() -> App { subcommand("rustc") diff --git a/src/bin/cargo/commands/rustdoc.rs b/src/bin/cargo/commands/rustdoc.rs index 301e65449b8..194eaaef82a 100644 --- a/src/bin/cargo/commands/rustdoc.rs +++ b/src/bin/cargo/commands/rustdoc.rs @@ -1,6 +1,6 @@ use command_prelude::*; -use cargo::ops::{self, CompileMode, DocOptions}; +use cargo::ops::{self, DocOptions}; pub fn cli() -> App { subcommand("rustdoc") diff --git a/src/bin/cargo/commands/test.rs b/src/bin/cargo/commands/test.rs index a25f62f8ec3..f0c352bca43 100644 --- a/src/bin/cargo/commands/test.rs +++ b/src/bin/cargo/commands/test.rs @@ -1,6 +1,6 @@ use command_prelude::*; -use cargo::ops::{self, CompileMode}; +use cargo::ops; pub fn cli() -> App { subcommand("test") @@ -94,7 +94,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let mut compile_opts = args.compile_options(config, CompileMode::Test)?; let doc = args.is_present("doc"); if doc { - compile_opts.mode = ops::CompileMode::Doctest; + compile_opts.build_config.mode = CompileMode::Doctest; compile_opts.filter = ops::CompileFilter::new( true, Vec::new(), diff --git a/src/cargo/core/compiler/build_config.rs b/src/cargo/core/compiler/build_config.rs new file mode 100644 index 00000000000..e4d1377b78f --- /dev/null +++ b/src/cargo/core/compiler/build_config.rs @@ -0,0 +1,190 @@ +use std::path::Path; +use util::{CargoResult, CargoResultExt, Config}; + +/// Configuration information for a rustc build. +#[derive(Debug)] +pub struct BuildConfig { + /// The target arch triple, defaults to host arch + pub requested_target: Option, + /// How many rustc jobs to run in parallel + pub jobs: u32, + /// Whether we are building for release + pub release: bool, + /// In what mode we are compiling + pub mode: CompileMode, + /// Whether to print std output in json format (for machine reading) + pub message_format: MessageFormat, +} + +impl BuildConfig { + /// Parse all config files to learn about build configuration. Currently + /// configured options are: + /// + /// * build.jobs + /// * build.target + /// * target.$target.ar + /// * target.$target.linker + /// * target.$target.libfoo.metadata + pub fn new( + config: &Config, + jobs: Option, + requested_target: &Option, + mode: CompileMode, + ) -> CargoResult { + let requested_target = match requested_target { + &Some(ref target) if target.ends_with(".json") => { + let path = Path::new(target) + .canonicalize() + .chain_err(|| format_err!("Target path {:?} is not a valid file", target))?; + Some(path.into_os_string() + .into_string() + .map_err(|_| format_err!("Target path is not valid unicode"))?) + } + other => other.clone(), + }; + if let Some(ref s) = requested_target { + if s.trim().is_empty() { + bail!("target was empty") + } + } + let cfg_target = config.get_string("build.target")?.map(|s| s.val); + let target = requested_target.clone().or(cfg_target); + + if jobs == Some(0) { + bail!("jobs must be at least 1") + } + if jobs.is_some() && config.jobserver_from_env().is_some() { + config.shell().warn( + "a `-j` argument was passed to Cargo but Cargo is \ + also configured with an external jobserver in \ + its environment, ignoring the `-j` parameter", + )?; + } + let cfg_jobs = match config.get_i64("build.jobs")? { + Some(v) => { + if v.val <= 0 { + bail!( + "build.jobs must be positive, but found {} in {}", + v.val, + v.definition + ) + } else if v.val >= i64::from(u32::max_value()) { + bail!( + "build.jobs is too large: found {} in {}", + v.val, + v.definition + ) + } else { + Some(v.val as u32) + } + } + None => None, + }; + let jobs = jobs.or(cfg_jobs).unwrap_or(::num_cpus::get() as u32); + Ok(BuildConfig { + requested_target: target, + jobs, + release: false, + mode, + message_format: MessageFormat::Human, + }) + } + + pub fn json_messages(&self) -> bool { + self.message_format == MessageFormat::Json + } + + pub fn test(&self) -> bool { + self.mode == CompileMode::Test || self.mode == CompileMode::Bench + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MessageFormat { + Human, + Json, +} + +/// The general "mode" of what to do. +/// This is used for two purposes. The commands themselves pass this in to +/// `compile_ws` to tell it the general execution strategy. This influences +/// the default targets selected. The other use is in the `Unit` struct +/// to indicate what is being done with a specific target. +#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash)] +pub enum CompileMode { + /// A target being built for a test. + Test, + /// Building a target with `rustc` (lib or bin). + Build, + /// Building a target with `rustc` to emit `rmeta` metadata only. If + /// `test` is true, then it is also compiled with `--test` to check it like + /// a test. + Check { test: bool }, + /// Used to indicate benchmarks should be built. This is not used in + /// `Target` because it is essentially the same as `Test` (indicating + /// `--test` should be passed to rustc) and by using `Test` instead it + /// allows some de-duping of Units to occur. + Bench, + /// A target that will be documented with `rustdoc`. + /// If `deps` is true, then it will also document all dependencies. + Doc { deps: bool }, + /// A target that will be tested with `rustdoc`. + Doctest, + /// A marker for Units that represent the execution of a `build.rs` + /// script. + RunCustomBuild, +} + +impl CompileMode { + /// Returns true if the unit is being checked. + pub fn is_check(&self) -> bool { + match *self { + CompileMode::Check { .. } => true, + _ => false, + } + } + + /// Returns true if this is a doc or doctest. Be careful using this. + /// Although both run rustdoc, the dependencies for those two modes are + /// very different. + pub fn is_doc(&self) -> bool { + match *self { + CompileMode::Doc { .. } | CompileMode::Doctest => true, + _ => false, + } + } + + /// Returns true if this is any type of test (test, benchmark, doctest, or + /// check-test). + pub fn is_any_test(&self) -> bool { + match *self { + CompileMode::Test + | CompileMode::Bench + | CompileMode::Check { test: true } + | CompileMode::Doctest => true, + _ => false, + } + } + + /// Returns true if this is the *execution* of a `build.rs` script. + pub fn is_run_custom_build(&self) -> bool { + *self == CompileMode::RunCustomBuild + } + + /// List of all modes (currently used by `cargo clean -p` for computing + /// all possible outputs). + pub fn all_modes() -> &'static [CompileMode] { + static ALL: [CompileMode; 9] = [ + CompileMode::Test, + CompileMode::Build, + CompileMode::Check { test: true }, + CompileMode::Check { test: false }, + CompileMode::Bench, + CompileMode::Doc { deps: true }, + CompileMode::Doc { deps: false }, + CompileMode::Doctest, + CompileMode::RunCustomBuild, + ]; + &ALL + } +} diff --git a/src/cargo/core/compiler/build_context/mod.rs b/src/cargo/core/compiler/build_context/mod.rs index 30708ebc806..1e7112b9abe 100644 --- a/src/cargo/core/compiler/build_context/mod.rs +++ b/src/cargo/core/compiler/build_context/mod.rs @@ -1,14 +1,15 @@ +use std::collections::HashMap; use std::env; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::str::{self, FromStr}; use core::profiles::Profiles; use core::{Dependency, Workspace}; use core::{Package, PackageId, PackageSet, Resolve}; use util::errors::CargoResult; -use util::{profile, Cfg, CfgExpr, Config}; +use util::{profile, Cfg, CfgExpr, Config, Rustc}; -use super::{BuildConfig, Kind, TargetConfig, Unit}; +use super::{BuildConfig, BuildOutput, Kind, Unit}; mod target_info; pub use self::target_info::{FileFlavor, TargetInfo}; @@ -31,6 +32,12 @@ pub struct BuildContext<'a, 'cfg: 'a> { pub extra_compiler_args: Option<(Unit<'a>, Vec)>, pub packages: &'a PackageSet<'cfg>, + /// Information about the compiler + pub rustc: Rustc, + /// Build information for the host arch + pub host_config: TargetConfig, + /// Build information for the target + pub target_config: TargetConfig, pub target_info: TargetInfo, pub host_info: TargetInfo, pub incremental_env: Option, @@ -51,11 +58,19 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> { Err(_) => None, }; + let rustc = config.rustc(Some(ws))?; + let host_config = TargetConfig::new(config, &rustc.host)?; + let target_config = match build_config.requested_target.as_ref() { + Some(triple) => TargetConfig::new(config, triple)?, + None => host_config.clone(), + }; let (host_info, target_info) = { let _p = profile::start("BuildContext::probe_target_info"); debug!("probe_target_info"); - let host_info = TargetInfo::new(config, &build_config, Kind::Host)?; - let target_info = TargetInfo::new(config, &build_config, Kind::Target)?; + let host_info = + TargetInfo::new(config, &build_config.requested_target, &rustc, Kind::Host)?; + let target_info = + TargetInfo::new(config, &build_config.requested_target, &rustc, Kind::Target)?; (host_info, target_info) }; @@ -64,7 +79,10 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> { resolve, packages, config, + rustc, + target_config, target_info, + host_config, host_info, build_config, profiles, @@ -109,8 +127,8 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> { None => return true, }; let (name, info) = match kind { - Kind::Host => (self.build_config.host_triple(), &self.host_info), - Kind::Target => (self.build_config.target_triple(), &self.target_info), + Kind::Host => (self.host_triple(), &self.host_info), + Kind::Target => (self.target_triple(), &self.target_info), }; platform.matches(name, info.cfg()) } @@ -139,11 +157,29 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> { info.cfg().unwrap_or(&[]) } + /// The host arch triple + /// + /// e.g. x86_64-unknown-linux-gnu, would be + /// - machine: x86_64 + /// - hardware-platform: unknown + /// - operating system: linux-gnu + pub fn host_triple(&self) -> &str { + &self.rustc.host + } + + pub fn target_triple(&self) -> &str { + self.build_config + .requested_target + .as_ref() + .map(|s| s.as_str()) + .unwrap_or(self.host_triple()) + } + /// Get the target configuration for a particular host or target fn target_config(&self, kind: Kind) -> &TargetConfig { match kind { - Kind::Host => &self.build_config.host, - Kind::Target => &self.build_config.target, + Kind::Host => &self.host_config, + Kind::Target => &self.target_config, } } @@ -155,7 +191,8 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> { pub fn rustflags_args(&self, unit: &Unit) -> CargoResult> { env_args( self.config, - &self.build_config, + &self.build_config.requested_target, + self.host_triple(), self.info(&unit.kind).cfg(), unit.kind, "RUSTFLAGS", @@ -165,7 +202,8 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> { pub fn rustdocflags_args(&self, unit: &Unit) -> CargoResult> { env_args( self.config, - &self.build_config, + &self.build_config.requested_target, + self.host_triple(), self.info(&unit.kind).cfg(), unit.kind, "RUSTDOCFLAGS", @@ -193,6 +231,97 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> { } } +/// Information required to build for a target +#[derive(Clone, Default)] +pub struct TargetConfig { + /// The path of archiver (lib builder) for this target. + pub ar: Option, + /// The path of the linker for this target. + pub linker: Option, + /// Special build options for any necessary input files (filename -> options) + pub overrides: HashMap, +} + +impl TargetConfig { + pub fn new(config: &Config, triple: &str) -> CargoResult { + let key = format!("target.{}", triple); + let mut ret = TargetConfig { + ar: config.get_path(&format!("{}.ar", key))?.map(|v| v.val), + linker: config.get_path(&format!("{}.linker", key))?.map(|v| v.val), + overrides: HashMap::new(), + }; + let table = match config.get_table(&key)? { + Some(table) => table.val, + None => return Ok(ret), + }; + for (lib_name, value) in table { + match lib_name.as_str() { + "ar" | "linker" | "runner" | "rustflags" => continue, + _ => {} + } + + let mut output = BuildOutput { + library_paths: Vec::new(), + library_links: Vec::new(), + cfgs: Vec::new(), + env: Vec::new(), + metadata: Vec::new(), + rerun_if_changed: Vec::new(), + rerun_if_env_changed: Vec::new(), + warnings: Vec::new(), + }; + // We require deterministic order of evaluation, so we must sort the pairs by key first. + let mut pairs = Vec::new(); + for (k, value) in value.table(&lib_name)?.0 { + pairs.push((k, value)); + } + pairs.sort_by_key(|p| p.0); + for (k, value) in pairs { + let key = format!("{}.{}", key, k); + match &k[..] { + "rustc-flags" => { + let (flags, definition) = value.string(k)?; + let whence = format!("in `{}` (in {})", key, definition.display()); + let (paths, links) = BuildOutput::parse_rustc_flags(flags, &whence)?; + output.library_paths.extend(paths); + output.library_links.extend(links); + } + "rustc-link-lib" => { + let list = value.list(k)?; + output + .library_links + .extend(list.iter().map(|v| v.0.clone())); + } + "rustc-link-search" => { + let list = value.list(k)?; + output + .library_paths + .extend(list.iter().map(|v| PathBuf::from(&v.0))); + } + "rustc-cfg" => { + let list = value.list(k)?; + output.cfgs.extend(list.iter().map(|v| v.0.clone())); + } + "rustc-env" => for (name, val) in value.table(k)?.0 { + let val = val.string(name)?.0; + output.env.push((name.clone(), val.to_string())); + }, + "warning" | "rerun-if-changed" | "rerun-if-env-changed" => { + bail!("`{}` is not supported in build script overrides", k); + } + _ => { + let val = value.string(k)?.0; + output.metadata.push((k.clone(), val.to_string())); + } + } + } + ret.overrides.insert(lib_name, output); + } + + Ok(ret) + } +} + /// Acquire extra flags to pass to the compiler from various locations. /// /// The locations are: @@ -212,7 +341,8 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> { /// scripts, ...), even if it is the same as the target. fn env_args( config: &Config, - build_config: &BuildConfig, + requested_target: &Option, + host_triple: &str, target_cfg: Option<&[Cfg]>, kind: Kind, name: &str, @@ -236,7 +366,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. - let compiling_with_target = build_config.requested_target.is_some(); + let compiling_with_target = requested_target.is_some(); let is_target_kind = kind == Kind::Target; if compiling_with_target && !is_target_kind { @@ -261,11 +391,10 @@ fn env_args( .flat_map(|c| c.to_lowercase()) .collect::(); // Then the target.*.rustflags value... - let target = build_config - .requested_target + let target = requested_target .as_ref() .map(|s| s.as_str()) - .unwrap_or(build_config.host_triple()); + .unwrap_or(host_triple); let key = format!("target.{}.{}", target, name); if let Some(args) = config.get_list_or_split_string(&key)? { let args = args.val.into_iter(); diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs index ff691ff2a30..84950bd0dd3 100644 --- a/src/cargo/core/compiler/build_context/target_info.rs +++ b/src/cargo/core/compiler/build_context/target_info.rs @@ -3,8 +3,8 @@ use std::collections::hash_map::{Entry, HashMap}; use std::path::PathBuf; use std::str::{self, FromStr}; -use super::{env_args, BuildConfig}; -use util::{CargoResult, CargoResultExt, Cfg, Config, ProcessBuilder}; +use super::env_args; +use util::{CargoResult, CargoResultExt, Cfg, Config, ProcessBuilder, Rustc}; use core::TargetKind; use super::Kind; @@ -51,9 +51,21 @@ impl FileType { } impl TargetInfo { - pub fn new(config: &Config, build_config: &BuildConfig, kind: Kind) -> CargoResult { - let rustflags = env_args(config, build_config, None, kind, "RUSTFLAGS")?; - let mut process = build_config.rustc.process(); + pub fn new( + config: &Config, + requested_target: &Option, + rustc: &Rustc, + kind: Kind, + ) -> CargoResult { + let rustflags = env_args( + config, + requested_target, + &rustc.host, + None, + kind, + "RUSTFLAGS", + )?; + let mut process = rustc.process(); process .arg("-") .arg("--crate-name") @@ -62,8 +74,12 @@ impl TargetInfo { .args(&rustflags) .env_remove("RUST_LOG"); + let target_triple = requested_target + .as_ref() + .map(|s| s.as_str()) + .unwrap_or(&rustc.host); if kind == Kind::Target { - process.arg("--target").arg(&build_config.target_triple()); + process.arg("--target").arg(target_triple); } let crate_type_process = process.clone(); @@ -78,12 +94,11 @@ impl TargetInfo { with_cfg.arg("--print=cfg"); let mut has_cfg_and_sysroot = true; - let (output, error) = build_config - .rustc + let (output, error) = rustc .cached_output(&with_cfg) .or_else(|_| { has_cfg_and_sysroot = false; - build_config.rustc.cached_output(&process) + rustc.cached_output(&process) }) .chain_err(|| "failed to run `rustc` to learn about target-specific information")?; @@ -114,7 +129,7 @@ impl TargetInfo { } else { rustlib.push("lib"); rustlib.push("rustlib"); - rustlib.push(build_config.target_triple()); + rustlib.push(target_triple); rustlib.push("lib"); sysroot_libdir = Some(rustlib); } diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index ece186c9dc1..e6900e64cf5 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -7,6 +7,7 @@ use lazycell::LazyCell; use core::{Package, PackageId, Target, TargetKind}; use util::{self, join_paths, process, CargoResult, Config, ProcessBuilder}; +use super::BuildContext; /// A structure returning the result of a compilation. pub struct Compilation<'cfg> { @@ -67,25 +68,25 @@ pub struct Compilation<'cfg> { } impl<'cfg> Compilation<'cfg> { - pub fn new(config: &'cfg Config, rustc_process: ProcessBuilder) -> Compilation<'cfg> { + pub fn new<'a>(bcx: &BuildContext<'a, 'cfg>) -> Compilation<'cfg> { Compilation { libraries: HashMap::new(), native_dirs: BTreeSet::new(), // TODO: deprecated, remove root_output: PathBuf::from("/"), deps_output: PathBuf::from("/"), host_deps_output: PathBuf::from("/"), - host_dylib_path: None, - target_dylib_path: None, + host_dylib_path: bcx.host_info.sysroot_libdir.clone(), + target_dylib_path: bcx.target_info.sysroot_libdir.clone(), tests: Vec::new(), binaries: Vec::new(), extra_env: HashMap::new(), to_doc_test: Vec::new(), cfgs: HashMap::new(), rustdocflags: HashMap::new(), - config, - rustc_process, - host: String::new(), - target: String::new(), + config: bcx.config, + rustc_process: bcx.rustc.process(), + host: bcx.host_triple().to_string(), + target: bcx.target_triple().to_string(), target_runner: LazyCell::new(), } } diff --git a/src/cargo/core/compiler/context/compilation_files.rs b/src/cargo/core/compiler/context/compilation_files.rs index cf3046eec2d..3eb32c60e2f 100644 --- a/src/cargo/core/compiler/context/compilation_files.rs +++ b/src/cargo/core/compiler/context/compilation_files.rs @@ -268,7 +268,7 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { crate_type, flavor, unit.target.kind(), - bcx.build_config.target_triple(), + bcx.target_triple(), )?; match file_types { @@ -324,14 +324,14 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> { does not support these crate types", unsupported.join(", "), unit.pkg, - bcx.build_config.target_triple() + bcx.target_triple() ) } bail!( "cannot compile `{}` as the target `{}` does not \ support any of the output crate types", unit.pkg, - bcx.build_config.target_triple() + bcx.target_triple() ); } info!("Target filenames: {:?}", ret); @@ -384,7 +384,7 @@ fn compute_metadata<'a, 'cfg>( let __cargo_default_lib_metadata = env::var("__CARGO_DEFAULT_LIB_METADATA"); if !(unit.mode.is_any_test() || unit.mode.is_check()) && (unit.target.is_dylib() || unit.target.is_cdylib() - || (unit.target.is_bin() && bcx.build_config.target_triple().starts_with("wasm32-"))) + || (unit.target.is_bin() && bcx.target_triple().starts_with("wasm32-"))) && unit.pkg.package_id().source_id().is_path() && __cargo_default_lib_metadata.is_err() { @@ -442,7 +442,7 @@ fn compute_metadata<'a, 'cfg>( unit.target.name().hash(&mut hasher); unit.target.kind().hash(&mut hasher); - bcx.build_config.rustc.verbose_version.hash(&mut hasher); + bcx.rustc.verbose_version.hash(&mut hasher); // Seed the contents of __CARGO_DEFAULT_LIB_METADATA to the hasher if present. // This should be the release channel, to get a different hash for each channel. diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index 7c039fcca6b..78c6d34fcce 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -8,7 +8,6 @@ use jobserver::Client; use core::{Package, PackageId, Resolve, Target}; use core::profiles::Profile; -use ops::CompileMode; use util::errors::{CargoResult, CargoResultExt}; use util::{internal, profile, Config}; @@ -16,7 +15,7 @@ use super::custom_build::{self, BuildDeps, BuildScripts, BuildState}; use super::fingerprint::Fingerprint; use super::job_queue::JobQueue; use super::layout::Layout; -use super::{BuildContext, Compilation, Executor, FileFlavor, Kind}; +use super::{BuildContext, Compilation, CompileMode, Executor, FileFlavor, Kind}; mod unit_dependencies; use self::unit_dependencies::build_unit_dependencies; @@ -95,13 +94,10 @@ impl<'a, 'cfg> Context<'a, 'cfg> { .chain_err(|| "failed to create jobserver")?, }; - let mut compilation = Compilation::new(config, bcx.build_config.rustc.process()); - compilation.host_dylib_path = bcx.host_info.sysroot_libdir.clone(); - compilation.target_dylib_path = bcx.target_info.sysroot_libdir.clone(); Ok(Self { bcx, - compilation, - build_state: Arc::new(BuildState::new(&bcx.build_config)), + compilation: Compilation::new(bcx), + build_state: Arc::new(BuildState::new(&bcx.host_config, &bcx.target_config)), fingerprints: HashMap::new(), compiled: HashSet::new(), build_scripts: HashMap::new(), @@ -245,8 +241,6 @@ impl<'a, 'cfg> Context<'a, 'cfg> { self.compilation.native_dirs.insert(dir.clone()); } } - self.compilation.host = self.bcx.build_config.host_triple().to_string(); - self.compilation.target = self.bcx.build_config.target_triple().to_string(); Ok(self.compilation) } diff --git a/src/cargo/core/compiler/context/unit_dependencies.rs b/src/cargo/core/compiler/context/unit_dependencies.rs index ee66f62d970..e53c3da374d 100644 --- a/src/cargo/core/compiler/context/unit_dependencies.rs +++ b/src/cargo/core/compiler/context/unit_dependencies.rs @@ -15,11 +15,10 @@ //! (for example, with and without tests), so we actually build a dependency //! graph of `Unit`s, which capture these properties. -use super::{BuildContext, Kind, Unit}; +use super::{BuildContext, CompileMode, Kind, Unit}; use core::dependency::Kind as DepKind; use core::profiles::ProfileFor; use core::{Package, Target}; -use ops::CompileMode; use std::collections::HashMap; use CargoResult; @@ -35,7 +34,7 @@ pub fn build_unit_dependencies<'a, 'cfg>( // cleared, and avoid building the lib thrice (once with `panic`, once // without, once for --test). In particular, the lib included for // doctests and examples are `Build` mode here. - let profile_for = if unit.mode.is_any_test() || bcx.build_config.test { + let profile_for = if unit.mode.is_any_test() || bcx.build_config.test() { ProfileFor::TestDependency } else { ProfileFor::Any diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index b23b1256d66..f358a5eaa4f 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -12,7 +12,7 @@ use util::{self, internal, paths, profile}; use util::{Cfg, Freshness}; use super::job::Work; -use super::{fingerprint, Context, Kind, Unit}; +use super::{fingerprint, Context, Kind, TargetConfig, Unit}; /// Contains the parsed output of a custom build script. #[derive(Clone, Debug, Hash)] @@ -131,8 +131,8 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes .env( "TARGET", &match unit.kind { - Kind::Host => &bcx.build_config.host_triple(), - Kind::Target => bcx.build_config.target_triple(), + Kind::Host => &bcx.host_triple(), + Kind::Target => bcx.target_triple(), }, ) .env("DEBUG", debug.to_string()) @@ -145,12 +145,12 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes "debug" }, ) - .env("HOST", &bcx.build_config.host_triple()) - .env("RUSTC", &bcx.build_config.rustc.path) + .env("HOST", &bcx.host_triple()) + .env("RUSTC", &bcx.rustc.path) .env("RUSTDOC", &*bcx.config.rustdoc()?) .inherit_jobserver(&cx.jobserver); - if let Some(ref linker) = bcx.build_config.target.linker { + if let Some(ref linker) = bcx.target_config.linker { cmd.env("RUSTC_LINKER", linker); } @@ -231,7 +231,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes ); let build_scripts = super::load_build_deps(cx, unit); let kind = unit.kind; - let json_messages = bcx.build_config.json_messages; + let json_messages = bcx.build_config.json_messages(); // Check to see if the build script has already run, and if it has keep // track of whether it has told us about some explicit dependencies @@ -364,10 +364,10 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes } impl BuildState { - pub fn new(config: &super::BuildConfig) -> BuildState { + pub fn new(host_config: &TargetConfig, target_config: &TargetConfig) -> BuildState { let mut overrides = HashMap::new(); - let i1 = config.host.overrides.iter().map(|p| (p, Kind::Host)); - let i2 = config.target.overrides.iter().map(|p| (p, Kind::Target)); + let i1 = host_config.overrides.iter().map(|p| (p, Kind::Host)); + let i2 = target_config.overrides.iter().map(|p| (p, Kind::Target)); for ((name, output), kind) in i1.chain(i2) { overrides.insert((name.clone(), kind), output.clone()); } diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index 551aab14347..284bc599304 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -462,7 +462,7 @@ fn calculate<'a, 'cfg>( cx.used_in_plugin.contains(unit), // used when passing panic=abort )); let fingerprint = Arc::new(Fingerprint { - rustc: util::hash_u64(&bcx.build_config.rustc.verbose_version), + rustc: util::hash_u64(&bcx.rustc.verbose_version), target: util::hash_u64(&unit.target), profile: profile_hash, // Note that .0 is hashed here, not .1 which is the cwd. That doesn't diff --git a/src/cargo/core/compiler/job_queue.rs b/src/cargo/core/compiler/job_queue.rs index f167b49de64..235dccc93e6 100644 --- a/src/cargo/core/compiler/job_queue.rs +++ b/src/cargo/core/compiler/job_queue.rs @@ -11,12 +11,11 @@ use jobserver::{Acquired, HelperThread}; use core::profiles::Profile; use core::{PackageId, Target}; use handle_error; -use ops::CompileMode; use util::{internal, profile, CargoResult, CargoResultExt, ProcessBuilder}; use util::{Config, DependencyQueue, Dirty, Fresh, Freshness}; use super::job::Job; -use super::{BuildContext, Context, Kind, Unit}; +use super::{BuildContext, CompileMode, Context, Kind, Unit}; /// A management structure of the entire dependency graph to compile. /// diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 4e87f66c6f4..8b503aaae6a 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -1,4 +1,3 @@ -use std::collections::HashMap; use std::env; use std::ffi::{OsStr, OsString}; use std::fs; @@ -12,10 +11,9 @@ use serde_json; use core::profiles::{Lto, Profile}; use core::shell::ColorChoice; use core::{Feature, PackageId, Target}; -use ops::CompileMode; use util::errors::{CargoResult, CargoResultExt, Internal}; use util::paths; -use util::{self, machine_message, Config, Freshness, ProcessBuilder, Rustc}; +use util::{self, machine_message, Freshness, ProcessBuilder}; use util::{internal, join_paths, profile}; use self::job::{Job, Work}; @@ -23,12 +21,14 @@ use self::job_queue::JobQueue; use self::output_depinfo::output_depinfo; -pub use self::build_context::{BuildContext, FileFlavor, TargetInfo}; +pub use self::build_context::{BuildContext, FileFlavor, TargetConfig, TargetInfo}; +pub use self::build_config::{BuildConfig, CompileMode, MessageFormat}; pub use self::compilation::Compilation; pub use self::context::{Context, Unit}; pub use self::custom_build::{BuildMap, BuildOutput, BuildScripts}; pub use self::layout::is_bad_artifact_name; +mod build_config; mod build_context; mod compilation; mod context; @@ -48,203 +48,6 @@ pub enum Kind { Target, } -/// Configuration information for a rustc build. -pub struct BuildConfig { - pub rustc: Rustc, - /// Build information for the host arch - pub host: TargetConfig, - /// The target arch triple, defaults to host arch - pub requested_target: Option, - /// Build information for the target - pub target: TargetConfig, - /// How many rustc jobs to run in parallel - pub jobs: u32, - /// Whether we are building for release - pub release: bool, - /// Whether we are running tests - pub test: bool, - /// Whether to print std output in json format (for machine reading) - pub json_messages: bool, -} - -impl BuildConfig { - /// Parse all config files to learn about build configuration. Currently - /// configured options are: - /// - /// * build.jobs - /// * build.target - /// * target.$target.ar - /// * target.$target.linker - /// * target.$target.libfoo.metadata - pub fn new( - config: &Config, - jobs: Option, - requested_target: &Option, - rustc_info_cache: Option, - ) -> CargoResult { - if let &Some(ref s) = requested_target { - if s.trim().is_empty() { - bail!("target was empty") - } - } - let cfg_target = config.get_string("build.target")?.map(|s| s.val); - let target = requested_target.clone().or(cfg_target); - - if jobs.is_some() && config.jobserver_from_env().is_some() { - config.shell().warn( - "a `-j` argument was passed to Cargo but Cargo is \ - also configured with an external jobserver in \ - its environment, ignoring the `-j` parameter", - )?; - } - let cfg_jobs = match config.get_i64("build.jobs")? { - Some(v) => { - if v.val <= 0 { - bail!( - "build.jobs must be positive, but found {} in {}", - v.val, - v.definition - ) - } else if v.val >= i64::from(u32::max_value()) { - bail!( - "build.jobs is too large: found {} in {}", - v.val, - v.definition - ) - } else { - Some(v.val as u32) - } - } - None => None, - }; - let jobs = jobs.or(cfg_jobs).unwrap_or(::num_cpus::get() as u32); - let rustc = config.rustc(rustc_info_cache)?; - let host_config = TargetConfig::new(config, &rustc.host)?; - let target_config = match target.as_ref() { - Some(triple) => TargetConfig::new(config, triple)?, - None => host_config.clone(), - }; - Ok(BuildConfig { - rustc, - requested_target: target, - jobs, - host: host_config, - target: target_config, - release: false, - test: false, - json_messages: false, - }) - } - - /// The host arch triple - /// - /// e.g. x86_64-unknown-linux-gnu, would be - /// - machine: x86_64 - /// - hardware-platform: unknown - /// - operating system: linux-gnu - pub fn host_triple(&self) -> &str { - &self.rustc.host - } - - pub fn target_triple(&self) -> &str { - self.requested_target - .as_ref() - .map(|s| s.as_str()) - .unwrap_or(self.host_triple()) - } -} - -/// Information required to build for a target -#[derive(Clone, Default)] -pub struct TargetConfig { - /// The path of archiver (lib builder) for this target. - pub ar: Option, - /// The path of the linker for this target. - pub linker: Option, - /// Special build options for any necessary input files (filename -> options) - pub overrides: HashMap, -} - -impl TargetConfig { - pub fn new(config: &Config, triple: &str) -> CargoResult { - let key = format!("target.{}", triple); - let mut ret = TargetConfig { - ar: config.get_path(&format!("{}.ar", key))?.map(|v| v.val), - linker: config.get_path(&format!("{}.linker", key))?.map(|v| v.val), - overrides: HashMap::new(), - }; - let table = match config.get_table(&key)? { - Some(table) => table.val, - None => return Ok(ret), - }; - for (lib_name, value) in table { - match lib_name.as_str() { - "ar" | "linker" | "runner" | "rustflags" => continue, - _ => {} - } - - let mut output = BuildOutput { - library_paths: Vec::new(), - library_links: Vec::new(), - cfgs: Vec::new(), - env: Vec::new(), - metadata: Vec::new(), - rerun_if_changed: Vec::new(), - rerun_if_env_changed: Vec::new(), - warnings: Vec::new(), - }; - // We require deterministic order of evaluation, so we must sort the pairs by key first. - let mut pairs = Vec::new(); - for (k, value) in value.table(&lib_name)?.0 { - pairs.push((k, value)); - } - pairs.sort_by_key(|p| p.0); - for (k, value) in pairs { - let key = format!("{}.{}", key, k); - match &k[..] { - "rustc-flags" => { - let (flags, definition) = value.string(k)?; - let whence = format!("in `{}` (in {})", key, definition.display()); - let (paths, links) = BuildOutput::parse_rustc_flags(flags, &whence)?; - output.library_paths.extend(paths); - output.library_links.extend(links); - } - "rustc-link-lib" => { - let list = value.list(k)?; - output - .library_links - .extend(list.iter().map(|v| v.0.clone())); - } - "rustc-link-search" => { - let list = value.list(k)?; - output - .library_paths - .extend(list.iter().map(|v| PathBuf::from(&v.0))); - } - "rustc-cfg" => { - let list = value.list(k)?; - output.cfgs.extend(list.iter().map(|v| v.0.clone())); - } - "rustc-env" => for (name, val) in value.table(k)?.0 { - let val = val.string(name)?.0; - output.env.push((name.clone(), val.to_string())); - }, - "warning" | "rerun-if-changed" | "rerun-if-env-changed" => { - bail!("`{}` is not supported in build script overrides", k); - } - _ => { - let val = value.string(k)?.0; - output.metadata.push((k.clone(), val.to_string())); - } - } - } - ret.overrides.insert(lib_name, output); - } - - Ok(ret) - } -} - /// A glorified callback for executing calls to rustc. Rather than calling rustc /// directly, we'll use an Executor, giving clients an opportunity to intercept /// the build calls. @@ -382,7 +185,7 @@ fn rustc<'a, 'cfg>( let dep_info_loc = fingerprint::dep_info_loc(&mut cx, unit); rustc.args(&cx.bcx.rustflags_args(unit)?); - let json_messages = cx.bcx.build_config.json_messages; + let json_messages = cx.bcx.build_config.json_messages(); let package_id = unit.pkg.package_id().clone(); let target = unit.target.clone(); @@ -569,7 +372,7 @@ fn link_targets<'a, 'cfg>( .into_iter() .map(|s| s.to_owned()) .collect(); - let json_messages = bcx.build_config.json_messages; + let json_messages = bcx.build_config.json_messages(); Ok(Work::new(move |_| { // If we're a "root crate", e.g. the target of this compilation, then we @@ -876,7 +679,7 @@ fn build_base_args<'a, 'cfg>( ColorChoice::CargoAuto => {} } - if bcx.build_config.json_messages { + if bcx.build_config.json_messages() { cmd.arg("--error-format").arg("json"); } diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs index fbca991d68f..8ad40abd8f6 100644 --- a/src/cargo/core/profiles.rs +++ b/src/cargo/core/profiles.rs @@ -1,9 +1,9 @@ use std::collections::HashSet; use std::{cmp, fmt, hash}; +use core::compiler::CompileMode; use core::interning::InternedString; use core::Shell; -use ops::CompileMode; use util::lev_distance::lev_distance; use util::toml::{StringOrBool, TomlProfile, U32OrBool}; use util::CargoResult; diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index f9f032760a7..65e47fb9aeb 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -1,10 +1,10 @@ use std::fs; use std::path::Path; -use core::compiler::{BuildConfig, BuildContext, Context, Kind, Unit}; +use core::compiler::{BuildConfig, BuildContext, CompileMode, Context, Kind, Unit}; use core::profiles::ProfileFor; use core::Workspace; -use ops::{self, CompileMode}; +use ops; use util::errors::{CargoResult, CargoResultExt}; use util::paths; use util::Config; @@ -88,7 +88,7 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> { } } - let mut build_config = BuildConfig::new(config, Some(1), &opts.target, None)?; + let mut build_config = BuildConfig::new(config, Some(1), &opts.target, CompileMode::Build)?; build_config.release = opts.release; let bcx = BuildContext::new( ws, diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index c13ad0a3551..36eac5b3b41 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -23,27 +23,25 @@ //! use std::collections::HashSet; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::Arc; use core::compiler::{BuildConfig, BuildContext, Compilation, Context, DefaultExecutor, Executor}; -use core::compiler::{Kind, Unit}; +use core::compiler::{CompileMode, Kind, Unit}; use core::profiles::{ProfileFor, Profiles}; use core::resolver::{Method, Resolve}; use core::{Package, Source, Target}; use core::{PackageId, PackageIdSpec, TargetKind, Workspace}; use ops; use util::config::Config; -use util::{lev_distance, profile, CargoResult, CargoResultExt}; +use util::{lev_distance, profile, CargoResult}; /// Contains information about how a package should be compiled. #[derive(Debug)] pub struct CompileOptions<'a> { pub config: &'a Config, - /// Number of concurrent jobs to use. - pub jobs: Option, - /// The target platform to compile for (example: `i686-unknown-linux-gnu`). - pub target: Option, + /// Configuration information for a rustc build + pub build_config: BuildConfig, /// Extra features to build for the root package pub features: Vec, /// Flag whether all available features should be built for the root package @@ -55,12 +53,6 @@ pub struct CompileOptions<'a> { /// Filter to apply to the root package to select which targets will be /// built. pub filter: CompileFilter, - /// Whether this is a release build or not - pub release: bool, - /// Mode for this compile. - pub mode: CompileMode, - /// `--error_format` flag for the compiler. - pub message_format: MessageFormat, /// Extra arguments to be passed to rustdoc (for main crate and dependencies) pub target_rustdoc_args: Option>, /// The specified target will be compiled with all the available arguments, @@ -75,118 +67,24 @@ pub struct CompileOptions<'a> { } impl<'a> CompileOptions<'a> { - pub fn default(config: &'a Config, mode: CompileMode) -> CompileOptions<'a> { - CompileOptions { + pub fn new(config: &'a Config, mode: CompileMode) -> CargoResult> { + Ok(CompileOptions { config, - jobs: None, - target: None, + build_config: BuildConfig::new(config, None, &None, mode)?, features: Vec::new(), all_features: false, no_default_features: false, spec: ops::Packages::Packages(Vec::new()), - mode, - release: false, filter: CompileFilter::Default { required_features_filterable: false, }, - message_format: MessageFormat::Human, target_rustdoc_args: None, target_rustc_args: None, export_dir: None, - } - } -} - -/// The general "mode" of what to do. -/// This is used for two purposes. The commands themselves pass this in to -/// `compile_ws` to tell it the general execution strategy. This influences -/// the default targets selected. The other use is in the `Unit` struct -/// to indicate what is being done with a specific target. -#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash)] -pub enum CompileMode { - /// A target being built for a test. - Test, - /// Building a target with `rustc` (lib or bin). - Build, - /// Building a target with `rustc` to emit `rmeta` metadata only. If - /// `test` is true, then it is also compiled with `--test` to check it like - /// a test. - Check { test: bool }, - /// Used to indicate benchmarks should be built. This is not used in - /// `Target` because it is essentially the same as `Test` (indicating - /// `--test` should be passed to rustc) and by using `Test` instead it - /// allows some de-duping of Units to occur. - Bench, - /// A target that will be documented with `rustdoc`. - /// If `deps` is true, then it will also document all dependencies. - Doc { deps: bool }, - /// A target that will be tested with `rustdoc`. - Doctest, - /// A marker for Units that represent the execution of a `build.rs` - /// script. - RunCustomBuild, -} - -impl CompileMode { - /// Returns true if the unit is being checked. - pub fn is_check(&self) -> bool { - match *self { - CompileMode::Check { .. } => true, - _ => false, - } - } - - /// Returns true if this is a doc or doctest. Be careful using this. - /// Although both run rustdoc, the dependencies for those two modes are - /// very different. - pub fn is_doc(&self) -> bool { - match *self { - CompileMode::Doc { .. } | CompileMode::Doctest => true, - _ => false, - } - } - - /// Returns true if this is any type of test (test, benchmark, doctest, or - /// check-test). - pub fn is_any_test(&self) -> bool { - match *self { - CompileMode::Test - | CompileMode::Bench - | CompileMode::Check { test: true } - | CompileMode::Doctest => true, - _ => false, - } - } - - /// Returns true if this is the *execution* of a `build.rs` script. - pub fn is_run_custom_build(&self) -> bool { - *self == CompileMode::RunCustomBuild - } - - /// List of all modes (currently used by `cargo clean -p` for computing - /// all possible outputs). - pub fn all_modes() -> &'static [CompileMode] { - static ALL: [CompileMode; 9] = [ - CompileMode::Test, - CompileMode::Build, - CompileMode::Check { test: true }, - CompileMode::Check { test: false }, - CompileMode::Bench, - CompileMode::Doc { deps: true }, - CompileMode::Doc { deps: false }, - CompileMode::Doctest, - CompileMode::RunCustomBuild, - ]; - &ALL + }) } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum MessageFormat { - Human, - Json, -} - #[derive(Clone, PartialEq, Eq, Debug)] pub enum Packages { Default, @@ -304,44 +202,17 @@ pub fn compile_ws<'a>( ) -> CargoResult> { let CompileOptions { config, - jobs, - ref target, + ref build_config, ref spec, ref features, all_features, no_default_features, - release, - mode, - message_format, ref filter, ref target_rustdoc_args, ref target_rustc_args, ref export_dir, } = *options; - let target = match target { - &Some(ref target) if target.ends_with(".json") => { - let path = Path::new(target) - .canonicalize() - .chain_err(|| format_err!("Target path {:?} is not a valid file", target))?; - Some(path.into_os_string() - .into_string() - .map_err(|_| format_err!("Target path is not valid unicode"))?) - } - other => other.clone(), - }; - - if jobs == Some(0) { - bail!("jobs must be at least 1") - } - - let rustc_info_cache = ws.target_dir() - .join(".rustc_info.json") - .into_path_unlocked(); - let mut build_config = BuildConfig::new(config, jobs, &target, Some(rustc_info_cache))?; - build_config.release = release; - build_config.test = mode == CompileMode::Test || mode == CompileMode::Bench; - build_config.json_messages = message_format == MessageFormat::Json; let default_arch_kind = if build_config.requested_target.is_some() { Kind::Target } else { @@ -351,7 +222,7 @@ pub fn compile_ws<'a>( let specs = spec.into_package_id_specs(ws)?; let features = Method::split_features(features); let method = Method::Required { - dev_deps: ws.require_optional_deps() || filter.need_dev_deps(mode), + dev_deps: ws.require_optional_deps() || filter.need_dev_deps(build_config.mode), features: &features, all_features, uses_default_features: !no_default_features, @@ -397,9 +268,8 @@ pub fn compile_ws<'a>( &to_builds, filter, default_arch_kind, - mode, &resolve_with_overrides, - release, + build_config, )?; if let Some(args) = extra_args { @@ -570,15 +440,14 @@ fn generate_targets<'a>( packages: &[&'a Package], filter: &CompileFilter, default_arch_kind: Kind, - mode: CompileMode, resolve: &Resolve, - release: bool, + build_config: &BuildConfig, ) -> CargoResult>> { let mut units = Vec::new(); // Helper for creating a Unit struct. let new_unit = |pkg: &'a Package, target: &'a Target, target_mode: CompileMode| { - let profile_for = if mode.is_any_test() { + let profile_for = if build_config.mode.is_any_test() { // NOTE: The ProfileFor here is subtle. If you have a profile // with `panic` set, the `panic` flag is cleared for // tests/benchmarks and their dependencies. If we left this @@ -629,7 +498,7 @@ fn generate_targets<'a>( ws.is_member(pkg), profile_for, target_mode, - release, + build_config.release, ); // Once the profile has been selected for benchmarks, we don't need to // distinguish between benches and tests. Switching the mode allows @@ -665,12 +534,17 @@ fn generate_targets<'a>( CompileFilter::Default { required_features_filterable, } => { - let default_units = generate_default_targets(pkg.targets(), mode) + let default_units = generate_default_targets(pkg.targets(), build_config.mode) .iter() - .map(|t| (new_unit(pkg, t, mode), !required_features_filterable)) + .map(|t| { + ( + new_unit(pkg, t, build_config.mode), + !required_features_filterable, + ) + }) .collect::>(); proposals.extend(default_units); - if mode == CompileMode::Test { + if build_config.mode == CompileMode::Test { // Include the lib as it will be required for doctests. if let Some(t) = pkg.targets().iter().find(|t| t.is_lib() && t.doctested()) { proposals.push((new_unit(pkg, t, CompileMode::Build), false)); @@ -687,7 +561,7 @@ fn generate_targets<'a>( } => { if lib { if let Some(target) = pkg.targets().iter().find(|t| t.is_lib()) { - proposals.push((new_unit(pkg, target, mode), false)); + proposals.push((new_unit(pkg, target, build_config.mode), false)); } else if !all_targets { bail!("no library targets found") } @@ -698,10 +572,10 @@ fn generate_targets<'a>( FilterRule::All => Target::tested, FilterRule::Just(_) => Target::is_test, }; - let test_mode = match mode { + let test_mode = match build_config.mode { CompileMode::Build => CompileMode::Test, CompileMode::Check { .. } => CompileMode::Check { test: true }, - _ => mode, + _ => build_config.mode, }; // If --benches was specified, add all targets that would be // generated by `cargo bench`. @@ -709,20 +583,22 @@ fn generate_targets<'a>( FilterRule::All => Target::benched, FilterRule::Just(_) => Target::is_bench, }; - let bench_mode = match mode { + let bench_mode = match build_config.mode { CompileMode::Build => CompileMode::Bench, CompileMode::Check { .. } => CompileMode::Check { test: true }, - _ => mode, + _ => build_config.mode, }; proposals.extend( list_rule_targets(pkg, bins, "bin", Target::is_bin)? .into_iter() - .map(|(t, required)| (new_unit(pkg, t, mode), required)) + .map(|(t, required)| (new_unit(pkg, t, build_config.mode), required)) .chain( list_rule_targets(pkg, examples, "example", Target::is_example)? .into_iter() - .map(|(t, required)| (new_unit(pkg, t, mode), required)), + .map(|(t, required)| { + (new_unit(pkg, t, build_config.mode), required) + }), ) .chain( list_rule_targets(pkg, tests, "test", test_filter)? @@ -741,7 +617,7 @@ fn generate_targets<'a>( // If any integration tests/benches are being run, make sure that // binaries are built as well. - if !mode.is_check() && proposals.iter().any(|&(ref unit, _)| { + if !build_config.mode.is_check() && proposals.iter().any(|&(ref unit, _)| { unit.mode.is_any_test() && (unit.target.is_test() || unit.target.is_bench()) }) { proposals.extend( diff --git a/src/cargo/ops/cargo_doc.rs b/src/cargo/ops/cargo_doc.rs index ed71ddc2acb..ab3aa62cee1 100644 --- a/src/cargo/ops/cargo_doc.rs +++ b/src/cargo/ops/cargo_doc.rs @@ -91,7 +91,7 @@ pub fn doc(ws: &Workspace, options: &DocOptions) -> CargoResult<()> { // nothing we can do about it and otherwise if it's getting overwritten // then that's also ok! let mut target_dir = ws.target_dir(); - if let Some(ref triple) = options.compile_opts.target { + if let Some(ref triple) = options.compile_opts.build_config.requested_target { target_dir.push(Path::new(triple).file_stem().unwrap()); } let path = target_dir.join("doc").join(&name).join("index.html"); diff --git a/src/cargo/ops/cargo_fetch.rs b/src/cargo/ops/cargo_fetch.rs index db400fb4de4..ce9a788bc89 100644 --- a/src/cargo/ops/cargo_fetch.rs +++ b/src/cargo/ops/cargo_fetch.rs @@ -1,4 +1,4 @@ -use core::compiler::{BuildConfig, Kind, TargetInfo}; +use core::compiler::{BuildConfig, CompileMode, Kind, TargetInfo}; use core::{PackageSet, Resolve, Workspace}; use ops; use std::collections::HashSet; @@ -19,13 +19,14 @@ pub fn fetch<'a>( let (packages, resolve) = ops::resolve_ws(ws)?; let jobs = Some(1); - let build_config = BuildConfig::new(ws.config(), jobs, &options.target, None)?; - let target_info = TargetInfo::new(ws.config(), &build_config, Kind::Target)?; + let config = ws.config(); + let build_config = BuildConfig::new(config, jobs, &options.target, CompileMode::Build)?; + let rustc = config.rustc(Some(ws))?; + let target_info = + TargetInfo::new(config, &build_config.requested_target, &rustc, Kind::Target)?; { let mut fetched_packages = HashSet::new(); - let mut deps_to_fetch = ws.members() - .map(|p| p.package_id()) - .collect::>(); + let mut deps_to_fetch = ws.members().map(|p| p.package_id()).collect::>(); while let Some(id) = deps_to_fetch.pop() { if !fetched_packages.insert(id) { diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index 19774f5e356..f9de8286824 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -10,7 +10,7 @@ use git2; use tar::{Archive, Builder, EntryType, Header}; use core::{Package, Source, SourceId, Workspace}; -use core::compiler::DefaultExecutor; +use core::compiler::{BuildConfig, CompileMode, DefaultExecutor}; use sources::PathSource; use util::{self, internal, Config, FileLock}; use util::paths; @@ -337,8 +337,7 @@ fn run_verify(ws: &Workspace, tar: &FileLock, opts: &PackageOpts) -> CargoResult None, &ops::CompileOptions { config, - jobs: opts.jobs, - target: opts.target.clone(), + build_config: BuildConfig::new(config, opts.jobs, &opts.target, CompileMode::Build)?, features: Vec::new(), no_default_features: false, all_features: false, @@ -346,9 +345,6 @@ fn run_verify(ws: &Workspace, tar: &FileLock, opts: &PackageOpts) -> CargoResult filter: ops::CompileFilter::Default { required_features_filterable: true, }, - release: false, - message_format: ops::MessageFormat::Human, - mode: ops::CompileMode::Build, target_rustdoc_args: None, target_rustc_args: None, export_dir: None, diff --git a/src/cargo/ops/mod.rs b/src/cargo/ops/mod.rs index c72d03a89e3..1d061936131 100644 --- a/src/cargo/ops/mod.rs +++ b/src/cargo/ops/mod.rs @@ -1,6 +1,6 @@ pub use self::cargo_clean::{clean, CleanOptions}; pub use self::cargo_compile::{compile, compile_with_exec, compile_ws, CompileOptions}; -pub use self::cargo_compile::{CompileFilter, CompileMode, FilterRule, MessageFormat, Packages}; +pub use self::cargo_compile::{CompileFilter, FilterRule, Packages}; pub use self::cargo_read_manifest::{read_package, read_packages}; pub use self::cargo_run::run; pub use self::cargo_install::{install, install_list, uninstall}; diff --git a/src/cargo/util/config.rs b/src/cargo/util/config.rs index 91cf39cc7a8..f3a1d796810 100644 --- a/src/cargo/util/config.rs +++ b/src/cargo/util/config.rs @@ -20,7 +20,7 @@ use toml; use lazycell::LazyCell; use core::shell::Verbosity; -use core::{CliUnstable, Shell, SourceId}; +use core::{CliUnstable, Shell, SourceId, Workspace}; use ops; use url::Url; use util::ToUrl; @@ -170,7 +170,12 @@ impl Config { } /// Get the path to the `rustc` executable - pub fn rustc(&self, cache_location: Option) -> CargoResult { + pub fn rustc(&self, ws: Option<&Workspace>) -> CargoResult { + let cache_location = ws.map(|ws| { + ws.target_dir() + .join(".rustc_info.json") + .into_path_unlocked() + }); Rustc::new( self.get_tool("rustc")?, self.maybe_get_tool("rustc_wrapper")?,