]> git.proxmox.com Git - cargo.git/commitdiff
Extract new build_context module out of context module
authorDirkjan Ochtman <dirkjan@ochtman.nl>
Sat, 28 Apr 2018 15:33:11 +0000 (17:33 +0200)
committerDirkjan Ochtman <dirkjan@ochtman.nl>
Wed, 2 May 2018 08:03:33 +0000 (10:03 +0200)
src/cargo/core/compiler/build_context/mod.rs [new file with mode: 0644]
src/cargo/core/compiler/build_context/target_info.rs [new file with mode: 0644]
src/cargo/core/compiler/context/mod.rs
src/cargo/core/compiler/context/target_info.rs [deleted file]
src/cargo/core/compiler/fingerprint.rs
src/cargo/core/compiler/mod.rs

diff --git a/src/cargo/core/compiler/build_context/mod.rs b/src/cargo/core/compiler/build_context/mod.rs
new file mode 100644 (file)
index 0000000..30708eb
--- /dev/null
@@ -0,0 +1,323 @@
+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())
+}
diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs
new file mode 100644 (file)
index 0000000..ff691ff
--- /dev/null
@@ -0,0 +1,276 @@
+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())))
+}
index 0eda1caf82dd86b74a6b94eba03b66d85ee71b19..b9846f5c13e73b915d72bf153e31b73bd45bdd07 100644 (file)
@@ -1,26 +1,22 @@
 #![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;
@@ -29,9 +25,6 @@ mod compilation_files;
 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.
@@ -70,186 +63,6 @@ pub struct Unit<'a> {
     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>,
@@ -617,132 +430,3 @@ impl<'a, 'cfg> Context<'a, '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())
-}
diff --git a/src/cargo/core/compiler/context/target_info.rs b/src/cargo/core/compiler/context/target_info.rs
deleted file mode 100644 (file)
index ff691ff..0000000
+++ /dev/null
@@ -1,276 +0,0 @@
-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())))
-}
index 6eba514052c051a196495bdc5db538c7364b0118..78b18af265f6fe4e126b9468732932f1eb88ed5f 100644 (file)
@@ -15,7 +15,7 @@ use util::errors::{CargoResult, CargoResultExt};
 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;
 
index 2becebccd6d2b684bdfeff854f2ced8a2914f41f..845d3d2c4fe93b9dbc5e04e3f209cf5fcd8e6534 100644 (file)
@@ -23,11 +23,13 @@ use self::job_queue::JobQueue;
 
 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;