--- /dev/null
+use std::env;
+use std::path::Path;
+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 super::{BuildConfig, Kind, TargetConfig, Unit};
+
+mod target_info;
+pub use self::target_info::{FileFlavor, TargetInfo};
+
+/// The build context, containing all information about a build task
+pub struct BuildContext<'a, 'cfg: 'a> {
+ /// The workspace the build is for
+ pub ws: &'a Workspace<'cfg>,
+ /// The cargo configuration
+ pub config: &'cfg Config,
+ /// The dependency graph for our build
+ pub resolve: &'a Resolve,
+ pub profiles: &'a Profiles,
+ pub build_config: &'a BuildConfig,
+ /// This is a workaround to carry the extra compiler args for either
+ /// `rustc` or `rustdoc` given on the command-line for the commands `cargo
+ /// rustc` and `cargo rustdoc`. These commands only support one target,
+ /// but we don't want the args passed to any dependencies, so we include
+ /// the `Unit` corresponding to the top-level target.
+ pub extra_compiler_args: Option<(Unit<'a>, Vec<String>)>,
+ pub packages: &'a PackageSet<'cfg>,
+
+ pub target_info: TargetInfo,
+ pub host_info: TargetInfo,
+ pub incremental_env: Option<bool>,
+}
+
+impl<'a, 'cfg> BuildContext<'a, 'cfg> {
+ pub fn new(
+ ws: &'a Workspace<'cfg>,
+ resolve: &'a Resolve,
+ packages: &'a PackageSet<'cfg>,
+ config: &'cfg Config,
+ build_config: &'a BuildConfig,
+ profiles: &'a Profiles,
+ extra_compiler_args: Option<(Unit<'a>, Vec<String>)>,
+ ) -> CargoResult<BuildContext<'a, 'cfg>> {
+ let incremental_env = match env::var("CARGO_INCREMENTAL") {
+ Ok(v) => Some(v == "1"),
+ Err(_) => None,
+ };
+
+ 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)?;
+ (host_info, target_info)
+ };
+
+ Ok(BuildContext {
+ ws,
+ resolve,
+ packages,
+ config,
+ target_info,
+ host_info,
+ build_config,
+ profiles,
+ incremental_env,
+ extra_compiler_args,
+ })
+ }
+
+ pub fn extern_crate_name(&self, unit: &Unit<'a>, dep: &Unit<'a>) -> CargoResult<String> {
+ let deps = {
+ let a = unit.pkg.package_id();
+ let b = dep.pkg.package_id();
+ if a == b {
+ &[]
+ } else {
+ self.resolve.dependencies_listed(a, b)
+ }
+ };
+
+ let crate_name = dep.target.crate_name();
+ let mut names = deps.iter()
+ .map(|d| d.rename().unwrap_or(&crate_name));
+ let name = names.next().unwrap_or(&crate_name);
+ for n in names {
+ if n == name {
+ continue
+ }
+ bail!("multiple dependencies listed for the same crate must \
+ all have the same name, but the dependency on `{}` \
+ is listed as having different names", dep.pkg.package_id());
+ }
+ Ok(name.to_string())
+ }
+
+ /// Whether a dependency should be compiled for the host or target platform,
+ /// specified by `Kind`.
+ pub fn dep_platform_activated(&self, dep: &Dependency, kind: Kind) -> bool {
+ // If this dependency is only available for certain platforms,
+ // make sure we're only enabling it for that platform.
+ let platform = match dep.platform() {
+ Some(p) => p,
+ 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),
+ };
+ platform.matches(name, info.cfg())
+ }
+
+ /// Gets a package for the given package id.
+ pub fn get_package(&self, id: &PackageId) -> CargoResult<&'a Package> {
+ self.packages.get(id)
+ }
+
+ /// Get the user-specified linker for a particular host or target
+ pub fn linker(&self, kind: Kind) -> Option<&Path> {
+ self.target_config(kind).linker.as_ref().map(|s| s.as_ref())
+ }
+
+ /// Get the user-specified `ar` program for a particular host or target
+ pub fn ar(&self, kind: Kind) -> Option<&Path> {
+ self.target_config(kind).ar.as_ref().map(|s| s.as_ref())
+ }
+
+ /// Get the list of cfg printed out from the compiler for the specified kind
+ pub fn cfg(&self, kind: Kind) -> &[Cfg] {
+ let info = match kind {
+ Kind::Host => &self.host_info,
+ Kind::Target => &self.target_info,
+ };
+ info.cfg().unwrap_or(&[])
+ }
+
+ /// 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,
+ }
+ }
+
+ /// Number of jobs specified for this build
+ pub fn jobs(&self) -> u32 {
+ self.build_config.jobs
+ }
+
+ pub fn rustflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
+ env_args(
+ self.config,
+ &self.build_config,
+ self.info(&unit.kind).cfg(),
+ unit.kind,
+ "RUSTFLAGS",
+ )
+ }
+
+ pub fn rustdocflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
+ env_args(
+ self.config,
+ &self.build_config,
+ self.info(&unit.kind).cfg(),
+ unit.kind,
+ "RUSTDOCFLAGS",
+ )
+ }
+
+ pub fn show_warnings(&self, pkg: &PackageId) -> bool {
+ pkg.source_id().is_path() || self.config.extra_verbose()
+ }
+
+ fn info(&self, kind: &Kind) -> &TargetInfo {
+ match *kind {
+ Kind::Host => &self.host_info,
+ Kind::Target => &self.target_info,
+ }
+ }
+
+ pub fn extra_args_for(&self, unit: &Unit<'a>) -> Option<&Vec<String>> {
+ if let Some((ref args_unit, ref args)) = self.extra_compiler_args {
+ if args_unit == unit {
+ return Some(args);
+ }
+ }
+ None
+ }
+}
+
+/// Acquire extra flags to pass to the compiler from various locations.
+///
+/// The locations are:
+///
+/// - the `RUSTFLAGS` environment variable
+///
+/// then if this was not found
+///
+/// - `target.*.rustflags` from the manifest (Cargo.toml)
+/// - `target.cfg(..).rustflags` from the manifest
+///
+/// then if neither of these were found
+///
+/// - `build.rustflags` from the manifest
+///
+/// Note that if a `target` is specified, no args will be passed to host code (plugins, build
+/// scripts, ...), even if it is the same as the target.
+fn env_args(
+ config: &Config,
+ build_config: &BuildConfig,
+ target_cfg: Option<&[Cfg]>,
+ kind: Kind,
+ name: &str,
+) -> CargoResult<Vec<String>> {
+ // We *want* to apply RUSTFLAGS only to builds for the
+ // requested target architecture, and not to things like build
+ // scripts and plugins, which may be for an entirely different
+ // architecture. Cargo's present architecture makes it quite
+ // hard to only apply flags to things that are not build
+ // scripts and plugins though, so we do something more hacky
+ // instead to avoid applying the same RUSTFLAGS to multiple targets
+ // arches:
+ //
+ // 1) If --target is not specified we just apply RUSTFLAGS to
+ // all builds; they are all going to have the same target.
+ //
+ // 2) If --target *is* specified then we only apply RUSTFLAGS
+ // to compilation units with the Target kind, which indicates
+ // it was chosen by the --target flag.
+ //
+ // 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 is_target_kind = kind == Kind::Target;
+
+ if compiling_with_target && !is_target_kind {
+ // This is probably a build script or plugin and we're
+ // compiling with --target. In this scenario there are
+ // no rustflags we can apply.
+ return Ok(Vec::new());
+ }
+
+ // First try RUSTFLAGS from the environment
+ if let Ok(a) = env::var(name) {
+ let args = a.split(' ')
+ .map(str::trim)
+ .filter(|s| !s.is_empty())
+ .map(str::to_string);
+ return Ok(args.collect());
+ }
+
+ let mut rustflags = Vec::new();
+
+ let name = name.chars()
+ .flat_map(|c| c.to_lowercase())
+ .collect::<String>();
+ // Then the target.*.rustflags value...
+ let target = build_config
+ .requested_target
+ .as_ref()
+ .map(|s| s.as_str())
+ .unwrap_or(build_config.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();
+ rustflags.extend(args);
+ }
+ // ...including target.'cfg(...)'.rustflags
+ if let Some(target_cfg) = target_cfg {
+ if let Some(table) = config.get_table("target")? {
+ let cfgs = table.val.keys().filter_map(|t| {
+ if t.starts_with("cfg(") && t.ends_with(')') {
+ let cfg = &t[4..t.len() - 1];
+ CfgExpr::from_str(cfg).ok().and_then(|c| {
+ if c.matches(target_cfg) {
+ Some(t)
+ } else {
+ None
+ }
+ })
+ } else {
+ None
+ }
+ });
+
+ // Note that we may have multiple matching `[target]` sections and
+ // because we're passing flags to the compiler this can affect
+ // cargo's caching and whether it rebuilds. Ensure a deterministic
+ // ordering through sorting for now. We may perhaps one day wish to
+ // ensure a deterministic ordering via the order keys were defined
+ // in files perhaps.
+ let mut cfgs = cfgs.collect::<Vec<_>>();
+ cfgs.sort();
+
+ for n in cfgs {
+ let key = format!("target.{}.{}", n, name);
+ if let Some(args) = config.get_list_or_split_string(&key)? {
+ let args = args.val.into_iter();
+ rustflags.extend(args);
+ }
+ }
+ }
+ }
+
+ if !rustflags.is_empty() {
+ return Ok(rustflags);
+ }
+
+ // Then the build.rustflags value
+ let key = format!("build.{}", name);
+ if let Some(args) = config.get_list_or_split_string(&key)? {
+ let args = args.val.into_iter();
+ return Ok(args.collect());
+ }
+
+ Ok(Vec::new())
+}
--- /dev/null
+use std::cell::RefCell;
+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 core::TargetKind;
+use super::Kind;
+
+#[derive(Clone)]
+pub struct TargetInfo {
+ crate_type_process: Option<ProcessBuilder>,
+ crate_types: RefCell<HashMap<String, Option<(String, String)>>>,
+ cfg: Option<Vec<Cfg>>,
+ pub sysroot_libdir: Option<PathBuf>,
+}
+
+/// Type of each file generated by a Unit.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum FileFlavor {
+ /// Not a special file type.
+ Normal,
+ /// It is something you can link against (e.g. a library)
+ Linkable,
+ /// It is a piece of external debug information (e.g. *.dSYM and *.pdb)
+ DebugInfo,
+}
+
+pub struct FileType {
+ pub flavor: FileFlavor,
+ suffix: String,
+ prefix: String,
+ // wasm bin target will generate two files in deps such as
+ // "web-stuff.js" and "web_stuff.wasm". Note the different usages of
+ // "-" and "_". should_replace_hyphens is a flag to indicate that
+ // we need to convert the stem "web-stuff" to "web_stuff", so we
+ // won't miss "web_stuff.wasm".
+ should_replace_hyphens: bool,
+}
+
+impl FileType {
+ pub fn filename(&self, stem: &str) -> String {
+ let stem = if self.should_replace_hyphens {
+ stem.replace("-", "_")
+ } else {
+ stem.to_string()
+ };
+ format!("{}{}{}", self.prefix, stem, self.suffix)
+ }
+}
+
+impl TargetInfo {
+ pub fn new(config: &Config, build_config: &BuildConfig, kind: Kind) -> CargoResult<TargetInfo> {
+ let rustflags = env_args(config, build_config, None, kind, "RUSTFLAGS")?;
+ let mut process = build_config.rustc.process();
+ process
+ .arg("-")
+ .arg("--crate-name")
+ .arg("___")
+ .arg("--print=file-names")
+ .args(&rustflags)
+ .env_remove("RUST_LOG");
+
+ if kind == Kind::Target {
+ process.arg("--target").arg(&build_config.target_triple());
+ }
+
+ let crate_type_process = process.clone();
+ const KNOWN_CRATE_TYPES: &[&str] =
+ &["bin", "rlib", "dylib", "cdylib", "staticlib", "proc-macro"];
+ for crate_type in KNOWN_CRATE_TYPES.iter() {
+ process.arg("--crate-type").arg(crate_type);
+ }
+
+ let mut with_cfg = process.clone();
+ with_cfg.arg("--print=sysroot");
+ with_cfg.arg("--print=cfg");
+
+ let mut has_cfg_and_sysroot = true;
+ let (output, error) = build_config
+ .rustc
+ .cached_output(&with_cfg)
+ .or_else(|_| {
+ has_cfg_and_sysroot = false;
+ build_config.rustc.cached_output(&process)
+ })
+ .chain_err(|| "failed to run `rustc` to learn about target-specific information")?;
+
+ let mut lines = output.lines();
+ let mut map = HashMap::new();
+ for crate_type in KNOWN_CRATE_TYPES {
+ let out = parse_crate_type(crate_type, &error, &mut lines)?;
+ map.insert(crate_type.to_string(), out);
+ }
+
+ let mut sysroot_libdir = None;
+ if has_cfg_and_sysroot {
+ let line = match lines.next() {
+ Some(line) => line,
+ None => bail!(
+ "output of --print=sysroot missing when learning about \
+ target-specific information from rustc"
+ ),
+ };
+ let mut rustlib = PathBuf::from(line);
+ if kind == Kind::Host {
+ if cfg!(windows) {
+ rustlib.push("bin");
+ } else {
+ rustlib.push("lib");
+ }
+ sysroot_libdir = Some(rustlib);
+ } else {
+ rustlib.push("lib");
+ rustlib.push("rustlib");
+ rustlib.push(build_config.target_triple());
+ rustlib.push("lib");
+ sysroot_libdir = Some(rustlib);
+ }
+ }
+
+ let cfg = if has_cfg_and_sysroot {
+ Some(lines.map(Cfg::from_str).collect::<CargoResult<_>>()?)
+ } else {
+ None
+ };
+
+ Ok(TargetInfo {
+ crate_type_process: Some(crate_type_process),
+ crate_types: RefCell::new(map),
+ cfg,
+ sysroot_libdir,
+ })
+ }
+
+ pub fn cfg(&self) -> Option<&[Cfg]> {
+ self.cfg.as_ref().map(|v| v.as_ref())
+ }
+
+ pub fn file_types(
+ &self,
+ crate_type: &str,
+ flavor: FileFlavor,
+ kind: &TargetKind,
+ target_triple: &str,
+ ) -> CargoResult<Option<Vec<FileType>>> {
+ let mut crate_types = self.crate_types.borrow_mut();
+ let entry = crate_types.entry(crate_type.to_string());
+ let crate_type_info = match entry {
+ Entry::Occupied(o) => &*o.into_mut(),
+ Entry::Vacant(v) => {
+ let value = self.discover_crate_type(v.key())?;
+ &*v.insert(value)
+ }
+ };
+ let (prefix, suffix) = match *crate_type_info {
+ Some((ref prefix, ref suffix)) => (prefix, suffix),
+ None => return Ok(None),
+ };
+ let mut ret = vec![
+ FileType {
+ suffix: suffix.clone(),
+ prefix: prefix.clone(),
+ flavor,
+ should_replace_hyphens: false,
+ },
+ ];
+
+ // rust-lang/cargo#4500
+ if target_triple.ends_with("pc-windows-msvc") && crate_type.ends_with("dylib")
+ && suffix == ".dll"
+ {
+ ret.push(FileType {
+ suffix: ".dll.lib".to_string(),
+ prefix: prefix.clone(),
+ flavor: FileFlavor::Normal,
+ should_replace_hyphens: false,
+ })
+ }
+
+ // rust-lang/cargo#4535
+ if target_triple.starts_with("wasm32-") && crate_type == "bin" && suffix == ".js" {
+ ret.push(FileType {
+ suffix: ".wasm".to_string(),
+ prefix: prefix.clone(),
+ flavor: FileFlavor::Normal,
+ should_replace_hyphens: true,
+ })
+ }
+
+ // rust-lang/cargo#4490, rust-lang/cargo#4960
+ // - only uplift debuginfo for binaries.
+ // tests are run directly from target/debug/deps/
+ // and examples are inside target/debug/examples/ which already have symbols next to them
+ // so no need to do anything.
+ if *kind == TargetKind::Bin {
+ if target_triple.contains("-apple-") {
+ ret.push(FileType {
+ suffix: ".dSYM".to_string(),
+ prefix: prefix.clone(),
+ flavor: FileFlavor::DebugInfo,
+ should_replace_hyphens: false,
+ })
+ } else if target_triple.ends_with("-msvc") {
+ ret.push(FileType {
+ suffix: ".pdb".to_string(),
+ prefix: prefix.clone(),
+ flavor: FileFlavor::DebugInfo,
+ should_replace_hyphens: false,
+ })
+ }
+ }
+
+ Ok(Some(ret))
+ }
+
+ fn discover_crate_type(&self, crate_type: &str) -> CargoResult<Option<(String, String)>> {
+ let mut process = self.crate_type_process.clone().unwrap();
+
+ process.arg("--crate-type").arg(crate_type);
+
+ let output = process.exec_with_output().chain_err(|| {
+ format!(
+ "failed to run `rustc` to learn about \
+ crate-type {} information",
+ crate_type
+ )
+ })?;
+
+ let error = str::from_utf8(&output.stderr).unwrap();
+ let output = str::from_utf8(&output.stdout).unwrap();
+ Ok(parse_crate_type(crate_type, error, &mut output.lines())?)
+ }
+}
+
+/// Takes rustc output (using specialized command line args), and calculates the file prefix and
+/// suffix for the given crate type, or returns None if the type is not supported. (e.g. for a
+/// rust library like libcargo.rlib, prefix = "lib", suffix = "rlib").
+///
+/// The caller needs to ensure that the lines object is at the correct line for the given crate
+/// type: this is not checked.
+// This function can not handle more than 1 file per type (with wasm32-unknown-emscripten, there
+// are 2 files for bin (.wasm and .js))
+fn parse_crate_type(
+ crate_type: &str,
+ error: &str,
+ lines: &mut str::Lines,
+) -> CargoResult<Option<(String, String)>> {
+ let not_supported = error.lines().any(|line| {
+ (line.contains("unsupported crate type") || line.contains("unknown crate type"))
+ && line.contains(crate_type)
+ });
+ if not_supported {
+ return Ok(None);
+ }
+ let line = match lines.next() {
+ Some(line) => line,
+ None => bail!(
+ "malformed output when learning about \
+ crate-type {} information",
+ crate_type
+ ),
+ };
+ let mut parts = line.trim().split("___");
+ let prefix = parts.next().unwrap();
+ let suffix = match parts.next() {
+ Some(part) => part,
+ None => bail!(
+ "output of --print=file-names has changed in \
+ the compiler, cannot parse"
+ ),
+ };
+
+ Ok(Some((prefix.to_string(), suffix.to_string())))
+}
#![allow(deprecated)]
use std::collections::{HashMap, HashSet};
-use std::env;
-use std::path::{Path, PathBuf};
-use std::str::{self, FromStr};
+use std::path::PathBuf;
use std::sync::Arc;
use jobserver::Client;
-use core::profiles::{Profile, Profiles};
-use core::{Dependency, Workspace};
-use core::{Package, PackageId, PackageSet, Resolve, Target};
+use core::{Package, PackageId, Target};
+use core::profiles::Profile;
use ops::CompileMode;
use util::errors::{CargoResult, CargoResultExt};
-use util::{internal, profile, Cfg, CfgExpr, Config};
+use util::{internal, profile, Config};
use super::custom_build::{self, BuildDeps, BuildScripts, BuildState};
use super::fingerprint::Fingerprint;
use super::job_queue::JobQueue;
use super::layout::Layout;
use super::links::Links;
-use super::TargetConfig;
-use super::{BuildConfig, Compilation, Executor, Kind};
+use super::{BuildContext, Compilation, Executor, FileFlavor, Kind};
mod unit_dependencies;
use self::unit_dependencies::build_unit_dependencies;
pub use self::compilation_files::Metadata;
use self::compilation_files::{CompilationFiles, OutputFile};
-mod target_info;
-pub use self::target_info::{FileFlavor, TargetInfo};
-
/// All information needed to define a Unit.
///
/// A unit is an object that has enough information so that cargo knows how to build it.
pub mode: CompileMode,
}
-/// The build context, containing all information about a build task
-pub struct BuildContext<'a, 'cfg: 'a> {
- /// The workspace the build is for
- pub ws: &'a Workspace<'cfg>,
- /// The cargo configuration
- pub config: &'cfg Config,
- /// The dependency graph for our build
- pub resolve: &'a Resolve,
- pub profiles: &'a Profiles,
- pub build_config: &'a BuildConfig,
- /// This is a workaround to carry the extra compiler args for either
- /// `rustc` or `rustdoc` given on the command-line for the commands `cargo
- /// rustc` and `cargo rustdoc`. These commands only support one target,
- /// but we don't want the args passed to any dependencies, so we include
- /// the `Unit` corresponding to the top-level target.
- extra_compiler_args: Option<(Unit<'a>, Vec<String>)>,
- pub packages: &'a PackageSet<'cfg>,
-
- target_info: TargetInfo,
- host_info: TargetInfo,
- incremental_env: Option<bool>,
-}
-
-impl<'a, 'cfg> BuildContext<'a, 'cfg> {
- pub fn new(
- ws: &'a Workspace<'cfg>,
- resolve: &'a Resolve,
- packages: &'a PackageSet<'cfg>,
- config: &'cfg Config,
- build_config: &'a BuildConfig,
- profiles: &'a Profiles,
- extra_compiler_args: Option<(Unit<'a>, Vec<String>)>,
- ) -> CargoResult<BuildContext<'a, 'cfg>> {
- let incremental_env = match env::var("CARGO_INCREMENTAL") {
- Ok(v) => Some(v == "1"),
- Err(_) => None,
- };
-
- 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)?;
- (host_info, target_info)
- };
-
- Ok(BuildContext {
- ws,
- resolve,
- packages,
- config,
- target_info,
- host_info,
- build_config,
- profiles,
- incremental_env,
- extra_compiler_args,
- })
- }
-
- pub fn extern_crate_name(&self, unit: &Unit<'a>, dep: &Unit<'a>) -> CargoResult<String> {
- let deps = {
- let a = unit.pkg.package_id();
- let b = dep.pkg.package_id();
- if a == b {
- &[]
- } else {
- self.resolve.dependencies_listed(a, b)
- }
- };
-
- let crate_name = dep.target.crate_name();
- let mut names = deps.iter()
- .map(|d| d.rename().unwrap_or(&crate_name));
- let name = names.next().unwrap_or(&crate_name);
- for n in names {
- if n == name {
- continue
- }
- bail!("multiple dependencies listed for the same crate must \
- all have the same name, but the dependency on `{}` \
- is listed as having different names", dep.pkg.package_id());
- }
- Ok(name.to_string())
- }
-
- /// Whether a dependency should be compiled for the host or target platform,
- /// specified by `Kind`.
- fn dep_platform_activated(&self, dep: &Dependency, kind: Kind) -> bool {
- // If this dependency is only available for certain platforms,
- // make sure we're only enabling it for that platform.
- let platform = match dep.platform() {
- Some(p) => p,
- 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),
- };
- platform.matches(name, info.cfg())
- }
-
- /// Gets a package for the given package id.
- pub fn get_package(&self, id: &PackageId) -> CargoResult<&'a Package> {
- self.packages.get(id)
- }
-
- /// Get the user-specified linker for a particular host or target
- pub fn linker(&self, kind: Kind) -> Option<&Path> {
- self.target_config(kind).linker.as_ref().map(|s| s.as_ref())
- }
-
- /// Get the user-specified `ar` program for a particular host or target
- pub fn ar(&self, kind: Kind) -> Option<&Path> {
- self.target_config(kind).ar.as_ref().map(|s| s.as_ref())
- }
-
- /// Get the list of cfg printed out from the compiler for the specified kind
- pub fn cfg(&self, kind: Kind) -> &[Cfg] {
- let info = match kind {
- Kind::Host => &self.host_info,
- Kind::Target => &self.target_info,
- };
- info.cfg().unwrap_or(&[])
- }
-
- /// 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,
- }
- }
-
- /// Number of jobs specified for this build
- pub fn jobs(&self) -> u32 {
- self.build_config.jobs
- }
-
- pub fn rustflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
- env_args(
- self.config,
- &self.build_config,
- self.info(&unit.kind).cfg(),
- unit.kind,
- "RUSTFLAGS",
- )
- }
-
- pub fn rustdocflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
- env_args(
- self.config,
- &self.build_config,
- self.info(&unit.kind).cfg(),
- unit.kind,
- "RUSTDOCFLAGS",
- )
- }
-
- pub fn show_warnings(&self, pkg: &PackageId) -> bool {
- pkg.source_id().is_path() || self.config.extra_verbose()
- }
-
- fn info(&self, kind: &Kind) -> &TargetInfo {
- match *kind {
- Kind::Host => &self.host_info,
- Kind::Target => &self.target_info,
- }
- }
-
- pub fn extra_args_for(&self, unit: &Unit<'a>) -> Option<&Vec<String>> {
- if let Some((ref args_unit, ref args)) = self.extra_compiler_args {
- if args_unit == unit {
- return Some(args);
- }
- }
- None
- }
-}
-
pub struct Context<'a, 'cfg: 'a> {
pub bcx: &'a BuildContext<'a, 'cfg>,
pub compilation: Compilation<'cfg>,
Ok(vec!["-C".to_string(), format!("incremental={}", dir)])
}
}
-
-/// Acquire extra flags to pass to the compiler from various locations.
-///
-/// The locations are:
-///
-/// - the `RUSTFLAGS` environment variable
-///
-/// then if this was not found
-///
-/// - `target.*.rustflags` from the manifest (Cargo.toml)
-/// - `target.cfg(..).rustflags` from the manifest
-///
-/// then if neither of these were found
-///
-/// - `build.rustflags` from the manifest
-///
-/// Note that if a `target` is specified, no args will be passed to host code (plugins, build
-/// scripts, ...), even if it is the same as the target.
-fn env_args(
- config: &Config,
- build_config: &BuildConfig,
- target_cfg: Option<&[Cfg]>,
- kind: Kind,
- name: &str,
-) -> CargoResult<Vec<String>> {
- // We *want* to apply RUSTFLAGS only to builds for the
- // requested target architecture, and not to things like build
- // scripts and plugins, which may be for an entirely different
- // architecture. Cargo's present architecture makes it quite
- // hard to only apply flags to things that are not build
- // scripts and plugins though, so we do something more hacky
- // instead to avoid applying the same RUSTFLAGS to multiple targets
- // arches:
- //
- // 1) If --target is not specified we just apply RUSTFLAGS to
- // all builds; they are all going to have the same target.
- //
- // 2) If --target *is* specified then we only apply RUSTFLAGS
- // to compilation units with the Target kind, which indicates
- // it was chosen by the --target flag.
- //
- // 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 is_target_kind = kind == Kind::Target;
-
- if compiling_with_target && !is_target_kind {
- // This is probably a build script or plugin and we're
- // compiling with --target. In this scenario there are
- // no rustflags we can apply.
- return Ok(Vec::new());
- }
-
- // First try RUSTFLAGS from the environment
- if let Ok(a) = env::var(name) {
- let args = a.split(' ')
- .map(str::trim)
- .filter(|s| !s.is_empty())
- .map(str::to_string);
- return Ok(args.collect());
- }
-
- let mut rustflags = Vec::new();
-
- let name = name.chars()
- .flat_map(|c| c.to_lowercase())
- .collect::<String>();
- // Then the target.*.rustflags value...
- let target = build_config
- .requested_target
- .as_ref()
- .map(|s| s.as_str())
- .unwrap_or(build_config.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();
- rustflags.extend(args);
- }
- // ...including target.'cfg(...)'.rustflags
- if let Some(target_cfg) = target_cfg {
- if let Some(table) = config.get_table("target")? {
- let cfgs = table.val.keys().filter_map(|t| {
- if t.starts_with("cfg(") && t.ends_with(')') {
- let cfg = &t[4..t.len() - 1];
- CfgExpr::from_str(cfg).ok().and_then(|c| {
- if c.matches(target_cfg) {
- Some(t)
- } else {
- None
- }
- })
- } else {
- None
- }
- });
-
- // Note that we may have multiple matching `[target]` sections and
- // because we're passing flags to the compiler this can affect
- // cargo's caching and whether it rebuilds. Ensure a deterministic
- // ordering through sorting for now. We may perhaps one day wish to
- // ensure a deterministic ordering via the order keys were defined
- // in files perhaps.
- let mut cfgs = cfgs.collect::<Vec<_>>();
- cfgs.sort();
-
- for n in cfgs {
- let key = format!("target.{}.{}", n, name);
- if let Some(args) = config.get_list_or_split_string(&key)? {
- let args = args.val.into_iter();
- rustflags.extend(args);
- }
- }
- }
- }
-
- if !rustflags.is_empty() {
- return Ok(rustflags);
- }
-
- // Then the build.rustflags value
- let key = format!("build.{}", name);
- if let Some(args) = config.get_list_or_split_string(&key)? {
- let args = args.val.into_iter();
- return Ok(args.collect());
- }
-
- Ok(Vec::new())
-}
+++ /dev/null
-use std::cell::RefCell;
-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 core::TargetKind;
-use super::Kind;
-
-#[derive(Clone)]
-pub struct TargetInfo {
- crate_type_process: Option<ProcessBuilder>,
- crate_types: RefCell<HashMap<String, Option<(String, String)>>>,
- cfg: Option<Vec<Cfg>>,
- pub sysroot_libdir: Option<PathBuf>,
-}
-
-/// Type of each file generated by a Unit.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum FileFlavor {
- /// Not a special file type.
- Normal,
- /// It is something you can link against (e.g. a library)
- Linkable,
- /// It is a piece of external debug information (e.g. *.dSYM and *.pdb)
- DebugInfo,
-}
-
-pub struct FileType {
- pub flavor: FileFlavor,
- suffix: String,
- prefix: String,
- // wasm bin target will generate two files in deps such as
- // "web-stuff.js" and "web_stuff.wasm". Note the different usages of
- // "-" and "_". should_replace_hyphens is a flag to indicate that
- // we need to convert the stem "web-stuff" to "web_stuff", so we
- // won't miss "web_stuff.wasm".
- should_replace_hyphens: bool,
-}
-
-impl FileType {
- pub fn filename(&self, stem: &str) -> String {
- let stem = if self.should_replace_hyphens {
- stem.replace("-", "_")
- } else {
- stem.to_string()
- };
- format!("{}{}{}", self.prefix, stem, self.suffix)
- }
-}
-
-impl TargetInfo {
- pub fn new(config: &Config, build_config: &BuildConfig, kind: Kind) -> CargoResult<TargetInfo> {
- let rustflags = env_args(config, build_config, None, kind, "RUSTFLAGS")?;
- let mut process = build_config.rustc.process();
- process
- .arg("-")
- .arg("--crate-name")
- .arg("___")
- .arg("--print=file-names")
- .args(&rustflags)
- .env_remove("RUST_LOG");
-
- if kind == Kind::Target {
- process.arg("--target").arg(&build_config.target_triple());
- }
-
- let crate_type_process = process.clone();
- const KNOWN_CRATE_TYPES: &[&str] =
- &["bin", "rlib", "dylib", "cdylib", "staticlib", "proc-macro"];
- for crate_type in KNOWN_CRATE_TYPES.iter() {
- process.arg("--crate-type").arg(crate_type);
- }
-
- let mut with_cfg = process.clone();
- with_cfg.arg("--print=sysroot");
- with_cfg.arg("--print=cfg");
-
- let mut has_cfg_and_sysroot = true;
- let (output, error) = build_config
- .rustc
- .cached_output(&with_cfg)
- .or_else(|_| {
- has_cfg_and_sysroot = false;
- build_config.rustc.cached_output(&process)
- })
- .chain_err(|| "failed to run `rustc` to learn about target-specific information")?;
-
- let mut lines = output.lines();
- let mut map = HashMap::new();
- for crate_type in KNOWN_CRATE_TYPES {
- let out = parse_crate_type(crate_type, &error, &mut lines)?;
- map.insert(crate_type.to_string(), out);
- }
-
- let mut sysroot_libdir = None;
- if has_cfg_and_sysroot {
- let line = match lines.next() {
- Some(line) => line,
- None => bail!(
- "output of --print=sysroot missing when learning about \
- target-specific information from rustc"
- ),
- };
- let mut rustlib = PathBuf::from(line);
- if kind == Kind::Host {
- if cfg!(windows) {
- rustlib.push("bin");
- } else {
- rustlib.push("lib");
- }
- sysroot_libdir = Some(rustlib);
- } else {
- rustlib.push("lib");
- rustlib.push("rustlib");
- rustlib.push(build_config.target_triple());
- rustlib.push("lib");
- sysroot_libdir = Some(rustlib);
- }
- }
-
- let cfg = if has_cfg_and_sysroot {
- Some(lines.map(Cfg::from_str).collect::<CargoResult<_>>()?)
- } else {
- None
- };
-
- Ok(TargetInfo {
- crate_type_process: Some(crate_type_process),
- crate_types: RefCell::new(map),
- cfg,
- sysroot_libdir,
- })
- }
-
- pub fn cfg(&self) -> Option<&[Cfg]> {
- self.cfg.as_ref().map(|v| v.as_ref())
- }
-
- pub fn file_types(
- &self,
- crate_type: &str,
- flavor: FileFlavor,
- kind: &TargetKind,
- target_triple: &str,
- ) -> CargoResult<Option<Vec<FileType>>> {
- let mut crate_types = self.crate_types.borrow_mut();
- let entry = crate_types.entry(crate_type.to_string());
- let crate_type_info = match entry {
- Entry::Occupied(o) => &*o.into_mut(),
- Entry::Vacant(v) => {
- let value = self.discover_crate_type(v.key())?;
- &*v.insert(value)
- }
- };
- let (prefix, suffix) = match *crate_type_info {
- Some((ref prefix, ref suffix)) => (prefix, suffix),
- None => return Ok(None),
- };
- let mut ret = vec![
- FileType {
- suffix: suffix.clone(),
- prefix: prefix.clone(),
- flavor,
- should_replace_hyphens: false,
- },
- ];
-
- // rust-lang/cargo#4500
- if target_triple.ends_with("pc-windows-msvc") && crate_type.ends_with("dylib")
- && suffix == ".dll"
- {
- ret.push(FileType {
- suffix: ".dll.lib".to_string(),
- prefix: prefix.clone(),
- flavor: FileFlavor::Normal,
- should_replace_hyphens: false,
- })
- }
-
- // rust-lang/cargo#4535
- if target_triple.starts_with("wasm32-") && crate_type == "bin" && suffix == ".js" {
- ret.push(FileType {
- suffix: ".wasm".to_string(),
- prefix: prefix.clone(),
- flavor: FileFlavor::Normal,
- should_replace_hyphens: true,
- })
- }
-
- // rust-lang/cargo#4490, rust-lang/cargo#4960
- // - only uplift debuginfo for binaries.
- // tests are run directly from target/debug/deps/
- // and examples are inside target/debug/examples/ which already have symbols next to them
- // so no need to do anything.
- if *kind == TargetKind::Bin {
- if target_triple.contains("-apple-") {
- ret.push(FileType {
- suffix: ".dSYM".to_string(),
- prefix: prefix.clone(),
- flavor: FileFlavor::DebugInfo,
- should_replace_hyphens: false,
- })
- } else if target_triple.ends_with("-msvc") {
- ret.push(FileType {
- suffix: ".pdb".to_string(),
- prefix: prefix.clone(),
- flavor: FileFlavor::DebugInfo,
- should_replace_hyphens: false,
- })
- }
- }
-
- Ok(Some(ret))
- }
-
- fn discover_crate_type(&self, crate_type: &str) -> CargoResult<Option<(String, String)>> {
- let mut process = self.crate_type_process.clone().unwrap();
-
- process.arg("--crate-type").arg(crate_type);
-
- let output = process.exec_with_output().chain_err(|| {
- format!(
- "failed to run `rustc` to learn about \
- crate-type {} information",
- crate_type
- )
- })?;
-
- let error = str::from_utf8(&output.stderr).unwrap();
- let output = str::from_utf8(&output.stdout).unwrap();
- Ok(parse_crate_type(crate_type, error, &mut output.lines())?)
- }
-}
-
-/// Takes rustc output (using specialized command line args), and calculates the file prefix and
-/// suffix for the given crate type, or returns None if the type is not supported. (e.g. for a
-/// rust library like libcargo.rlib, prefix = "lib", suffix = "rlib").
-///
-/// The caller needs to ensure that the lines object is at the correct line for the given crate
-/// type: this is not checked.
-// This function can not handle more than 1 file per type (with wasm32-unknown-emscripten, there
-// are 2 files for bin (.wasm and .js))
-fn parse_crate_type(
- crate_type: &str,
- error: &str,
- lines: &mut str::Lines,
-) -> CargoResult<Option<(String, String)>> {
- let not_supported = error.lines().any(|line| {
- (line.contains("unsupported crate type") || line.contains("unknown crate type"))
- && line.contains(crate_type)
- });
- if not_supported {
- return Ok(None);
- }
- let line = match lines.next() {
- Some(line) => line,
- None => bail!(
- "malformed output when learning about \
- crate-type {} information",
- crate_type
- ),
- };
- let mut parts = line.trim().split("___");
- let prefix = parts.next().unwrap();
- let suffix = match parts.next() {
- Some(part) => part,
- None => bail!(
- "output of --print=file-names has changed in \
- the compiler, cannot parse"
- ),
- };
-
- Ok(Some((prefix.to_string(), suffix.to_string())))
-}
use util::paths;
use util::{internal, profile, Dirty, Fresh, Freshness};
-use super::context::{BuildContext, Context, FileFlavor, Unit};
+use super::{Context, BuildContext, FileFlavor, Unit};
use super::custom_build::BuildDeps;
use super::job::Work;
use self::output_depinfo::output_depinfo;
+pub use self::build_context::{BuildContext, FileFlavor, TargetInfo};
pub use self::compilation::Compilation;
-pub use self::context::{BuildContext, Context, FileFlavor, TargetInfo, Unit};
+pub use self::context::{Context, Unit};
pub use self::custom_build::{BuildMap, BuildOutput, BuildScripts};
pub use self::layout::is_bad_artifact_name;
+mod build_context;
mod compilation;
mod context;
mod custom_build;