Skip to content

Commit

Permalink
auto merge of #735 : tomaka/cargo/platform-specific-deps, r=alexcrichton
Browse files Browse the repository at this point in the history
cc #610
  • Loading branch information
bors committed Oct 30, 2014
2 parents 978093f + 63a3410 commit 7e78341
Show file tree
Hide file tree
Showing 12 changed files with 250 additions and 55 deletions.
25 changes: 25 additions & 0 deletions src/cargo/core/dependency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ pub struct Dependency {
optional: bool,
default_features: bool,
features: Vec<String>,

// This dependency should be used only for this platform.
// `None` means *all platforms*.
only_for_platform: Option<String>,
}

impl Dependency {
Expand Down Expand Up @@ -57,6 +61,7 @@ impl Dependency {
features: Vec::new(),
default_features: true,
specified_req: None,
only_for_platform: None,
}
}

Expand Down Expand Up @@ -121,6 +126,11 @@ impl Dependency {
.source_id(id.get_source_id().clone())
}

pub fn only_for_platform(mut self, platform: Option<String>) -> Dependency {
self.only_for_platform = platform;
self
}

/// Returns false if the dependency is only used to build the local package.
pub fn is_transitive(&self) -> bool { self.transitive }
pub fn is_optional(&self) -> bool { self.optional }
Expand All @@ -140,6 +150,21 @@ impl Dependency {
(self.only_match_name || (self.req.matches(id.get_version()) &&
&self.source_id == id.get_source_id()))
}

/// If none, this dependencies must be built for all platforms.
/// If some, it must only be built for the specified platform.
pub fn get_only_for_platform(&self) -> Option<&str> {
self.only_for_platform.as_ref().map(|s| s.as_slice())
}

/// Returns true if the dependency should be built for this platform.
pub fn is_active_for_platform(&self, platform: &str) -> bool {
match self.only_for_platform {
None => true,
Some(ref p) if p.as_slice() == platform => true,
_ => false
}
}
}

#[deriving(PartialEq,Clone,Encodable)]
Expand Down
44 changes: 31 additions & 13 deletions src/cargo/core/resolver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ pub enum ResolveMethod<'a> {
ResolveEverything,
ResolveRequired(/* dev_deps = */ bool,
/* features = */ &'a [String],
/* uses_default_features = */ bool),
/* uses_default_features = */ bool,
/* target_platform = */ Option<&'a str>),
}

impl Resolve {
Expand Down Expand Up @@ -150,6 +151,12 @@ fn activate<R: Registry>(mut cx: Context,
parent: &Summary,
method: ResolveMethod)
-> CargoResult<CargoResult<Context>> {
// Extracting the platform request.
let platform = match method {
ResolveRequired(_, _, _, platform) => platform,
ResolveEverything => None,
};

// First, figure out our set of dependencies based on the requsted set of
// features. This also calculates what features we're going to enable for
// our own dependencies.
Expand All @@ -176,18 +183,19 @@ fn activate<R: Registry>(mut cx: Context,
a.len().cmp(&b.len())
});

activate_deps(cx, registry, parent, deps.as_slice(), 0)
activate_deps(cx, registry, parent, platform, deps.as_slice(), 0)
}

fn activate_deps<R: Registry>(cx: Context,
registry: &mut R,
parent: &Summary,
deps: &[(&Dependency, Vec<Rc<Summary>>, Vec<String>)],
cur: uint) -> CargoResult<CargoResult<Context>> {
fn activate_deps<'a, R: Registry>(cx: Context,
registry: &mut R,
parent: &Summary,
platform: Option<&'a str>,
deps: &'a [(&Dependency, Vec<Rc<Summary>>, Vec<String>)],
cur: uint) -> CargoResult<CargoResult<Context>> {
if cur == deps.len() { return Ok(Ok(cx)) }
let (dep, ref candidates, ref features) = deps[cur];
let method = ResolveRequired(false, features.as_slice(),
dep.uses_default_features());
dep.uses_default_features(), platform);

let key = (dep.get_name().to_string(), dep.get_source_id().clone());
let prev_active = cx.activations.find(&key)
Expand Down Expand Up @@ -269,7 +277,7 @@ fn activate_deps<R: Registry>(cx: Context,
Err(e) => { last_err = Some(e); continue }
}
};
match try!(activate_deps(my_cx, registry, parent, deps, cur + 1)) {
match try!(activate_deps(my_cx, registry, parent, platform, deps, cur + 1)) {
Ok(cx) => return Ok(Ok(cx)),
Err(e) => { last_err = Some(e); }
}
Expand Down Expand Up @@ -341,12 +349,22 @@ fn resolve_features<'a>(cx: &mut Context, parent: &'a Summary,
(&'a Dependency, Vec<String>)>> {
let dev_deps = match method {
ResolveEverything => true,
ResolveRequired(dev_deps, _, _) => dev_deps,
ResolveRequired(dev_deps, _, _, _) => dev_deps,
};

// First, filter by dev-dependencies
let deps = parent.get_dependencies();
let mut deps = deps.iter().filter(|d| d.is_transitive() || dev_deps);
let deps = deps.iter().filter(|d| d.is_transitive() || dev_deps);

// Second, ignoring dependencies that should not be compiled for this platform
let mut deps = deps.filter(|d| {
match method {
ResolveRequired(_, _, _, Some(ref platform)) => {
d.is_active_for_platform(platform.as_slice())
},
_ => true
}
});

let (mut feature_deps, used_features) = try!(build_features(parent, method));
let mut ret = HashMap::new();
Expand Down Expand Up @@ -419,15 +437,15 @@ fn build_features(s: &Summary, method: ResolveMethod)
&mut visited));
}
}
ResolveRequired(_, requested_features, _) => {
ResolveRequired(_, requested_features, _, _) => {
for feat in requested_features.iter() {
try!(add_feature(s, feat.as_slice(), &mut deps, &mut used,
&mut visited));
}
}
}
match method {
ResolveEverything | ResolveRequired(_, _, true) => {
ResolveEverything | ResolveRequired(_, _, true, _) => {
if s.get_features().find_equiv(&"default").is_some() &&
!visited.contains_equiv(&"default") {
try!(add_feature(s, "default", &mut deps, &mut used,
Expand Down
6 changes: 5 additions & 1 deletion src/cargo/ops/cargo_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ pub fn compile_pkg(package: &Package, options: &mut CompileOptions)

let (packages, resolve_with_overrides, sources) = {
let mut config = try!(Config::new(*shell, jobs, target.clone()));
let rustc_host = config.rustc_host().to_string();
let mut registry = PackageRegistry::new(&mut config);

// First, resolve the package's *listed* dependencies, as well as
Expand All @@ -98,8 +99,11 @@ pub fn compile_pkg(package: &Package, options: &mut CompileOptions)
let _p = profile::start("resolving w/ overrides...");

try!(registry.add_overrides(override_ids));

let platform = target.as_ref().map(|e| e.as_slice()).or(Some(rustc_host.as_slice()));
let method = resolver::ResolveRequired(dev_deps, features.as_slice(),
!no_default_features);
!no_default_features,
platform);
let resolved_with_overrides =
try!(ops::resolve_with_previous(&mut registry, package, method,
Some(&resolve), None));
Expand Down
35 changes: 4 additions & 31 deletions src/cargo/ops/cargo_rustc/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::collections::hashmap::{Occupied, Vacant};
use std::str;

use core::{SourceMap, Package, PackageId, PackageSet, Resolve, Target};
use util::{mod, CargoResult, ChainError, internal, Config, profile, Require};
use util::{mod, CargoResult, ChainError, internal, Config, profile};
use util::human;

use super::{Kind, KindPlugin, KindTarget, Compilation};
Expand All @@ -17,7 +17,6 @@ pub enum PlatformRequirement {
}

pub struct Context<'a, 'b> {
pub rustc_version: String,
pub config: &'b mut Config<'b>,
pub resolve: &'a Resolve,
pub sources: &'a SourceMap<'b>,
Expand All @@ -27,7 +26,6 @@ pub struct Context<'a, 'b> {
host: Layout,
target: Option<Layout>,
target_triple: String,
host_triple: String,
host_dylib: Option<(String, String)>,
package_set: &'a PackageSet,
target_dylib: Option<(String, String)>,
Expand All @@ -49,13 +47,10 @@ impl<'a, 'b> Context<'a, 'b> {
let (dylib, _) = try!(Context::filename_parts(None));
dylib
};
let (rustc_version, rustc_host) = try!(Context::rustc_version());
let target_triple = config.target().map(|s| s.to_string());
let target_triple = target_triple.unwrap_or(rustc_host.clone());
let target_triple = target_triple.unwrap_or(config.rustc_host().to_string());
Ok(Context {
rustc_version: rustc_version,
target_triple: target_triple,
host_triple: rustc_host,
env: env,
host: host,
target: target,
Expand All @@ -71,28 +66,6 @@ impl<'a, 'b> Context<'a, 'b> {
})
}

/// Run `rustc` to figure out what its current version string is.
///
/// The second element of the tuple returned is the target triple that rustc
/// is a host for.
fn rustc_version() -> CargoResult<(String, String)> {
let output = try!(util::process("rustc").arg("-v").arg("verbose")
.exec_with_output());
let output = try!(String::from_utf8(output.output).map_err(|_| {
internal("rustc -v didn't return utf8 output")
}));
let triple = {
let triple = output.as_slice().lines().filter(|l| {
l.starts_with("host: ")
}).map(|l| l.slice_from(6)).next();
let triple = try!(triple.require(|| {
internal("rustc -v didn't have a line for `host:`")
}));
triple.to_string()
};
Ok((output, triple))
}

/// Run `rustc` to discover the dylib prefix/suffix for the target
/// specified as well as the exe suffix
fn filename_parts(target: Option<&str>)
Expand Down Expand Up @@ -204,9 +177,9 @@ impl<'a, 'b> Context<'a, 'b> {
/// otherwise it corresponds to the target platform.
fn dylib(&self, kind: Kind) -> CargoResult<(&str, &str)> {
let (triple, pair) = if kind == KindPlugin {
(&self.host_triple, &self.host_dylib)
(self.config.rustc_host(), &self.host_dylib)
} else {
(&self.target_triple, &self.target_dylib)
(self.target_triple.as_slice(), &self.target_dylib)
};
match *pair {
None => return Err(human(format!("dylib outputs are not supported \
Expand Down
2 changes: 1 addition & 1 deletion src/cargo/ops/cargo_rustc/fingerprint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ fn is_fresh(loc: &Path, new_fingerprint: &str) -> CargoResult<bool> {
/// fingerprint.
fn mk_fingerprint<T: Hash>(cx: &Context, data: &T) -> String {
let hasher = SipHasher::new_with_keys(0,0);
util::to_hex(hasher.hash(&(&cx.rustc_version, data)))
util::to_hex(hasher.hash(&(cx.config.rustc_version(), data)))
}

fn calculate_target_fresh(pkg: &Package, dep_info: &Path) -> CargoResult<bool> {
Expand Down
26 changes: 24 additions & 2 deletions src/cargo/ops/cargo_rustc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use std::io::fs::PathExtensions;
use std::os;

use core::{SourceMap, Package, PackageId, PackageSet, Target, Resolve};
use util::{CargoResult, ProcessBuilder, CargoError, human, caused_human};
use util::{Config, internal, ChainError, Fresh, profile};
use util::{mod, CargoResult, ProcessBuilder, CargoError, human, caused_human};
use util::{Require, Config, internal, ChainError, Fresh, profile};

use self::job::{Job, Work};
use self::job_queue as jq;
Expand All @@ -28,6 +28,28 @@ mod layout;
#[deriving(PartialEq, Eq)]
pub enum Kind { KindPlugin, KindTarget }

/// Run `rustc` to figure out what its current version string is.
///
/// The second element of the tuple returned is the target triple that rustc
/// is a host for.
pub fn rustc_version() -> CargoResult<(String, String)> {
let output = try!(util::process("rustc").arg("-v").arg("verbose")
.exec_with_output());
let output = try!(String::from_utf8(output.output).map_err(|_| {
internal("rustc -v didn't return utf8 output")
}));
let triple = {
let triple = output.as_slice().lines().filter(|l| {
l.starts_with("host: ")
}).map(|l| l.slice_from(6)).next();
let triple = try!(triple.require(|| {
internal("rustc -v didn't have a line for `host:`")
}));
triple.to_string()
};
Ok((output, triple))
}

// This is a temporary assert that ensures the consistency of the arguments
// given the current limitations of Cargo. The long term fix is to have each
// Target know the absolute path to the build location.
Expand Down
2 changes: 1 addition & 1 deletion src/cargo/ops/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pub use self::cargo_clean::{clean, CleanOptions};
pub use self::cargo_compile::{compile, compile_pkg, CompileOptions};
pub use self::cargo_read_manifest::{read_manifest,read_package,read_packages};
pub use self::cargo_rustc::{compile_targets, Compilation, Layout, Kind};
pub use self::cargo_rustc::{compile_targets, Compilation, Layout, Kind, rustc_version};
pub use self::cargo_rustc::{KindTarget, KindPlugin, Context, LayoutProxy};
pub use self::cargo_rustc::{PlatformRequirement, PlatformTarget};
pub use self::cargo_rustc::{PlatformPlugin, PlatformPluginAndTarget};
Expand Down
2 changes: 1 addition & 1 deletion src/cargo/ops/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ fn transmit(pkg: &Package, tarball: &Path, registry: &mut Registry)
name: dep.get_name().to_string(),
features: dep.get_features().to_vec(),
version_req: dep.get_version_req().to_string(),
target: None, // FIXME: fill this out
target: dep.get_only_for_platform().map(|s| s.to_string()),
}
}).collect::<Vec<NewCrateDependency>>();
let manifest = pkg.get_manifest();
Expand Down
5 changes: 3 additions & 2 deletions src/cargo/sources/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,10 +417,11 @@ impl<'a, 'b> RegistrySource<'a, 'b> {

let dep = try!(Dependency::parse(name.as_slice(), Some(req.as_slice()),
&self.source_id));
drop(target); // FIXME: pass this in

Ok(dep.optional(optional)
.default_features(default_features)
.features(features))
.features(features)
.only_for_platform(target))
}

/// Actually perform network operations to update the registry
Expand Down
19 changes: 19 additions & 0 deletions src/cargo/util/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::string;
use serialize::{Encodable,Encoder};
use toml;
use core::MultiShell;
use ops;
use util::{CargoResult, ChainError, Require, internal, human};

use util::toml as cargo_toml;
Expand All @@ -18,6 +19,9 @@ pub struct Config<'a> {
target: Option<string::String>,
linker: Option<string::String>,
ar: Option<string::String>,
rustc_version: string::String,
/// The current host and default target of rustc
rustc_host: string::String,
}

impl<'a> Config<'a> {
Expand All @@ -27,6 +31,9 @@ impl<'a> Config<'a> {
if jobs == Some(0) {
return Err(human("jobs must be at least 1"))
}

let (rustc_version, rustc_host) = try!(ops::rustc_version());

Ok(Config {
home_path: try!(os::homedir().require(|| {
human("Cargo couldn't find your home directory. \
Expand All @@ -37,6 +44,8 @@ impl<'a> Config<'a> {
target: target,
ar: None,
linker: None,
rustc_version: rustc_version,
rustc_host: rustc_host,
})
}

Expand Down Expand Up @@ -84,6 +93,16 @@ impl<'a> Config<'a> {
pub fn ar(&self) -> Option<&str> {
self.ar.as_ref().map(|t| t.as_slice())
}

/// Return the output of `rustc -v verbose`
pub fn rustc_version(&self) -> &str {
self.rustc_version.as_slice()
}

/// Return the host platform and default target of rustc
pub fn rustc_host(&self) -> &str {
self.rustc_host.as_slice()
}
}

#[deriving(Eq,PartialEq,Clone,Encodable,Decodable)]
Expand Down
Loading

0 comments on commit 7e78341

Please sign in to comment.