use command_prelude::*;
use cargo::ops;
+use cargo::ops::FetchOptions;
pub fn cli() -> App {
subcommand("fetch")
.about("Fetch dependencies of a package from the network")
.arg_manifest_path()
+ .arg_target_triple("Fetch dependencies for the target triple")
.after_help(
"\
If a lockfile is available, this command will ensure that all of the git
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
- ops::fetch(&ws)?;
+
+ let opts = FetchOptions {
+ config,
+ target: args.target(),
+ };
+ ops::fetch(&ws, &opts)?;
Ok(())
}
pub use self::compilation_files::Metadata;
mod target_info;
-pub use self::target_info::FileFlavor;
-use self::target_info::TargetInfo;
+pub use self::target_info::{FileFlavor, TargetInfo};
/// All information needed to define a Unit.
///
use self::output_depinfo::output_depinfo;
pub use self::compilation::Compilation;
-pub use self::context::{Context, FileFlavor, Unit};
+pub use self::context::{Context, FileFlavor, TargetInfo, Unit};
pub use self::custom_build::{BuildMap, BuildOutput, BuildScripts};
pub use self::layout::is_bad_artifact_name;
-use core::{PackageSet, Resolve, Workspace};
+use core::compiler::{BuildConfig, Kind, TargetInfo};
+use core::{Package, PackageId, PackageSet, Resolve, Workspace};
use ops;
+use std::collections::HashSet;
use util::CargoResult;
+use util::Config;
+
+pub struct FetchOptions<'a> {
+ pub config: &'a Config,
+ /// The target arch triple to fetch dependencies for
+ pub target: Option<String>,
+}
/// Executes `cargo fetch`.
-pub fn fetch<'a>(ws: &Workspace<'a>) -> CargoResult<(Resolve, PackageSet<'a>)> {
+pub fn fetch<'a>(
+ ws: &Workspace<'a>,
+ options: &FetchOptions<'a>,
+) -> CargoResult<(Resolve, PackageSet<'a>)> {
let (packages, resolve) = ops::resolve_ws(ws)?;
- for id in resolve.iter() {
- packages.get(id)?;
- }
+
+ fetch_for_target(ws, options.config, &options.target, &resolve, &packages)?;
+
Ok((resolve, packages))
}
+
+fn fetch_for_target<'a, 'cfg: 'a>(
+ ws: &'a Workspace<'cfg>,
+ config: &'cfg Config,
+ target: &Option<String>,
+ resolve: &'a Resolve,
+ packages: &'a PackageSet<'cfg>,
+) -> CargoResult<HashSet<&'a PackageId>> {
+ let mut fetched_packages = HashSet::new();
+ let mut deps_to_fetch = Vec::new();
+ let jobs = Some(1);
+ let build_config = BuildConfig::new(config, jobs, target, None)?;
+ let target_info = TargetInfo::new(config, &build_config, Kind::Target)?;
+ let root_package_ids = ws.members().map(Package::package_id).collect::<Vec<_>>();
+
+ deps_to_fetch.extend(root_package_ids);
+
+ while let Some(id) = deps_to_fetch.pop() {
+ if !fetched_packages.insert(id) {
+ continue;
+ }
+
+ let package = packages.get(id)?;
+ let deps = resolve.deps(id);
+ let dependency_ids = deps.filter(|dep| {
+ package
+ .dependencies()
+ .iter()
+ .filter(|d| d.name() == dep.name() && d.version_req().matches(dep.version()))
+ .any(|d| {
+ // If no target was specified then all dependencies can be fetched.
+ let target = match *target {
+ Some(ref t) => t,
+ None => return true,
+ };
+ // If this dependency is only available for certain platforms,
+ // make sure we're only fetching it for that platform.
+ let platform = match d.platform() {
+ Some(p) => p,
+ None => return true,
+ };
+ platform.matches(target, target_info.cfg())
+ })
+ }).collect::<Vec<_>>();
+
+ deps_to_fetch.extend(dependency_ids);
+ }
+
+ Ok(fetched_packages)
+}
pub use self::registry::{http_handle, needs_custom_http_transport, registry_login, search};
pub use self::registry::{modify_owners, yank, OwnersOptions, PublishOpts};
pub use self::registry::configure_http_handle;
-pub use self::cargo_fetch::fetch;
+pub use self::cargo_fetch::{fetch, FetchOptions};
pub use self::cargo_pkgid::pkgid;
pub use self::resolve::{resolve_with_previous, resolve_ws, resolve_ws_precisely,
resolve_ws_with_method};
-use cargotest::support::{execs, project};
+use cargotest::rustc_host;
+use cargotest::support::registry::Package;
+use cargotest::support::{cross_compile, execs, project};
use hamcrest::assert_that;
#[test]
assert_that(p.cargo("fetch"), execs().with_status(0).with_stdout(""));
}
+
+#[test]
+fn fetch_all_platform_dependencies_when_no_target_is_given() {
+ Package::new("d1", "1.2.3")
+ .file(
+ "Cargo.toml",
+ r#"
+ [project]
+ name = "d1"
+ version = "1.2.3"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .publish();
+
+ Package::new("d2", "0.1.2")
+ .file(
+ "Cargo.toml",
+ r#"
+ [project]
+ name = "d2"
+ version = "0.1.2"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .publish();
+
+ let target = cross_compile::alternate();
+ let host = rustc_host();
+ let p = project("foo")
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [target.{host}.dependencies]
+ d1 = "1.2.3"
+
+ [target.{target}.dependencies]
+ d2 = "0.1.2"
+ "#,
+ host = host,
+ target = target
+ ),
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ assert_that(
+ p.cargo("fetch"),
+ execs()
+ .with_status(0)
+ .with_stderr_contains("[..] Downloading d1 v1.2.3 [..]")
+ .with_stderr_contains("[..] Downloading d2 v0.1.2 [..]"),
+ );
+}
+
+#[test]
+fn fetch_platform_specific_dependencies() {
+ Package::new("d1", "1.2.3")
+ .file(
+ "Cargo.toml",
+ r#"
+ [project]
+ name = "d1"
+ version = "1.2.3"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .publish();
+
+ Package::new("d2", "0.1.2")
+ .file(
+ "Cargo.toml",
+ r#"
+ [project]
+ name = "d2"
+ version = "0.1.2"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .publish();
+
+ let target = cross_compile::alternate();
+ let host = rustc_host();
+ let p = project("foo")
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [target.{host}.dependencies]
+ d1 = "1.2.3"
+
+ [target.{target}.dependencies]
+ d2 = "0.1.2"
+ "#,
+ host = host,
+ target = target
+ ),
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ assert_that(
+ p.cargo("fetch").arg("--target").arg(&host),
+ execs()
+ .with_status(0)
+ .with_stderr_contains("[..] Downloading d1 v1.2.3 [..]")
+ .with_stderr_does_not_contain("[..] Downloading d2 v0.1.2 [..]"),
+ );
+
+ assert_that(
+ p.cargo("fetch").arg("--target").arg(&target),
+ execs()
+ .with_status(0)
+ .with_stderr_contains("[..] Downloading d2 v0.1.2[..]")
+ .with_stderr_does_not_contain("[..] Downloading d1 v1.2.3 [..]"),
+ );
+}