Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New custom build] Implement platform-specific dependencies #735

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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