Refactor context to extract dependencies calculation to a separate mod
authorAleksey Kladov <aleksey.kladov@gmail.com>
Fri, 23 Mar 2018 11:51:32 +0000 (14:51 +0300)
committerAleksey Kladov <aleksey.kladov@gmail.com>
Sat, 24 Mar 2018 21:51:32 +0000 (00:51 +0300)
src/cargo/ops/cargo_clean.rs
src/cargo/ops/cargo_rustc/context.rs [deleted file]
src/cargo/ops/cargo_rustc/context/mod.rs [new file with mode: 0644]
src/cargo/ops/cargo_rustc/context/unit_dependencies.rs [new file with mode: 0644]
src/cargo/ops/cargo_rustc/custom_build.rs
src/cargo/ops/cargo_rustc/fingerprint.rs
src/cargo/ops/cargo_rustc/job_queue.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/ops/cargo_rustc/output_depinfo.rs

index 89939e02d0fa789f56dfb1d302d2355191bac79c..7f7d96225daa5076c8b23fd4518cba321134bade 100644 (file)
@@ -100,6 +100,7 @@ pub fn clean(ws: &Workspace, opts: &CleanOptions) -> CargoResult<()> {
     }
 
     cx.probe_target_info()?;
+    cx.build_unit_dependencies(&units)?;
 
     for unit in units.iter() {
         rm_rf(&cx.fingerprint_dir(unit), config)?;
diff --git a/src/cargo/ops/cargo_rustc/context.rs b/src/cargo/ops/cargo_rustc/context.rs
deleted file mode 100644 (file)
index cdcfe41..0000000
+++ /dev/null
@@ -1,1378 +0,0 @@
-#![allow(deprecated)]
-
-use std::collections::{HashMap, HashSet};
-use std::collections::hash_map::Entry;
-use std::env;
-use std::fmt;
-use std::hash::{Hash, Hasher, SipHasher};
-use std::path::{Path, PathBuf};
-use std::str::{self, FromStr};
-use std::sync::Arc;
-use std::cell::RefCell;
-
-use jobserver::Client;
-
-use core::{Package, PackageId, PackageSet, Profile, Resolve, Target};
-use core::{Dependency, Profiles, TargetKind, Workspace};
-use core::dependency::Kind as DepKind;
-use util::{self, internal, profile, Cfg, CfgExpr, Config, ProcessBuilder};
-use util::errors::{CargoResult, CargoResultExt};
-
-use super::TargetConfig;
-use super::custom_build::{BuildDeps, BuildScripts, BuildState};
-use super::fingerprint::Fingerprint;
-use super::layout::Layout;
-use super::links::Links;
-use super::{BuildConfig, Compilation, Kind};
-
-/// All information needed to define a Unit.
-///
-/// A unit is an object that has enough information so that cargo knows how to build it.
-/// For example, if your project has dependencies, then every dependency will be built as a library
-/// unit. If your project is a library, then it will be built as a library unit as well, or if it
-/// is a binary with `main.rs`, then a binary will be output. There are also separate unit types
-/// for `test`ing and `check`ing, amongst others.
-///
-/// The unit also holds information about all possible metadata about the package in `pkg`.
-///
-/// A unit needs to know extra information in addition to the type and root source file. For
-/// example, it needs to know the target architecture (OS, chip arch etc.) and it needs to know
-/// whether you want a debug or release build. There is enough information in this struct to figure
-/// all that out.
-#[derive(Clone, Copy, Eq, PartialEq, Hash)]
-pub struct Unit<'a> {
-    /// Information about available targets, which files to include/exclude, etc. Basically stuff in
-    /// `Cargo.toml`.
-    pub pkg: &'a Package,
-    /// Information about the specific target to build, out of the possible targets in `pkg`. Not
-    /// to be confused with *target-triple* (or *target architecture* ...), the target arch for a
-    /// build.
-    pub target: &'a Target,
-    /// The profile contains information about *how* the build should be run, including debug
-    /// level, extra args to pass to rustc, etc.
-    pub profile: &'a Profile,
-    /// Whether this compilation unit is for the host or target architecture.
-    ///
-    /// For example, when
-    /// cross compiling and using a custom build script, the build script needs to be compiled for
-    /// the host architecture so the host rustc can use it (when compiling to the target
-    /// architecture).
-    pub kind: Kind,
-}
-
-/// Type of each file generated by a Unit.
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum TargetFileType {
-    /// 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,
-}
-
-/// The build context, containing all information about a build task
-pub struct Context<'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,
-    /// Information on the compilation output
-    pub compilation: Compilation<'cfg>,
-    pub packages: &'a PackageSet<'cfg>,
-    pub build_state: Arc<BuildState>,
-    pub build_script_overridden: HashSet<(PackageId, Kind)>,
-    pub build_explicit_deps: HashMap<Unit<'a>, BuildDeps>,
-    pub fingerprints: HashMap<Unit<'a>, Arc<Fingerprint>>,
-    pub compiled: HashSet<Unit<'a>>,
-    pub build_config: BuildConfig,
-    pub build_scripts: HashMap<Unit<'a>, Arc<BuildScripts>>,
-    pub links: Links<'a>,
-    pub used_in_plugin: HashSet<Unit<'a>>,
-    pub jobserver: Client,
-
-    /// The target directory layout for the host (and target if it is the same as host)
-    host: Layout,
-    /// The target directory layout for the target (if different from then host)
-    target: Option<Layout>,
-    target_info: TargetInfo,
-    host_info: TargetInfo,
-    profiles: &'a Profiles,
-    incremental_env: Option<bool>,
-
-    /// For each Unit, a list all files produced as a triple of
-    ///
-    ///  - File name that will be produced by the build process (in `deps`)
-    ///  - If it should be linked into `target`, and what it should be called (e.g. without
-    ///    metadata).
-    ///  - Type of the file (library / debug symbol / else)
-    target_filenames: HashMap<Unit<'a>, Arc<Vec<(PathBuf, Option<PathBuf>, TargetFileType)>>>,
-    target_metadatas: HashMap<Unit<'a>, Option<Metadata>>,
-}
-
-#[derive(Clone, Default)]
-struct TargetInfo {
-    crate_type_process: Option<ProcessBuilder>,
-    crate_types: RefCell<HashMap<String, Option<(String, String)>>>,
-    cfg: Option<Vec<Cfg>>,
-}
-
-impl TargetInfo {
-    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())?)
-    }
-}
-
-#[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
-pub struct Metadata(u64);
-
-impl<'a, 'cfg> Context<'a, 'cfg> {
-    pub fn new(
-        ws: &'a Workspace<'cfg>,
-        resolve: &'a Resolve,
-        packages: &'a PackageSet<'cfg>,
-        config: &'cfg Config,
-        build_config: BuildConfig,
-        profiles: &'a Profiles,
-    ) -> CargoResult<Context<'a, 'cfg>> {
-        let dest = if build_config.release {
-            "release"
-        } else {
-            "debug"
-        };
-        let host_layout = Layout::new(ws, None, dest)?;
-        let target_layout = match build_config.requested_target.as_ref() {
-            Some(target) => Some(Layout::new(ws, Some(target), dest)?),
-            None => None,
-        };
-
-        let incremental_env = match env::var("CARGO_INCREMENTAL") {
-            Ok(v) => Some(v == "1"),
-            Err(_) => None,
-        };
-
-        // Load up the jobserver that we'll use to manage our parallelism. This
-        // is the same as the GNU make implementation of a jobserver, and
-        // intentionally so! It's hoped that we can interact with GNU make and
-        // all share the same jobserver.
-        //
-        // Note that if we don't have a jobserver in our environment then we
-        // create our own, and we create it with `n-1` tokens because one token
-        // is ourself, a running process.
-        let jobserver = match config.jobserver_from_env() {
-            Some(c) => c.clone(),
-            None => Client::new(build_config.jobs as usize - 1)
-                .chain_err(|| "failed to create jobserver")?,
-        };
-
-        Ok(Context {
-            ws,
-            host: host_layout,
-            target: target_layout,
-            resolve,
-            packages,
-            config,
-            target_info: TargetInfo::default(),
-            host_info: TargetInfo::default(),
-            compilation: Compilation::new(config),
-            build_state: Arc::new(BuildState::new(&build_config)),
-            build_config,
-            fingerprints: HashMap::new(),
-            profiles,
-            compiled: HashSet::new(),
-            build_scripts: HashMap::new(),
-            build_explicit_deps: HashMap::new(),
-            links: Links::new(),
-            used_in_plugin: HashSet::new(),
-            incremental_env,
-            jobserver,
-            build_script_overridden: HashSet::new(),
-
-            // TODO: Pre-Calculate these with a topo-sort, rather than lazy-calculating
-            target_filenames: HashMap::new(),
-            target_metadatas: HashMap::new(),
-        })
-    }
-
-    /// Prepare this context, ensuring that all filesystem directories are in
-    /// place.
-    pub fn prepare(&mut self) -> CargoResult<()> {
-        let _p = profile::start("preparing layout");
-
-        self.host
-            .prepare()
-            .chain_err(|| internal("couldn't prepare build directories"))?;
-        if let Some(ref mut target) = self.target {
-            target
-                .prepare()
-                .chain_err(|| internal("couldn't prepare build directories"))?;
-        }
-
-        self.compilation.host_deps_output = self.host.deps().to_path_buf();
-
-        let layout = self.target.as_ref().unwrap_or(&self.host);
-        self.compilation.root_output = layout.dest().to_path_buf();
-        self.compilation.deps_output = layout.deps().to_path_buf();
-        Ok(())
-    }
-
-    /// Ensure that we've collected all target-specific information to compile
-    /// all the units mentioned in `units`.
-    pub fn probe_target_info(&mut self) -> CargoResult<()> {
-        debug!("probe_target_info");
-        self.probe_target_info_kind(Kind::Target)?;
-        if self.requested_target().is_none() {
-            self.host_info = self.target_info.clone();
-        } else {
-            self.probe_target_info_kind(Kind::Host)?;
-        }
-        Ok(())
-    }
-
-    fn probe_target_info_kind(&mut self, kind: Kind) -> CargoResult<()> {
-        let rustflags = env_args(
-            self.config,
-            &self.build_config,
-            self.info(&kind),
-            kind,
-            "RUSTFLAGS",
-        )?;
-        let mut process = self.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(&self.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 = with_cfg
-            .exec_with_output()
-            .or_else(|_| {
-                has_cfg_and_sysroot = false;
-                process.exec_with_output()
-            })
-            .chain_err(|| "failed to run `rustc` to learn about target-specific information")?;
-
-        let error = str::from_utf8(&output.stderr).unwrap();
-        let output = str::from_utf8(&output.stdout).unwrap();
-        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);
-        }
-
-        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");
-                }
-                self.compilation.host_dylib_path = Some(rustlib);
-            } else {
-                rustlib.push("lib");
-                rustlib.push("rustlib");
-                rustlib.push(self.target_triple());
-                rustlib.push("lib");
-                self.compilation.target_dylib_path = Some(rustlib);
-            }
-        }
-
-        let cfg = if has_cfg_and_sysroot {
-            Some(lines.map(Cfg::from_str).collect::<CargoResult<_>>()?)
-        } else {
-            None
-        };
-
-        let info = match kind {
-            Kind::Target => &mut self.target_info,
-            Kind::Host => &mut self.host_info,
-        };
-        info.crate_type_process = Some(crate_type_process);
-        info.crate_types = RefCell::new(map);
-        info.cfg = cfg;
-        Ok(())
-    }
-
-    /// Builds up the `used_in_plugin` internal to this context from the list of
-    /// top-level units.
-    ///
-    /// This will recursively walk `units` and all of their dependencies to
-    /// determine which crate are going to be used in plugins or not.
-    pub fn build_used_in_plugin_map(&mut self, units: &[Unit<'a>]) -> CargoResult<()> {
-        let mut visited = HashSet::new();
-        for unit in units {
-            self.walk_used_in_plugin_map(unit, unit.target.for_host(), &mut visited)?;
-        }
-        Ok(())
-    }
-
-    fn walk_used_in_plugin_map(
-        &mut self,
-        unit: &Unit<'a>,
-        is_plugin: bool,
-        visited: &mut HashSet<(Unit<'a>, bool)>,
-    ) -> CargoResult<()> {
-        if !visited.insert((*unit, is_plugin)) {
-            return Ok(());
-        }
-        if is_plugin {
-            self.used_in_plugin.insert(*unit);
-        }
-        for unit in self.dep_targets(unit)? {
-            self.walk_used_in_plugin_map(&unit, is_plugin || unit.target.for_host(), visited)?;
-        }
-        Ok(())
-    }
-
-    /// Returns the appropriate directory layout for either a plugin or not.
-    fn layout(&self, kind: Kind) -> &Layout {
-        match kind {
-            Kind::Host => &self.host,
-            Kind::Target => self.target.as_ref().unwrap_or(&self.host),
-        }
-    }
-
-    /// Returns the directories where Rust crate dependencies are found for the
-    /// specified unit.
-    pub fn deps_dir(&self, unit: &Unit) -> &Path {
-        self.layout(unit.kind).deps()
-    }
-
-    /// Returns the directory for the specified unit where fingerprint
-    /// information is stored.
-    pub fn fingerprint_dir(&mut self, unit: &Unit<'a>) -> PathBuf {
-        let dir = self.pkg_dir(unit);
-        self.layout(unit.kind).fingerprint().join(dir)
-    }
-
-    /// Returns the appropriate directory layout for either a plugin or not.
-    pub fn build_script_dir(&mut self, unit: &Unit<'a>) -> PathBuf {
-        assert!(unit.target.is_custom_build());
-        assert!(!unit.profile.run_custom_build);
-        let dir = self.pkg_dir(unit);
-        self.layout(Kind::Host).build().join(dir)
-    }
-
-    /// Returns the appropriate directory layout for either a plugin or not.
-    pub fn build_script_out_dir(&mut self, unit: &Unit<'a>) -> PathBuf {
-        assert!(unit.target.is_custom_build());
-        assert!(unit.profile.run_custom_build);
-        let dir = self.pkg_dir(unit);
-        self.layout(unit.kind).build().join(dir).join("out")
-    }
-
-    pub fn host_deps(&self) -> &Path {
-        self.host.deps()
-    }
-
-    /// Return the root of the build output tree
-    pub fn target_root(&self) -> &Path {
-        self.host.dest()
-    }
-
-    /// Returns the appropriate output directory for the specified package and
-    /// target.
-    pub fn out_dir(&mut self, unit: &Unit<'a>) -> PathBuf {
-        if unit.profile.doc {
-            self.layout(unit.kind).root().parent().unwrap().join("doc")
-        } else if unit.target.is_custom_build() {
-            self.build_script_dir(unit)
-        } else if unit.target.is_example() {
-            self.layout(unit.kind).examples().to_path_buf()
-        } else {
-            self.deps_dir(unit).to_path_buf()
-        }
-    }
-
-    fn pkg_dir(&mut self, unit: &Unit<'a>) -> String {
-        let name = unit.pkg.package_id().name();
-        match self.target_metadata(unit) {
-            Some(meta) => format!("{}-{}", name, meta),
-            None => format!("{}-{}", name, self.target_short_hash(unit)),
-        }
-    }
-
-    /// Return the host triple for this context
-    pub fn host_triple(&self) -> &str {
-        &self.build_config.host_triple
-    }
-
-    /// Return the target triple which this context is targeting.
-    pub fn target_triple(&self) -> &str {
-        self.requested_target()
-            .unwrap_or_else(|| self.host_triple())
-    }
-
-    /// Requested (not actual) target for the build
-    pub fn requested_target(&self) -> Option<&str> {
-        self.build_config.requested_target.as_ref().map(|s| &s[..])
-    }
-
-    /// Get the short hash based only on the PackageId
-    /// Used for the metadata when target_metadata returns None
-    pub fn target_short_hash(&self, unit: &Unit) -> String {
-        let hashable = unit.pkg.package_id().stable_hash(self.ws.root());
-        util::short_hash(&hashable)
-    }
-
-    /// Get the metadata for a target in a specific profile
-    /// We build to the path: "{filename}-{target_metadata}"
-    /// We use a linking step to link/copy to a predictable filename
-    /// like `target/debug/libfoo.{a,so,rlib}` and such.
-    pub fn target_metadata(&mut self, unit: &Unit<'a>) -> Option<Metadata> {
-        if let Some(cache) = self.target_metadatas.get(unit) {
-            return cache.clone();
-        }
-
-        let metadata = self.calc_target_metadata(unit);
-        self.target_metadatas.insert(*unit, metadata.clone());
-        metadata
-    }
-
-    fn calc_target_metadata(&mut self, unit: &Unit<'a>) -> Option<Metadata> {
-        // No metadata for dylibs because of a couple issues
-        // - OSX encodes the dylib name in the executable
-        // - Windows rustc multiple files of which we can't easily link all of them
-        //
-        // No metadata for bin because of an issue
-        // - wasm32 rustc/emcc encodes the .wasm name in the .js (rust-lang/cargo#4535)
-        //
-        // Two exceptions
-        // 1) Upstream dependencies (we aren't exporting + need to resolve name conflict)
-        // 2) __CARGO_DEFAULT_LIB_METADATA env var
-        //
-        // Note, though, that the compiler's build system at least wants
-        // path dependencies (eg libstd) to have hashes in filenames. To account for
-        // that we have an extra hack here which reads the
-        // `__CARGO_DEFAULT_LIB_METADATA` environment variable and creates a
-        // hash in the filename if that's present.
-        //
-        // This environment variable should not be relied on! It's
-        // just here for rustbuild. We need a more principled method
-        // doing this eventually.
-        let __cargo_default_lib_metadata = env::var("__CARGO_DEFAULT_LIB_METADATA");
-        if !(unit.profile.test || unit.profile.check)
-            && (unit.target.is_dylib() || unit.target.is_cdylib()
-                || (unit.target.is_bin() && self.target_triple().starts_with("wasm32-")))
-            && unit.pkg.package_id().source_id().is_path()
-            && !__cargo_default_lib_metadata.is_ok()
-        {
-            return None;
-        }
-
-        let mut hasher = SipHasher::new_with_keys(0, 0);
-
-        // Unique metadata per (name, source, version) triple. This'll allow us
-        // to pull crates from anywhere w/o worrying about conflicts
-        unit.pkg
-            .package_id()
-            .stable_hash(self.ws.root())
-            .hash(&mut hasher);
-
-        // Add package properties which map to environment variables
-        // exposed by Cargo
-        let manifest_metadata = unit.pkg.manifest().metadata();
-        manifest_metadata.authors.hash(&mut hasher);
-        manifest_metadata.description.hash(&mut hasher);
-        manifest_metadata.homepage.hash(&mut hasher);
-
-        // Also mix in enabled features to our metadata. This'll ensure that
-        // when changing feature sets each lib is separately cached.
-        self.resolve
-            .features_sorted(unit.pkg.package_id())
-            .hash(&mut hasher);
-
-        // Mix in the target-metadata of all the dependencies of this target
-        if let Ok(deps) = self.dep_targets(unit) {
-            let mut deps_metadata = deps.into_iter()
-                .map(|dep_unit| self.target_metadata(&dep_unit))
-                .collect::<Vec<_>>();
-            deps_metadata.sort();
-            deps_metadata.hash(&mut hasher);
-        }
-
-        // Throw in the profile we're compiling with. This helps caching
-        // panic=abort and panic=unwind artifacts, additionally with various
-        // settings like debuginfo and whatnot.
-        unit.profile.hash(&mut hasher);
-
-        // Artifacts compiled for the host should have a different metadata
-        // piece than those compiled for the target, so make sure we throw in
-        // the unit's `kind` as well
-        unit.kind.hash(&mut hasher);
-
-        // Finally throw in the target name/kind. This ensures that concurrent
-        // compiles of targets in the same crate don't collide.
-        unit.target.name().hash(&mut hasher);
-        unit.target.kind().hash(&mut hasher);
-
-        if let Ok(rustc) = self.config.rustc() {
-            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.
-        if let Ok(ref channel) = __cargo_default_lib_metadata {
-            channel.hash(&mut hasher);
-        }
-
-        Some(Metadata(hasher.finish()))
-    }
-
-    /// Returns the file stem for a given target/profile combo (with metadata)
-    pub fn file_stem(&mut self, unit: &Unit<'a>) -> String {
-        match self.target_metadata(unit) {
-            Some(ref metadata) => format!("{}-{}", unit.target.crate_name(), metadata),
-            None => self.bin_stem(unit),
-        }
-    }
-
-    /// Returns the bin stem for a given target (without metadata)
-    fn bin_stem(&self, unit: &Unit) -> String {
-        if unit.target.allows_underscores() {
-            unit.target.name().to_string()
-        } else {
-            unit.target.crate_name()
-        }
-    }
-
-    /// Returns a tuple with the directory and name of the hard link we expect
-    /// our target to be copied to. Eg, file_stem may be out_dir/deps/foo-abcdef
-    /// and link_stem would be out_dir/foo
-    /// This function returns it in two parts so the caller can add prefix/suffix
-    /// to filename separately
-    ///
-    /// Returns an Option because in some cases we don't want to link
-    /// (eg a dependent lib)
-    pub fn link_stem(&mut self, unit: &Unit<'a>) -> Option<(PathBuf, String)> {
-        let src_dir = self.out_dir(unit);
-        let bin_stem = self.bin_stem(unit);
-        let file_stem = self.file_stem(unit);
-
-        // We currently only lift files up from the `deps` directory. If
-        // it was compiled into something like `example/` or `doc/` then
-        // we don't want to link it up.
-        if src_dir.ends_with("deps") {
-            // Don't lift up library dependencies
-            if self.ws.members().find(|&p| p == unit.pkg).is_none() && !unit.target.is_bin() {
-                None
-            } else {
-                Some((
-                    src_dir.parent().unwrap().to_owned(),
-                    if unit.profile.test {
-                        file_stem
-                    } else {
-                        bin_stem
-                    },
-                ))
-            }
-        } else if bin_stem == file_stem {
-            None
-        } else if src_dir.ends_with("examples") || src_dir.parent().unwrap().ends_with("build") {
-            Some((src_dir, bin_stem))
-        } else {
-            None
-        }
-    }
-
-    /// Return the filenames that the given target for the given profile will
-    /// generate as a list of 3-tuples (filename, link_dst, linkable)
-    ///
-    ///  - filename: filename rustc compiles to. (Often has metadata suffix).
-    ///  - link_dst: Optional file to link/copy the result to (without metadata suffix)
-    ///  - linkable: Whether possible to link against file (eg it's a library)
-    pub fn target_filenames(
-        &mut self,
-        unit: &Unit<'a>,
-    ) -> CargoResult<Arc<Vec<(PathBuf, Option<PathBuf>, TargetFileType)>>> {
-        if let Some(cache) = self.target_filenames.get(unit) {
-            return Ok(Arc::clone(cache));
-        }
-
-        let result = self.calc_target_filenames(unit);
-        if let Ok(ref ret) = result {
-            self.target_filenames.insert(*unit, Arc::clone(ret));
-        }
-        result
-    }
-
-    fn calc_target_filenames(
-        &mut self,
-        unit: &Unit<'a>,
-    ) -> CargoResult<Arc<Vec<(PathBuf, Option<PathBuf>, TargetFileType)>>> {
-        let out_dir = self.out_dir(unit);
-        let stem = self.file_stem(unit);
-        let link_stem = self.link_stem(unit);
-        let info = if unit.target.for_host() {
-            &self.host_info
-        } else {
-            &self.target_info
-        };
-
-        let mut ret = Vec::new();
-        let mut unsupported = Vec::new();
-        {
-            if unit.profile.check {
-                let filename = out_dir.join(format!("lib{}.rmeta", stem));
-                let link_dst = link_stem
-                    .clone()
-                    .map(|(ld, ls)| ld.join(format!("lib{}.rmeta", ls)));
-                ret.push((filename, link_dst, TargetFileType::Linkable));
-            } else {
-                let mut add = |crate_type: &str, file_type: TargetFileType| -> CargoResult<()> {
-                    let crate_type = if crate_type == "lib" {
-                        "rlib"
-                    } else {
-                        crate_type
-                    };
-                    let mut crate_types = info.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 = info.discover_crate_type(v.key())?;
-                            &*v.insert(value)
-                        }
-                    };
-                    match *crate_type_info {
-                        Some((ref prefix, ref suffix)) => {
-                            let suffixes = add_target_specific_suffixes(
-                                self.target_triple(),
-                                crate_type,
-                                unit.target.kind(),
-                                suffix,
-                                file_type,
-                            );
-                            for (suffix, file_type, should_replace_hyphens) in suffixes {
-                                // 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".
-                                let conv = |s: String| {
-                                    if should_replace_hyphens {
-                                        s.replace("-", "_")
-                                    } else {
-                                        s
-                                    }
-                                };
-                                let filename = out_dir.join(format!(
-                                    "{}{}{}",
-                                    prefix,
-                                    conv(stem.clone()),
-                                    suffix
-                                ));
-                                let link_dst = link_stem.clone().map(|(ld, ls)| {
-                                    ld.join(format!("{}{}{}", prefix, conv(ls), suffix))
-                                });
-                                ret.push((filename, link_dst, file_type));
-                            }
-                            Ok(())
-                        }
-                        // not supported, don't worry about it
-                        None => {
-                            unsupported.push(crate_type.to_string());
-                            Ok(())
-                        }
-                    }
-                };
-                //info!("{:?}", unit);
-                match *unit.target.kind() {
-                    TargetKind::Bin
-                    | TargetKind::CustomBuild
-                    | TargetKind::ExampleBin
-                    | TargetKind::Bench
-                    | TargetKind::Test => {
-                        add("bin", TargetFileType::Normal)?;
-                    }
-                    TargetKind::Lib(..) | TargetKind::ExampleLib(..) if unit.profile.test => {
-                        add("bin", TargetFileType::Normal)?;
-                    }
-                    TargetKind::ExampleLib(ref kinds) | TargetKind::Lib(ref kinds) => {
-                        for kind in kinds {
-                            add(
-                                kind.crate_type(),
-                                if kind.linkable() {
-                                    TargetFileType::Linkable
-                                } else {
-                                    TargetFileType::Normal
-                                },
-                            )?;
-                        }
-                    }
-                }
-            }
-        }
-        if ret.is_empty() {
-            if !unsupported.is_empty() {
-                bail!(
-                    "cannot produce {} for `{}` as the target `{}` \
-                     does not support these crate types",
-                    unsupported.join(", "),
-                    unit.pkg,
-                    self.target_triple()
-                )
-            }
-            bail!(
-                "cannot compile `{}` as the target `{}` does not \
-                 support any of the output crate types",
-                unit.pkg,
-                self.target_triple()
-            );
-        }
-        info!("Target filenames: {:?}", ret);
-
-        Ok(Arc::new(ret))
-    }
-
-    /// For a package, return all targets which are registered as dependencies
-    /// for that package.
-    pub fn dep_targets(&self, unit: &Unit<'a>) -> CargoResult<Vec<Unit<'a>>> {
-        if unit.profile.run_custom_build {
-            return self.dep_run_custom_build(unit);
-        } else if unit.profile.doc && !unit.profile.test {
-            return self.doc_deps(unit);
-        }
-
-        let id = unit.pkg.package_id();
-        let deps = self.resolve.deps(id);
-        let mut ret = deps.filter(|dep| {
-            unit.pkg
-                .dependencies()
-                .iter()
-                .filter(|d| d.name() == dep.name() && d.version_req().matches(dep.version()))
-                .any(|d| {
-                    // If this target is a build command, then we only want build
-                    // dependencies, otherwise we want everything *other than* build
-                    // dependencies.
-                    if unit.target.is_custom_build() != d.is_build() {
-                        return false;
-                    }
-
-                    // If this dependency is *not* a transitive dependency, then it
-                    // only applies to test/example targets
-                    if !d.is_transitive() && !unit.target.is_test() && !unit.target.is_example()
-                        && !unit.profile.test
-                    {
-                        return false;
-                    }
-
-                    // If this dependency is only available for certain platforms,
-                    // make sure we're only enabling it for that platform.
-                    if !self.dep_platform_activated(d, unit.kind) {
-                        return false;
-                    }
-
-                    // If the dependency is optional, then we're only activating it
-                    // if the corresponding feature was activated
-                    if d.is_optional() && !self.resolve.features(id).contains(&*d.name()) {
-                        return false;
-                    }
-
-                    // If we've gotten past all that, then this dependency is
-                    // actually used!
-                    true
-                })
-        }).filter_map(|id| match self.get_package(id) {
-                Ok(pkg) => pkg.targets().iter().find(|t| t.is_lib()).map(|t| {
-                    let unit = Unit {
-                        pkg,
-                        target: t,
-                        profile: self.lib_or_check_profile(unit, t),
-                        kind: unit.kind.for_target(t),
-                    };
-                    Ok(unit)
-                }),
-                Err(e) => Some(Err(e)),
-            })
-            .collect::<CargoResult<Vec<_>>>()?;
-
-        // If this target is a build script, then what we've collected so far is
-        // all we need. If this isn't a build script, then it depends on the
-        // build script if there is one.
-        if unit.target.is_custom_build() {
-            return Ok(ret);
-        }
-        ret.extend(self.dep_build_script(unit));
-
-        // If this target is a binary, test, example, etc, then it depends on
-        // the library of the same package. The call to `resolve.deps` above
-        // didn't include `pkg` in the return values, so we need to special case
-        // it here and see if we need to push `(pkg, pkg_lib_target)`.
-        if unit.target.is_lib() && !unit.profile.doc {
-            return Ok(ret);
-        }
-        ret.extend(self.maybe_lib(unit));
-
-        // Integration tests/benchmarks require binaries to be built
-        if unit.profile.test && (unit.target.is_test() || unit.target.is_bench()) {
-            ret.extend(
-                unit.pkg
-                    .targets()
-                    .iter()
-                    .filter(|t| {
-                        let no_required_features = Vec::new();
-
-                        t.is_bin() &&
-                // Skip binaries with required features that have not been selected.
-                t.required_features().unwrap_or(&no_required_features).iter().all(|f| {
-                    self.resolve.features(id).contains(f)
-                })
-                    })
-                    .map(|t| Unit {
-                        pkg: unit.pkg,
-                        target: t,
-                        profile: self.lib_or_check_profile(unit, t),
-                        kind: unit.kind.for_target(t),
-                    }),
-            );
-        }
-        Ok(ret)
-    }
-
-    /// Returns the dependencies needed to run a build script.
-    ///
-    /// The `unit` provided must represent an execution of a build script, and
-    /// the returned set of units must all be run before `unit` is run.
-    pub fn dep_run_custom_build(&self, unit: &Unit<'a>) -> CargoResult<Vec<Unit<'a>>> {
-        // If this build script's execution has been overridden then we don't
-        // actually depend on anything, we've reached the end of the dependency
-        // chain as we've got all the info we're gonna get.
-        let key = (unit.pkg.package_id().clone(), unit.kind);
-        if self.build_script_overridden.contains(&key) {
-            return Ok(Vec::new());
-        }
-
-        // When not overridden, then the dependencies to run a build script are:
-        //
-        // 1. Compiling the build script itself
-        // 2. For each immediate dependency of our package which has a `links`
-        //    key, the execution of that build script.
-        let not_custom_build = unit.pkg
-            .targets()
-            .iter()
-            .find(|t| !t.is_custom_build())
-            .unwrap();
-        let tmp = Unit {
-            target: not_custom_build,
-            profile: &self.profiles.dev,
-            ..*unit
-        };
-        let deps = self.dep_targets(&tmp)?;
-        Ok(deps.iter()
-            .filter_map(|unit| {
-                if !unit.target.linkable() || unit.pkg.manifest().links().is_none() {
-                    return None;
-                }
-                self.dep_build_script(unit)
-            })
-            .chain(Some(Unit {
-                profile: self.build_script_profile(unit.pkg.package_id()),
-                kind: Kind::Host, // build scripts always compiled for the host
-                ..*unit
-            }))
-            .collect())
-    }
-
-    /// Returns the dependencies necessary to document a package
-    fn doc_deps(&self, unit: &Unit<'a>) -> CargoResult<Vec<Unit<'a>>> {
-        let deps = self.resolve
-            .deps(unit.pkg.package_id())
-            .filter(|dep| {
-                unit.pkg
-                    .dependencies()
-                    .iter()
-                    .filter(|d| d.name() == dep.name())
-                    .any(|dep| match dep.kind() {
-                        DepKind::Normal => self.dep_platform_activated(dep, unit.kind),
-                        _ => false,
-                    })
-            })
-            .map(|dep| self.get_package(dep));
-
-        // To document a library, we depend on dependencies actually being
-        // built. If we're documenting *all* libraries, then we also depend on
-        // the documentation of the library being built.
-        let mut ret = Vec::new();
-        for dep in deps {
-            let dep = dep?;
-            let lib = match dep.targets().iter().find(|t| t.is_lib()) {
-                Some(lib) => lib,
-                None => continue,
-            };
-            ret.push(Unit {
-                pkg: dep,
-                target: lib,
-                profile: self.lib_or_check_profile(unit, lib),
-                kind: unit.kind.for_target(lib),
-            });
-            if self.build_config.doc_all {
-                ret.push(Unit {
-                    pkg: dep,
-                    target: lib,
-                    profile: &self.profiles.doc,
-                    kind: unit.kind.for_target(lib),
-                });
-            }
-        }
-
-        // Be sure to build/run the build script for documented libraries as
-        ret.extend(self.dep_build_script(unit));
-
-        // If we document a binary, we need the library available
-        if unit.target.is_bin() {
-            ret.extend(self.maybe_lib(unit));
-        }
-        Ok(ret)
-    }
-
-    /// If a build script is scheduled to be run for the package specified by
-    /// `unit`, this function will return the unit to run that build script.
-    ///
-    /// Overriding a build script simply means that the running of the build
-    /// script itself doesn't have any dependencies, so even in that case a unit
-    /// of work is still returned. `None` is only returned if the package has no
-    /// build script.
-    fn dep_build_script(&self, unit: &Unit<'a>) -> Option<Unit<'a>> {
-        unit.pkg
-            .targets()
-            .iter()
-            .find(|t| t.is_custom_build())
-            .map(|t| Unit {
-                pkg: unit.pkg,
-                target: t,
-                profile: &self.profiles.custom_build,
-                kind: unit.kind,
-            })
-    }
-
-    fn maybe_lib(&self, unit: &Unit<'a>) -> Option<Unit<'a>> {
-        unit.pkg
-            .targets()
-            .iter()
-            .find(|t| t.linkable())
-            .map(|t| Unit {
-                pkg: unit.pkg,
-                target: t,
-                profile: self.lib_or_check_profile(unit, t),
-                kind: unit.kind.for_target(t),
-            })
-    }
-
-    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.host_triple(), &self.host_info),
-            Kind::Target => (self.target_triple(), &self.target_info),
-        };
-        platform.matches(name, info.cfg.as_ref().map(|cfg| &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.as_ref().map(|s| &s[..]).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 lib_profile(&self) -> &'a Profile {
-        let (normal, test) = if self.build_config.release {
-            (&self.profiles.release, &self.profiles.bench_deps)
-        } else {
-            (&self.profiles.dev, &self.profiles.test_deps)
-        };
-        if self.build_config.test {
-            test
-        } else {
-            normal
-        }
-    }
-
-    pub fn lib_or_check_profile(&self, unit: &Unit, target: &Target) -> &'a Profile {
-        if !target.is_custom_build() && !target.for_host()
-            && (unit.profile.check || (unit.profile.doc && !unit.profile.test))
-        {
-            return &self.profiles.check;
-        }
-        self.lib_profile()
-    }
-
-    pub fn build_script_profile(&self, _pkg: &PackageId) -> &'a Profile {
-        // TODO: should build scripts always be built with the same library
-        //       profile? How is this controlled at the CLI layer?
-        self.lib_profile()
-    }
-
-    pub fn incremental_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
-        // There's a number of ways to configure incremental compilation right
-        // now. In order of descending priority (first is highest priority) we
-        // have:
-        //
-        // * `CARGO_INCREMENTAL` - this is blanket used unconditionally to turn
-        //   on/off incremental compilation for any cargo subcommand. We'll
-        //   respect this if set.
-        // * `build.incremental` - in `.cargo/config` this blanket key can
-        //   globally for a system configure whether incremental compilation is
-        //   enabled. Note that setting this to `true` will not actually affect
-        //   all builds though. For example a `true` value doesn't enable
-        //   release incremental builds, only dev incremental builds. This can
-        //   be useful to globally disable incremental compilation like
-        //   `CARGO_INCREMENTAL`.
-        // * `profile.dev.incremental` - in `Cargo.toml` specific profiles can
-        //   be configured to enable/disable incremental compilation. This can
-        //   be primarily used to disable incremental when buggy for a project.
-        // * Finally, each profile has a default for whether it will enable
-        //   incremental compilation or not. Primarily development profiles
-        //   have it enabled by default while release profiles have it disabled
-        //   by default.
-        let global_cfg = self.config.get_bool("build.incremental")?.map(|c| c.val);
-        let incremental = match (self.incremental_env, global_cfg, unit.profile.incremental) {
-            (Some(v), _, _) => v,
-            (None, Some(false), _) => false,
-            (None, _, other) => other,
-        };
-
-        if !incremental {
-            return Ok(Vec::new());
-        }
-
-        // Only enable incremental compilation for sources the user can
-        // modify (aka path sources). For things that change infrequently,
-        // non-incremental builds yield better performance in the compiler
-        // itself (aka crates.io / git dependencies)
-        //
-        // (see also https://github.com/rust-lang/cargo/issues/3972)
-        if !unit.pkg.package_id().source_id().is_path() {
-            return Ok(Vec::new());
-        }
-
-        let dir = self.layout(unit.kind).incremental().display();
-        Ok(vec!["-C".to_string(), format!("incremental={}", dir)])
-    }
-
-    pub fn rustflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
-        env_args(
-            self.config,
-            &self.build_config,
-            self.info(&unit.kind),
-            unit.kind,
-            "RUSTFLAGS",
-        )
-    }
-
-    pub fn rustdocflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
-        env_args(
-            self.config,
-            &self.build_config,
-            self.info(&unit.kind),
-            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,
-        }
-    }
-}
-
-/// 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_info: &TargetInfo,
-    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()
-        .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(ref target_cfg) = target_info.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())
-}
-
-impl fmt::Display for Metadata {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "{:016x}", self.0)
-    }
-}
-
-/// 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())))
-}
-
-// (not a rustdoc)
-// Return a list of 3-tuples (suffix, file_type, should_replace_hyphens).
-//
-// should_replace_hyphens will be used by the caller to replace "-" with "_"
-// in a bin_stem. See the caller side (calc_target_filenames()) for details.
-fn add_target_specific_suffixes(
-    target_triple: &str,
-    crate_type: &str,
-    target_kind: &TargetKind,
-    suffix: &str,
-    file_type: TargetFileType,
-) -> Vec<(String, TargetFileType, bool)> {
-    let mut ret = vec![(suffix.to_string(), file_type, false)];
-
-    // rust-lang/cargo#4500
-    if target_triple.ends_with("pc-windows-msvc") && crate_type.ends_with("dylib")
-        && suffix == ".dll"
-    {
-        ret.push((".dll.lib".to_string(), TargetFileType::Normal, false));
-    }
-
-    // rust-lang/cargo#4535
-    if target_triple.starts_with("wasm32-") && crate_type == "bin" && suffix == ".js" {
-        ret.push((".wasm".to_string(), TargetFileType::Normal, 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 *target_kind == TargetKind::Bin {
-        if target_triple.contains("-apple-") {
-            ret.push((".dSYM".to_string(), TargetFileType::DebugInfo, false));
-        } else if target_triple.ends_with("-msvc") {
-            ret.push((".pdb".to_string(), TargetFileType::DebugInfo, false));
-        }
-    }
-
-    ret
-}
diff --git a/src/cargo/ops/cargo_rustc/context/mod.rs b/src/cargo/ops/cargo_rustc/context/mod.rs
new file mode 100644 (file)
index 0000000..d519843
--- /dev/null
@@ -0,0 +1,1169 @@
+#![allow(deprecated)]
+
+use std::collections::{HashMap, HashSet};
+use std::collections::hash_map::Entry;
+use std::env;
+use std::fmt;
+use std::hash::{Hash, Hasher, SipHasher};
+use std::path::{Path, PathBuf};
+use std::str::{self, FromStr};
+use std::sync::Arc;
+use std::cell::RefCell;
+
+use jobserver::Client;
+
+use core::{Package, PackageId, PackageSet, Profile, Resolve, Target};
+use core::{Dependency, Profiles, TargetKind, Workspace};
+use util::{self, internal, profile, Cfg, CfgExpr, Config, ProcessBuilder};
+use util::errors::{CargoResult, CargoResultExt};
+
+use super::TargetConfig;
+use super::custom_build::{BuildDeps, BuildScripts, BuildState};
+use super::fingerprint::Fingerprint;
+use super::layout::Layout;
+use super::links::Links;
+use super::{BuildConfig, Compilation, Kind};
+
+mod unit_dependencies;
+use self::unit_dependencies::build_unit_dependencies;
+
+/// All information needed to define a Unit.
+///
+/// A unit is an object that has enough information so that cargo knows how to build it.
+/// For example, if your project has dependencies, then every dependency will be built as a library
+/// unit. If your project is a library, then it will be built as a library unit as well, or if it
+/// is a binary with `main.rs`, then a binary will be output. There are also separate unit types
+/// for `test`ing and `check`ing, amongst others.
+///
+/// The unit also holds information about all possible metadata about the package in `pkg`.
+///
+/// A unit needs to know extra information in addition to the type and root source file. For
+/// example, it needs to know the target architecture (OS, chip arch etc.) and it needs to know
+/// whether you want a debug or release build. There is enough information in this struct to figure
+/// all that out.
+#[derive(Clone, Copy, Eq, PartialEq, Hash)]
+pub struct Unit<'a> {
+    /// Information about available targets, which files to include/exclude, etc. Basically stuff in
+    /// `Cargo.toml`.
+    pub pkg: &'a Package,
+    /// Information about the specific target to build, out of the possible targets in `pkg`. Not
+    /// to be confused with *target-triple* (or *target architecture* ...), the target arch for a
+    /// build.
+    pub target: &'a Target,
+    /// The profile contains information about *how* the build should be run, including debug
+    /// level, extra args to pass to rustc, etc.
+    pub profile: &'a Profile,
+    /// Whether this compilation unit is for the host or target architecture.
+    ///
+    /// For example, when
+    /// cross compiling and using a custom build script, the build script needs to be compiled for
+    /// the host architecture so the host rustc can use it (when compiling to the target
+    /// architecture).
+    pub kind: Kind,
+}
+
+/// Type of each file generated by a Unit.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum TargetFileType {
+    /// 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,
+}
+
+/// The build context, containing all information about a build task
+pub struct Context<'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,
+    /// Information on the compilation output
+    pub compilation: Compilation<'cfg>,
+    pub packages: &'a PackageSet<'cfg>,
+    pub build_state: Arc<BuildState>,
+    pub build_script_overridden: HashSet<(PackageId, Kind)>,
+    pub build_explicit_deps: HashMap<Unit<'a>, BuildDeps>,
+    pub fingerprints: HashMap<Unit<'a>, Arc<Fingerprint>>,
+    pub compiled: HashSet<Unit<'a>>,
+    pub build_config: BuildConfig,
+    pub build_scripts: HashMap<Unit<'a>, Arc<BuildScripts>>,
+    pub links: Links<'a>,
+    pub used_in_plugin: HashSet<Unit<'a>>,
+    pub jobserver: Client,
+
+    /// The target directory layout for the host (and target if it is the same as host)
+    host: Layout,
+    /// The target directory layout for the target (if different from then host)
+    target: Option<Layout>,
+    target_info: TargetInfo,
+    host_info: TargetInfo,
+    profiles: &'a Profiles,
+    incremental_env: Option<bool>,
+
+    unit_dependencies: Option<HashMap<Unit<'a>, Vec<Unit<'a>>>>,
+    /// For each Unit, a list all files produced as a triple of
+    ///
+    ///  - File name that will be produced by the build process (in `deps`)
+    ///  - If it should be linked into `target`, and what it should be called (e.g. without
+    ///    metadata).
+    ///  - Type of the file (library / debug symbol / else)
+    target_filenames: HashMap<Unit<'a>, Arc<Vec<(PathBuf, Option<PathBuf>, TargetFileType)>>>,
+    target_metadatas: HashMap<Unit<'a>, Option<Metadata>>,
+}
+
+#[derive(Clone, Default)]
+struct TargetInfo {
+    crate_type_process: Option<ProcessBuilder>,
+    crate_types: RefCell<HashMap<String, Option<(String, String)>>>,
+    cfg: Option<Vec<Cfg>>,
+}
+
+impl TargetInfo {
+    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())?)
+    }
+}
+
+#[derive(Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
+pub struct Metadata(u64);
+
+impl<'a, 'cfg> Context<'a, 'cfg> {
+    pub fn new(
+        ws: &'a Workspace<'cfg>,
+        resolve: &'a Resolve,
+        packages: &'a PackageSet<'cfg>,
+        config: &'cfg Config,
+        build_config: BuildConfig,
+        profiles: &'a Profiles,
+    ) -> CargoResult<Context<'a, 'cfg>> {
+        let dest = if build_config.release {
+            "release"
+        } else {
+            "debug"
+        };
+        let host_layout = Layout::new(ws, None, dest)?;
+        let target_layout = match build_config.requested_target.as_ref() {
+            Some(target) => Some(Layout::new(ws, Some(target), dest)?),
+            None => None,
+        };
+
+        let incremental_env = match env::var("CARGO_INCREMENTAL") {
+            Ok(v) => Some(v == "1"),
+            Err(_) => None,
+        };
+
+        // Load up the jobserver that we'll use to manage our parallelism. This
+        // is the same as the GNU make implementation of a jobserver, and
+        // intentionally so! It's hoped that we can interact with GNU make and
+        // all share the same jobserver.
+        //
+        // Note that if we don't have a jobserver in our environment then we
+        // create our own, and we create it with `n-1` tokens because one token
+        // is ourself, a running process.
+        let jobserver = match config.jobserver_from_env() {
+            Some(c) => c.clone(),
+            None => Client::new(build_config.jobs as usize - 1)
+                .chain_err(|| "failed to create jobserver")?,
+        };
+
+        Ok(Context {
+            ws,
+            host: host_layout,
+            target: target_layout,
+            resolve,
+            packages,
+            config,
+            target_info: TargetInfo::default(),
+            host_info: TargetInfo::default(),
+            compilation: Compilation::new(config),
+            build_state: Arc::new(BuildState::new(&build_config)),
+            build_config,
+            fingerprints: HashMap::new(),
+            profiles,
+            compiled: HashSet::new(),
+            build_scripts: HashMap::new(),
+            build_explicit_deps: HashMap::new(),
+            links: Links::new(),
+            used_in_plugin: HashSet::new(),
+            incremental_env,
+            jobserver,
+            build_script_overridden: HashSet::new(),
+
+            unit_dependencies: None,
+            // TODO: Pre-Calculate these with a topo-sort, rather than lazy-calculating
+            target_filenames: HashMap::new(),
+            target_metadatas: HashMap::new(),
+        })
+    }
+
+    /// Prepare this context, ensuring that all filesystem directories are in
+    /// place.
+    pub fn prepare(&mut self) -> CargoResult<()> {
+        let _p = profile::start("preparing layout");
+
+        self.host
+            .prepare()
+            .chain_err(|| internal("couldn't prepare build directories"))?;
+        if let Some(ref mut target) = self.target {
+            target
+                .prepare()
+                .chain_err(|| internal("couldn't prepare build directories"))?;
+        }
+
+        self.compilation.host_deps_output = self.host.deps().to_path_buf();
+
+        let layout = self.target.as_ref().unwrap_or(&self.host);
+        self.compilation.root_output = layout.dest().to_path_buf();
+        self.compilation.deps_output = layout.deps().to_path_buf();
+        Ok(())
+    }
+
+    pub fn build_unit_dependencies(&mut self, units: &[Unit<'a>]) -> CargoResult<()> {
+        assert!(self.unit_dependencies.is_none());
+        self.unit_dependencies = Some(build_unit_dependencies(units, self)?);
+        Ok(())
+    }
+
+    /// Ensure that we've collected all target-specific information to compile
+    /// all the units mentioned in `units`.
+    pub fn probe_target_info(&mut self) -> CargoResult<()> {
+        debug!("probe_target_info");
+        self.probe_target_info_kind(Kind::Target)?;
+        if self.requested_target().is_none() {
+            self.host_info = self.target_info.clone();
+        } else {
+            self.probe_target_info_kind(Kind::Host)?;
+        }
+        Ok(())
+    }
+
+    fn probe_target_info_kind(&mut self, kind: Kind) -> CargoResult<()> {
+        let rustflags = env_args(
+            self.config,
+            &self.build_config,
+            self.info(&kind),
+            kind,
+            "RUSTFLAGS",
+        )?;
+        let mut process = self.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(&self.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 = with_cfg
+            .exec_with_output()
+            .or_else(|_| {
+                has_cfg_and_sysroot = false;
+                process.exec_with_output()
+            })
+            .chain_err(|| "failed to run `rustc` to learn about target-specific information")?;
+
+        let error = str::from_utf8(&output.stderr).unwrap();
+        let output = str::from_utf8(&output.stdout).unwrap();
+        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);
+        }
+
+        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");
+                }
+                self.compilation.host_dylib_path = Some(rustlib);
+            } else {
+                rustlib.push("lib");
+                rustlib.push("rustlib");
+                rustlib.push(self.target_triple());
+                rustlib.push("lib");
+                self.compilation.target_dylib_path = Some(rustlib);
+            }
+        }
+
+        let cfg = if has_cfg_and_sysroot {
+            Some(lines.map(Cfg::from_str).collect::<CargoResult<_>>()?)
+        } else {
+            None
+        };
+
+        let info = match kind {
+            Kind::Target => &mut self.target_info,
+            Kind::Host => &mut self.host_info,
+        };
+        info.crate_type_process = Some(crate_type_process);
+        info.crate_types = RefCell::new(map);
+        info.cfg = cfg;
+        Ok(())
+    }
+
+    /// Builds up the `used_in_plugin` internal to this context from the list of
+    /// top-level units.
+    ///
+    /// This will recursively walk `units` and all of their dependencies to
+    /// determine which crate are going to be used in plugins or not.
+    pub fn build_used_in_plugin_map(&mut self, units: &[Unit<'a>]) -> CargoResult<()> {
+        let mut visited = HashSet::new();
+        for unit in units {
+            self.walk_used_in_plugin_map(unit, unit.target.for_host(), &mut visited)?;
+        }
+        Ok(())
+    }
+
+    fn walk_used_in_plugin_map(
+        &mut self,
+        unit: &Unit<'a>,
+        is_plugin: bool,
+        visited: &mut HashSet<(Unit<'a>, bool)>,
+    ) -> CargoResult<()> {
+        if !visited.insert((*unit, is_plugin)) {
+            return Ok(());
+        }
+        if is_plugin {
+            self.used_in_plugin.insert(*unit);
+        }
+        for unit in self.dep_targets(unit) {
+            self.walk_used_in_plugin_map(&unit, is_plugin || unit.target.for_host(), visited)?;
+        }
+        Ok(())
+    }
+
+    /// Returns the appropriate directory layout for either a plugin or not.
+    fn layout(&self, kind: Kind) -> &Layout {
+        match kind {
+            Kind::Host => &self.host,
+            Kind::Target => self.target.as_ref().unwrap_or(&self.host),
+        }
+    }
+
+    /// Returns the directories where Rust crate dependencies are found for the
+    /// specified unit.
+    pub fn deps_dir(&self, unit: &Unit) -> &Path {
+        self.layout(unit.kind).deps()
+    }
+
+    /// Returns the directory for the specified unit where fingerprint
+    /// information is stored.
+    pub fn fingerprint_dir(&mut self, unit: &Unit<'a>) -> PathBuf {
+        let dir = self.pkg_dir(unit);
+        self.layout(unit.kind).fingerprint().join(dir)
+    }
+
+    /// Returns the appropriate directory layout for either a plugin or not.
+    pub fn build_script_dir(&mut self, unit: &Unit<'a>) -> PathBuf {
+        assert!(unit.target.is_custom_build());
+        assert!(!unit.profile.run_custom_build);
+        let dir = self.pkg_dir(unit);
+        self.layout(Kind::Host).build().join(dir)
+    }
+
+    /// Returns the appropriate directory layout for either a plugin or not.
+    pub fn build_script_out_dir(&mut self, unit: &Unit<'a>) -> PathBuf {
+        assert!(unit.target.is_custom_build());
+        assert!(unit.profile.run_custom_build);
+        let dir = self.pkg_dir(unit);
+        self.layout(unit.kind).build().join(dir).join("out")
+    }
+
+    pub fn host_deps(&self) -> &Path {
+        self.host.deps()
+    }
+
+    /// Return the root of the build output tree
+    pub fn target_root(&self) -> &Path {
+        self.host.dest()
+    }
+
+    /// Returns the appropriate output directory for the specified package and
+    /// target.
+    pub fn out_dir(&mut self, unit: &Unit<'a>) -> PathBuf {
+        if unit.profile.doc {
+            self.layout(unit.kind).root().parent().unwrap().join("doc")
+        } else if unit.target.is_custom_build() {
+            self.build_script_dir(unit)
+        } else if unit.target.is_example() {
+            self.layout(unit.kind).examples().to_path_buf()
+        } else {
+            self.deps_dir(unit).to_path_buf()
+        }
+    }
+
+    fn pkg_dir(&mut self, unit: &Unit<'a>) -> String {
+        let name = unit.pkg.package_id().name();
+        match self.target_metadata(unit) {
+            Some(meta) => format!("{}-{}", name, meta),
+            None => format!("{}-{}", name, self.target_short_hash(unit)),
+        }
+    }
+
+    /// Return the host triple for this context
+    pub fn host_triple(&self) -> &str {
+        &self.build_config.host_triple
+    }
+
+    /// Return the target triple which this context is targeting.
+    pub fn target_triple(&self) -> &str {
+        self.requested_target()
+            .unwrap_or_else(|| self.host_triple())
+    }
+
+    /// Requested (not actual) target for the build
+    pub fn requested_target(&self) -> Option<&str> {
+        self.build_config.requested_target.as_ref().map(|s| &s[..])
+    }
+
+    /// Get the short hash based only on the PackageId
+    /// Used for the metadata when target_metadata returns None
+    pub fn target_short_hash(&self, unit: &Unit) -> String {
+        let hashable = unit.pkg.package_id().stable_hash(self.ws.root());
+        util::short_hash(&hashable)
+    }
+
+    /// Get the metadata for a target in a specific profile
+    /// We build to the path: "{filename}-{target_metadata}"
+    /// We use a linking step to link/copy to a predictable filename
+    /// like `target/debug/libfoo.{a,so,rlib}` and such.
+    pub fn target_metadata(&mut self, unit: &Unit<'a>) -> Option<Metadata> {
+        if let Some(cache) = self.target_metadatas.get(unit) {
+            return cache.clone();
+        }
+
+        let metadata = self.calc_target_metadata(unit);
+        self.target_metadatas.insert(*unit, metadata.clone());
+        metadata
+    }
+
+    fn calc_target_metadata(&mut self, unit: &Unit<'a>) -> Option<Metadata> {
+        // No metadata for dylibs because of a couple issues
+        // - OSX encodes the dylib name in the executable
+        // - Windows rustc multiple files of which we can't easily link all of them
+        //
+        // No metadata for bin because of an issue
+        // - wasm32 rustc/emcc encodes the .wasm name in the .js (rust-lang/cargo#4535)
+        //
+        // Two exceptions
+        // 1) Upstream dependencies (we aren't exporting + need to resolve name conflict)
+        // 2) __CARGO_DEFAULT_LIB_METADATA env var
+        //
+        // Note, though, that the compiler's build system at least wants
+        // path dependencies (eg libstd) to have hashes in filenames. To account for
+        // that we have an extra hack here which reads the
+        // `__CARGO_DEFAULT_LIB_METADATA` environment variable and creates a
+        // hash in the filename if that's present.
+        //
+        // This environment variable should not be relied on! It's
+        // just here for rustbuild. We need a more principled method
+        // doing this eventually.
+        let __cargo_default_lib_metadata = env::var("__CARGO_DEFAULT_LIB_METADATA");
+        if !(unit.profile.test || unit.profile.check)
+            && (unit.target.is_dylib() || unit.target.is_cdylib()
+                || (unit.target.is_bin() && self.target_triple().starts_with("wasm32-")))
+            && unit.pkg.package_id().source_id().is_path()
+            && !__cargo_default_lib_metadata.is_ok()
+        {
+            return None;
+        }
+
+        let mut hasher = SipHasher::new_with_keys(0, 0);
+
+        // Unique metadata per (name, source, version) triple. This'll allow us
+        // to pull crates from anywhere w/o worrying about conflicts
+        unit.pkg
+            .package_id()
+            .stable_hash(self.ws.root())
+            .hash(&mut hasher);
+
+        // Add package properties which map to environment variables
+        // exposed by Cargo
+        let manifest_metadata = unit.pkg.manifest().metadata();
+        manifest_metadata.authors.hash(&mut hasher);
+        manifest_metadata.description.hash(&mut hasher);
+        manifest_metadata.homepage.hash(&mut hasher);
+
+        // Also mix in enabled features to our metadata. This'll ensure that
+        // when changing feature sets each lib is separately cached.
+        self.resolve
+            .features_sorted(unit.pkg.package_id())
+            .hash(&mut hasher);
+
+        // Mix in the target-metadata of all the dependencies of this target
+        {
+            let mut deps_metadata = self.dep_targets(unit)
+                .iter()
+                .map(|dep_unit| self.target_metadata(dep_unit))
+                .collect::<Vec<_>>();
+            deps_metadata.sort();
+            deps_metadata.hash(&mut hasher);
+        }
+
+        // Throw in the profile we're compiling with. This helps caching
+        // panic=abort and panic=unwind artifacts, additionally with various
+        // settings like debuginfo and whatnot.
+        unit.profile.hash(&mut hasher);
+
+        // Artifacts compiled for the host should have a different metadata
+        // piece than those compiled for the target, so make sure we throw in
+        // the unit's `kind` as well
+        unit.kind.hash(&mut hasher);
+
+        // Finally throw in the target name/kind. This ensures that concurrent
+        // compiles of targets in the same crate don't collide.
+        unit.target.name().hash(&mut hasher);
+        unit.target.kind().hash(&mut hasher);
+
+        if let Ok(rustc) = self.config.rustc() {
+            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.
+        if let Ok(ref channel) = __cargo_default_lib_metadata {
+            channel.hash(&mut hasher);
+        }
+
+        Some(Metadata(hasher.finish()))
+    }
+
+    /// Returns the file stem for a given target/profile combo (with metadata)
+    pub fn file_stem(&mut self, unit: &Unit<'a>) -> String {
+        match self.target_metadata(unit) {
+            Some(ref metadata) => format!("{}-{}", unit.target.crate_name(), metadata),
+            None => self.bin_stem(unit),
+        }
+    }
+
+    /// Returns the bin stem for a given target (without metadata)
+    fn bin_stem(&self, unit: &Unit) -> String {
+        if unit.target.allows_underscores() {
+            unit.target.name().to_string()
+        } else {
+            unit.target.crate_name()
+        }
+    }
+
+    /// Returns a tuple with the directory and name of the hard link we expect
+    /// our target to be copied to. Eg, file_stem may be out_dir/deps/foo-abcdef
+    /// and link_stem would be out_dir/foo
+    /// This function returns it in two parts so the caller can add prefix/suffix
+    /// to filename separately
+    ///
+    /// Returns an Option because in some cases we don't want to link
+    /// (eg a dependent lib)
+    pub fn link_stem(&mut self, unit: &Unit<'a>) -> Option<(PathBuf, String)> {
+        let src_dir = self.out_dir(unit);
+        let bin_stem = self.bin_stem(unit);
+        let file_stem = self.file_stem(unit);
+
+        // We currently only lift files up from the `deps` directory. If
+        // it was compiled into something like `example/` or `doc/` then
+        // we don't want to link it up.
+        if src_dir.ends_with("deps") {
+            // Don't lift up library dependencies
+            if self.ws.members().find(|&p| p == unit.pkg).is_none() && !unit.target.is_bin() {
+                None
+            } else {
+                Some((
+                    src_dir.parent().unwrap().to_owned(),
+                    if unit.profile.test {
+                        file_stem
+                    } else {
+                        bin_stem
+                    },
+                ))
+            }
+        } else if bin_stem == file_stem {
+            None
+        } else if src_dir.ends_with("examples") || src_dir.parent().unwrap().ends_with("build") {
+            Some((src_dir, bin_stem))
+        } else {
+            None
+        }
+    }
+
+    /// Return the filenames that the given target for the given profile will
+    /// generate as a list of 3-tuples (filename, link_dst, linkable)
+    ///
+    ///  - filename: filename rustc compiles to. (Often has metadata suffix).
+    ///  - link_dst: Optional file to link/copy the result to (without metadata suffix)
+    ///  - linkable: Whether possible to link against file (eg it's a library)
+    pub fn target_filenames(
+        &mut self,
+        unit: &Unit<'a>,
+    ) -> CargoResult<Arc<Vec<(PathBuf, Option<PathBuf>, TargetFileType)>>> {
+        if let Some(cache) = self.target_filenames.get(unit) {
+            return Ok(Arc::clone(cache));
+        }
+
+        let result = self.calc_target_filenames(unit);
+        if let Ok(ref ret) = result {
+            self.target_filenames.insert(*unit, Arc::clone(ret));
+        }
+        result
+    }
+
+    fn calc_target_filenames(
+        &mut self,
+        unit: &Unit<'a>,
+    ) -> CargoResult<Arc<Vec<(PathBuf, Option<PathBuf>, TargetFileType)>>> {
+        let out_dir = self.out_dir(unit);
+        let stem = self.file_stem(unit);
+        let link_stem = self.link_stem(unit);
+        let info = if unit.target.for_host() {
+            &self.host_info
+        } else {
+            &self.target_info
+        };
+
+        let mut ret = Vec::new();
+        let mut unsupported = Vec::new();
+        {
+            if unit.profile.check {
+                let filename = out_dir.join(format!("lib{}.rmeta", stem));
+                let link_dst = link_stem
+                    .clone()
+                    .map(|(ld, ls)| ld.join(format!("lib{}.rmeta", ls)));
+                ret.push((filename, link_dst, TargetFileType::Linkable));
+            } else {
+                let mut add = |crate_type: &str, file_type: TargetFileType| -> CargoResult<()> {
+                    let crate_type = if crate_type == "lib" {
+                        "rlib"
+                    } else {
+                        crate_type
+                    };
+                    let mut crate_types = info.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 = info.discover_crate_type(v.key())?;
+                            &*v.insert(value)
+                        }
+                    };
+                    match *crate_type_info {
+                        Some((ref prefix, ref suffix)) => {
+                            let suffixes = add_target_specific_suffixes(
+                                self.target_triple(),
+                                crate_type,
+                                unit.target.kind(),
+                                suffix,
+                                file_type,
+                            );
+                            for (suffix, file_type, should_replace_hyphens) in suffixes {
+                                // 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".
+                                let conv = |s: String| {
+                                    if should_replace_hyphens {
+                                        s.replace("-", "_")
+                                    } else {
+                                        s
+                                    }
+                                };
+                                let filename = out_dir.join(format!(
+                                    "{}{}{}",
+                                    prefix,
+                                    conv(stem.clone()),
+                                    suffix
+                                ));
+                                let link_dst = link_stem.clone().map(|(ld, ls)| {
+                                    ld.join(format!("{}{}{}", prefix, conv(ls), suffix))
+                                });
+                                ret.push((filename, link_dst, file_type));
+                            }
+                            Ok(())
+                        }
+                        // not supported, don't worry about it
+                        None => {
+                            unsupported.push(crate_type.to_string());
+                            Ok(())
+                        }
+                    }
+                };
+                //info!("{:?}", unit);
+                match *unit.target.kind() {
+                    TargetKind::Bin
+                    | TargetKind::CustomBuild
+                    | TargetKind::ExampleBin
+                    | TargetKind::Bench
+                    | TargetKind::Test => {
+                        add("bin", TargetFileType::Normal)?;
+                    }
+                    TargetKind::Lib(..) | TargetKind::ExampleLib(..) if unit.profile.test => {
+                        add("bin", TargetFileType::Normal)?;
+                    }
+                    TargetKind::ExampleLib(ref kinds) | TargetKind::Lib(ref kinds) => {
+                        for kind in kinds {
+                            add(
+                                kind.crate_type(),
+                                if kind.linkable() {
+                                    TargetFileType::Linkable
+                                } else {
+                                    TargetFileType::Normal
+                                },
+                            )?;
+                        }
+                    }
+                }
+            }
+        }
+        if ret.is_empty() {
+            if !unsupported.is_empty() {
+                bail!(
+                    "cannot produce {} for `{}` as the target `{}` \
+                     does not support these crate types",
+                    unsupported.join(", "),
+                    unit.pkg,
+                    self.target_triple()
+                )
+            }
+            bail!(
+                "cannot compile `{}` as the target `{}` does not \
+                 support any of the output crate types",
+                unit.pkg,
+                self.target_triple()
+            );
+        }
+        info!("Target filenames: {:?}", ret);
+
+        Ok(Arc::new(ret))
+    }
+
+    /// For a package, return all targets which are registered as dependencies
+    /// for that package.
+    // TODO: this ideally should be `-> &[Unit<'a>]`
+    pub fn dep_targets(&self, unit: &Unit<'a>) -> Vec<Unit<'a>> {
+        // If this build script's execution has been overridden then we don't
+        // actually depend on anything, we've reached the end of the dependency
+        // chain as we've got all the info we're gonna get.
+        //
+        // Note there's a subtlety about this piece of code! The
+        // `build_script_overridden` map here is populated in
+        // `custom_build::build_map` which you need to call before inspecting
+        // dependencies. However, that code itself calls this method and
+        // gets a full pre-filtered set of dependencies. This is not super
+        // obvious, and clear, but it does work at the moment.
+        if unit.profile.run_custom_build {
+            let key = (unit.pkg.package_id().clone(), unit.kind);
+            if self.build_script_overridden.contains(&key) {
+                return Vec::new();
+            }
+        }
+        self.unit_dependencies.as_ref().unwrap()[unit].clone()
+    }
+
+    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.host_triple(), &self.host_info),
+            Kind::Target => (self.target_triple(), &self.target_info),
+        };
+        platform.matches(name, info.cfg.as_ref().map(|cfg| &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.as_ref().map(|s| &s[..]).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 lib_profile(&self) -> &'a Profile {
+        let (normal, test) = if self.build_config.release {
+            (&self.profiles.release, &self.profiles.bench_deps)
+        } else {
+            (&self.profiles.dev, &self.profiles.test_deps)
+        };
+        if self.build_config.test {
+            test
+        } else {
+            normal
+        }
+    }
+
+    pub fn build_script_profile(&self, _pkg: &PackageId) -> &'a Profile {
+        // TODO: should build scripts always be built with the same library
+        //       profile? How is this controlled at the CLI layer?
+        self.lib_profile()
+    }
+
+    pub fn incremental_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
+        // There's a number of ways to configure incremental compilation right
+        // now. In order of descending priority (first is highest priority) we
+        // have:
+        //
+        // * `CARGO_INCREMENTAL` - this is blanket used unconditionally to turn
+        //   on/off incremental compilation for any cargo subcommand. We'll
+        //   respect this if set.
+        // * `build.incremental` - in `.cargo/config` this blanket key can
+        //   globally for a system configure whether incremental compilation is
+        //   enabled. Note that setting this to `true` will not actually affect
+        //   all builds though. For example a `true` value doesn't enable
+        //   release incremental builds, only dev incremental builds. This can
+        //   be useful to globally disable incremental compilation like
+        //   `CARGO_INCREMENTAL`.
+        // * `profile.dev.incremental` - in `Cargo.toml` specific profiles can
+        //   be configured to enable/disable incremental compilation. This can
+        //   be primarily used to disable incremental when buggy for a project.
+        // * Finally, each profile has a default for whether it will enable
+        //   incremental compilation or not. Primarily development profiles
+        //   have it enabled by default while release profiles have it disabled
+        //   by default.
+        let global_cfg = self.config.get_bool("build.incremental")?.map(|c| c.val);
+        let incremental = match (self.incremental_env, global_cfg, unit.profile.incremental) {
+            (Some(v), _, _) => v,
+            (None, Some(false), _) => false,
+            (None, _, other) => other,
+        };
+
+        if !incremental {
+            return Ok(Vec::new());
+        }
+
+        // Only enable incremental compilation for sources the user can
+        // modify (aka path sources). For things that change infrequently,
+        // non-incremental builds yield better performance in the compiler
+        // itself (aka crates.io / git dependencies)
+        //
+        // (see also https://github.com/rust-lang/cargo/issues/3972)
+        if !unit.pkg.package_id().source_id().is_path() {
+            return Ok(Vec::new());
+        }
+
+        let dir = self.layout(unit.kind).incremental().display();
+        Ok(vec!["-C".to_string(), format!("incremental={}", dir)])
+    }
+
+    pub fn rustflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
+        env_args(
+            self.config,
+            &self.build_config,
+            self.info(&unit.kind),
+            unit.kind,
+            "RUSTFLAGS",
+        )
+    }
+
+    pub fn rustdocflags_args(&self, unit: &Unit) -> CargoResult<Vec<String>> {
+        env_args(
+            self.config,
+            &self.build_config,
+            self.info(&unit.kind),
+            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,
+        }
+    }
+}
+
+/// 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_info: &TargetInfo,
+    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()
+        .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(ref target_cfg) = target_info.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())
+}
+
+impl fmt::Display for Metadata {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{:016x}", self.0)
+    }
+}
+
+/// 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())))
+}
+
+// (not a rustdoc)
+// Return a list of 3-tuples (suffix, file_type, should_replace_hyphens).
+//
+// should_replace_hyphens will be used by the caller to replace "-" with "_"
+// in a bin_stem. See the caller side (calc_target_filenames()) for details.
+fn add_target_specific_suffixes(
+    target_triple: &str,
+    crate_type: &str,
+    target_kind: &TargetKind,
+    suffix: &str,
+    file_type: TargetFileType,
+) -> Vec<(String, TargetFileType, bool)> {
+    let mut ret = vec![(suffix.to_string(), file_type, false)];
+
+    // rust-lang/cargo#4500
+    if target_triple.ends_with("pc-windows-msvc") && crate_type.ends_with("dylib")
+        && suffix == ".dll"
+    {
+        ret.push((".dll.lib".to_string(), TargetFileType::Normal, false));
+    }
+
+    // rust-lang/cargo#4535
+    if target_triple.starts_with("wasm32-") && crate_type == "bin" && suffix == ".js" {
+        ret.push((".wasm".to_string(), TargetFileType::Normal, 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 *target_kind == TargetKind::Bin {
+        if target_triple.contains("-apple-") {
+            ret.push((".dSYM".to_string(), TargetFileType::DebugInfo, false));
+        } else if target_triple.ends_with("-msvc") {
+            ret.push((".pdb".to_string(), TargetFileType::DebugInfo, false));
+        }
+    }
+
+    ret
+}
diff --git a/src/cargo/ops/cargo_rustc/context/unit_dependencies.rs b/src/cargo/ops/cargo_rustc/context/unit_dependencies.rs
new file mode 100644 (file)
index 0000000..2fe20cd
--- /dev/null
@@ -0,0 +1,301 @@
+//! Constructs the dependency graph for compilation.
+//!
+//! Rust code is typically organized as a set of Cargo packages. The
+//! dependencies between the packages themselves are stored in the
+//! `Resolve` struct. However, we can't use that information as is for
+//! compilation! A package typically contains several targets, or crates,
+//! and these targets has inter-dependencies. For example, you need to
+//! compile the `lib` target before the `bin` one, and you need to compile
+//! `build.rs` before either of those.
+//!
+//! So, we need to lower the `Resolve`, which specifies dependencies between
+//! *packages*, to a graph of dependencies between their *targets*, and this
+//! is exactly what this module is doing! Well, almost exactly: another
+//! complication is that we might want to compile the same target several times
+//! (for example, with and without tests), so we actually build a dependency
+//! graph of `Unit`s, which capture these properties.
+
+use ops::Unit;
+use std::collections::HashMap;
+use CargoResult;
+use core::dependency::Kind as DepKind;
+use ops::{Context, Kind};
+use core::Target;
+use core::Profile;
+
+pub fn build_unit_dependencies<'a, 'cfg>(
+    roots: &[Unit<'a>],
+    cx: &Context<'a, 'cfg>,
+) -> CargoResult<HashMap<Unit<'a>, Vec<Unit<'a>>>> {
+    let mut deps = HashMap::new();
+    for unit in roots.iter() {
+        deps_of(unit, cx, &mut deps)?;
+    }
+
+    Ok(deps)
+}
+
+fn deps_of<'a, 'b, 'cfg>(
+    unit: &Unit<'a>,
+    cx: &Context<'a, 'cfg>,
+    deps: &'b mut HashMap<Unit<'a>, Vec<Unit<'a>>>,
+) -> CargoResult<&'b [Unit<'a>]> {
+    if !deps.contains_key(unit) {
+        let unit_deps = compute_deps(unit, cx, deps)?;
+        deps.insert(*unit, unit_deps.clone());
+        for unit in unit_deps {
+            deps_of(&unit, cx, deps)?;
+        }
+    }
+    Ok(deps[unit].as_ref())
+}
+
+/// For a package, return all targets which are registered as dependencies
+/// for that package.
+fn compute_deps<'a, 'b, 'cfg>(
+    unit: &Unit<'a>,
+    cx: &Context<'a, 'cfg>,
+    deps: &'b mut HashMap<Unit<'a>, Vec<Unit<'a>>>,
+) -> CargoResult<Vec<Unit<'a>>> {
+    if unit.profile.run_custom_build {
+        return compute_deps_custom_build(unit, cx, deps);
+    } else if unit.profile.doc && !unit.profile.test {
+        return compute_deps_doc(unit, cx);
+    }
+
+    let id = unit.pkg.package_id();
+    let deps = cx.resolve.deps(id);
+    let mut ret = deps.filter(|dep| {
+        unit.pkg
+            .dependencies()
+            .iter()
+            .filter(|d| d.name() == dep.name() && d.version_req().matches(dep.version()))
+            .any(|d| {
+                // If this target is a build command, then we only want build
+                // dependencies, otherwise we want everything *other than* build
+                // dependencies.
+                if unit.target.is_custom_build() != d.is_build() {
+                    return false;
+                }
+
+                // If this dependency is *not* a transitive dependency, then it
+                // only applies to test/example targets
+                if !d.is_transitive() && !unit.target.is_test() && !unit.target.is_example()
+                    && !unit.profile.test
+                {
+                    return false;
+                }
+
+                // If this dependency is only available for certain platforms,
+                // make sure we're only enabling it for that platform.
+                if !cx.dep_platform_activated(d, unit.kind) {
+                    return false;
+                }
+
+                // If the dependency is optional, then we're only activating it
+                // if the corresponding feature was activated
+                if d.is_optional() && !cx.resolve.features(id).contains(&*d.name()) {
+                    return false;
+                }
+
+                // If we've gotten past all that, then this dependency is
+                // actually used!
+                true
+            })
+    }).filter_map(|id| match cx.get_package(id) {
+            Ok(pkg) => pkg.targets().iter().find(|t| t.is_lib()).map(|t| {
+                let unit = Unit {
+                    pkg,
+                    target: t,
+                    profile: lib_or_check_profile(unit, t, cx),
+                    kind: unit.kind.for_target(t),
+                };
+                Ok(unit)
+            }),
+            Err(e) => Some(Err(e)),
+        })
+        .collect::<CargoResult<Vec<_>>>()?;
+
+    // If this target is a build script, then what we've collected so far is
+    // all we need. If this isn't a build script, then it depends on the
+    // build script if there is one.
+    if unit.target.is_custom_build() {
+        return Ok(ret);
+    }
+    ret.extend(dep_build_script(unit, cx));
+
+    // If this target is a binary, test, example, etc, then it depends on
+    // the library of the same package. The call to `resolve.deps` above
+    // didn't include `pkg` in the return values, so we need to special case
+    // it here and see if we need to push `(pkg, pkg_lib_target)`.
+    if unit.target.is_lib() && !unit.profile.doc {
+        return Ok(ret);
+    }
+    ret.extend(maybe_lib(unit, cx));
+
+    // Integration tests/benchmarks require binaries to be built
+    if unit.profile.test && (unit.target.is_test() || unit.target.is_bench()) {
+        ret.extend(
+            unit.pkg
+                .targets()
+                .iter()
+                .filter(|t| {
+                    let no_required_features = Vec::new();
+
+                    t.is_bin() &&
+                        // Skip binaries with required features that have not been selected.
+                        t.required_features().unwrap_or(&no_required_features).iter().all(|f| {
+                            cx.resolve.features(id).contains(f)
+                        })
+                })
+                .map(|t| Unit {
+                    pkg: unit.pkg,
+                    target: t,
+                    profile: lib_or_check_profile(unit, t, cx),
+                    kind: unit.kind.for_target(t),
+                }),
+        );
+    }
+    Ok(ret)
+}
+
+/// Returns the dependencies needed to run a build script.
+///
+/// The `unit` provided must represent an execution of a build script, and
+/// the returned set of units must all be run before `unit` is run.
+fn compute_deps_custom_build<'a, 'cfg>(
+    unit: &Unit<'a>,
+    cx: &Context<'a, 'cfg>,
+    deps: &mut HashMap<Unit<'a>, Vec<Unit<'a>>>,
+) -> CargoResult<Vec<Unit<'a>>> {
+    // When not overridden, then the dependencies to run a build script are:
+    //
+    // 1. Compiling the build script itcx
+    // 2. For each immediate dependency of our package which has a `links`
+    //    key, the execution of that build script.
+    let not_custom_build = unit.pkg
+        .targets()
+        .iter()
+        .find(|t| !t.is_custom_build())
+        .unwrap();
+    let tmp = Unit {
+        target: not_custom_build,
+        profile: &cx.profiles.dev,
+        ..*unit
+    };
+    let deps = deps_of(&tmp, cx, deps)?;
+    Ok(deps.iter()
+        .filter_map(|unit| {
+            if !unit.target.linkable() || unit.pkg.manifest().links().is_none() {
+                return None;
+            }
+            dep_build_script(unit, cx)
+        })
+        .chain(Some(Unit {
+            profile: cx.build_script_profile(unit.pkg.package_id()),
+            kind: Kind::Host, // build scripts always compiled for the host
+            ..*unit
+        }))
+        .collect())
+}
+
+/// Returns the dependencies necessary to document a package
+fn compute_deps_doc<'a, 'cfg>(
+    unit: &Unit<'a>,
+    cx: &Context<'a, 'cfg>,
+) -> CargoResult<Vec<Unit<'a>>> {
+    let deps = cx.resolve
+        .deps(unit.pkg.package_id())
+        .filter(|dep| {
+            unit.pkg
+                .dependencies()
+                .iter()
+                .filter(|d| d.name() == dep.name())
+                .any(|dep| match dep.kind() {
+                    DepKind::Normal => cx.dep_platform_activated(dep, unit.kind),
+                    _ => false,
+                })
+        })
+        .map(|dep| cx.get_package(dep));
+
+    // To document a library, we depend on dependencies actually being
+    // built. If we're documenting *all* libraries, then we also depend on
+    // the documentation of the library being built.
+    let mut ret = Vec::new();
+    for dep in deps {
+        let dep = dep?;
+        let lib = match dep.targets().iter().find(|t| t.is_lib()) {
+            Some(lib) => lib,
+            None => continue,
+        };
+        ret.push(Unit {
+            pkg: dep,
+            target: lib,
+            profile: lib_or_check_profile(unit, lib, cx),
+            kind: unit.kind.for_target(lib),
+        });
+        if cx.build_config.doc_all {
+            ret.push(Unit {
+                pkg: dep,
+                target: lib,
+                profile: &cx.profiles.doc,
+                kind: unit.kind.for_target(lib),
+            });
+        }
+    }
+
+    // Be sure to build/run the build script for documented libraries as
+    ret.extend(dep_build_script(unit, cx));
+
+    // If we document a binary, we need the library available
+    if unit.target.is_bin() {
+        ret.extend(maybe_lib(unit, cx));
+    }
+    Ok(ret)
+}
+
+fn maybe_lib<'a, 'cfg>(unit: &Unit<'a>, cx: &Context<'a, 'cfg>) -> Option<Unit<'a>> {
+    unit.pkg
+        .targets()
+        .iter()
+        .find(|t| t.linkable())
+        .map(|t| Unit {
+            pkg: unit.pkg,
+            target: t,
+            profile: lib_or_check_profile(unit, t, cx),
+            kind: unit.kind.for_target(t),
+        })
+}
+
+/// If a build script is scheduled to be run for the package specified by
+/// `unit`, this function will return the unit to run that build script.
+///
+/// Overriding a build script simply means that the running of the build
+/// script itself doesn't have any dependencies, so even in that case a unit
+/// of work is still returned. `None` is only returned if the package has no
+/// build script.
+fn dep_build_script<'a, 'cfg>(unit: &Unit<'a>, cx: &Context<'a, 'cfg>) -> Option<Unit<'a>> {
+    unit.pkg
+        .targets()
+        .iter()
+        .find(|t| t.is_custom_build())
+        .map(|t| Unit {
+            pkg: unit.pkg,
+            target: t,
+            profile: &cx.profiles.custom_build,
+            kind: unit.kind,
+        })
+}
+
+fn lib_or_check_profile<'a, 'cfg>(
+    unit: &Unit,
+    target: &Target,
+    cx: &Context<'a, 'cfg>,
+) -> &'a Profile {
+    if !target.is_custom_build() && !target.for_host()
+        && (unit.profile.check || (unit.profile.doc && !unit.profile.test))
+    {
+        return &cx.profiles.check;
+    }
+    cx.lib_profile()
+}
index 32fb3ef74e3523bcc495e7b1e42cf23e04d7e3b2..ce0c0efd26e8bf2f7db5767660079a0d05c438cc 100644 (file)
@@ -101,7 +101,8 @@ pub fn prepare<'a, 'cfg>(
 }
 
 fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult<(Work, Work)> {
-    let dependencies = cx.dep_run_custom_build(unit)?;
+    assert!(unit.profile.run_custom_build);
+    let dependencies = cx.dep_targets(unit);
     let build_script_unit = dependencies
         .iter()
         .find(|d| !d.profile.run_custom_build && d.target.is_custom_build())
@@ -581,7 +582,7 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
         // to rustc invocation caching schemes, so be sure to generate the same
         // set of build script dependency orderings via sorting the targets that
         // come out of the `Context`.
-        let mut targets = cx.dep_targets(unit)?;
+        let mut targets = cx.dep_targets(unit);
         targets.sort_by_key(|u| u.pkg.package_id());
 
         for unit in targets.iter() {
index 38ac975de39328539bb76fde70eddf9c5eacfb94..bd441d35848bfd060be7a4c7b9849caa78e244fd 100644 (file)
@@ -431,7 +431,7 @@ fn calculate<'a, 'cfg>(
     // elsewhere. Also skip fingerprints of binaries because they don't actually
     // induce a recompile, they're just dependencies in the sense that they need
     // to be built.
-    let deps = cx.dep_targets(unit)?;
+    let deps = cx.dep_targets(unit);
     let deps = deps.iter()
         .filter(|u| !u.target.is_custom_build() && !u.target.is_bin())
         .map(|unit| {
index c71bab90efb8566e0dcd4f0f7ef1c327ba96928a..cf4c936257fd3db3fa324dc6bed6ca0426e326a9 100644 (file)
@@ -416,7 +416,7 @@ impl<'a> Key<'a> {
             profile: self.profile,
             kind: self.kind,
         };
-        let targets = cx.dep_targets(&unit)?;
+        let targets = cx.dep_targets(&unit);
         Ok(targets
             .iter()
             .filter_map(|unit| {
index a602c7c849e32d1f3ad0cda86371a8a6a5f40067..695b882c3e67a15645de11c8b667a07d29476eca 100644 (file)
@@ -170,6 +170,7 @@ pub fn compile_targets<'a, 'cfg: 'a>(
 
     cx.prepare()?;
     cx.probe_target_info()?;
+    cx.build_unit_dependencies(&units)?;
     cx.build_used_in_plugin_map(&units)?;
     custom_build::build_map(&mut cx, &units)?;
 
@@ -215,7 +216,7 @@ pub fn compile_targets<'a, 'cfg: 'a>(
             }
         }
 
-        for dep in cx.dep_targets(unit)?.iter() {
+        for dep in cx.dep_targets(unit).iter() {
             if !unit.target.is_lib() {
                 continue;
             }
@@ -333,7 +334,7 @@ fn compile<'a, 'cfg: 'a>(
     drop(p);
 
     // Be sure to compile all dependencies of this target as well.
-    for unit in cx.dep_targets(unit)?.iter() {
+    for unit in cx.dep_targets(unit).iter() {
         compile(cx, jobs, unit, exec)?;
     }
 
@@ -1022,7 +1023,7 @@ fn build_deps_args<'a, 'cfg>(
         });
     }
 
-    let dep_targets = cx.dep_targets(unit)?;
+    let dep_targets = cx.dep_targets(unit);
 
     // If there is not one linkable target but should, rustc fails later
     // on if there is an `extern crate` for it. This may turn into a hard
index 7508ca9d512538109f5992fb87c9bbc3c6635d0e..a8a83425ef8511626764bcf8a12c7aef5898ec10 100644 (file)
@@ -61,7 +61,7 @@ fn add_deps_for_unit<'a, 'b>(
     }
 
     // Recursively traverse all transitive dependencies
-    for dep_unit in &context.dep_targets(unit)? {
+    for dep_unit in context.dep_targets(unit).iter() {
         let source_id = dep_unit.pkg.package_id().source_id();
         if source_id.is_path() {
             add_deps_for_unit(deps, context, dep_unit, visited)?;