}
cx.probe_target_info()?;
+ cx.build_unit_dependencies(&units)?;
for unit in units.iter() {
rm_rf(&cx.fingerprint_dir(unit), config)?;
+++ /dev/null
-#![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
-}
--- /dev/null
+#![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
+}
--- /dev/null
+//! 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()
+}
}
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())
// 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() {
// 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| {
profile: self.profile,
kind: self.kind,
};
- let targets = cx.dep_targets(&unit)?;
+ let targets = cx.dep_targets(&unit);
Ok(targets
.iter()
.filter_map(|unit| {
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)?;
}
}
- for dep in cx.dep_targets(unit)?.iter() {
+ for dep in cx.dep_targets(unit).iter() {
if !unit.target.is_lib() {
continue;
}
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)?;
}
});
}
- 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
}
// 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)?;