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

Lazily download packages #2406

Merged
merged 11 commits into from
Feb 29, 2016
2 changes: 1 addition & 1 deletion src/cargo/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub use self::package_id_spec::PackageIdSpec;
pub use self::registry::Registry;
pub use self::resolver::Resolve;
pub use self::shell::{Shell, MultiShell, ShellConfig, Verbosity, ColorConfig};
pub use self::source::{Source, SourceId, SourceMap, SourceSet, GitReference};
pub use self::source::{Source, SourceId, SourceMap, GitReference};
pub use self::summary::Summary;

pub mod source;
Expand Down
109 changes: 39 additions & 70 deletions src/cargo/core/package.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use std::cell::{Ref, RefCell};
use std::collections::HashMap;
use std::fmt;
use std::hash;
use std::slice;
use std::path::{Path, PathBuf};

use semver::Version;

use core::{Dependency, Manifest, PackageId, SourceId, Registry, Target, Summary, Metadata};
use core::{Dependency, Manifest, PackageId, SourceId, Target};
use core::{Summary, Metadata, SourceMap};
use ops;
use util::{CargoResult, graph, Config};
use util::{CargoResult, Config, LazyCell, ChainError, internal, human};
use rustc_serialize::{Encoder,Encodable};
use core::source::Source;

/// Information about a package that is available somewhere in the file system.
///
Expand Down Expand Up @@ -118,78 +119,46 @@ impl hash::Hash for Package {
}
}

#[derive(PartialEq,Clone,Debug)]
pub struct PackageSet {
packages: Vec<Package>,
pub struct PackageSet<'cfg> {
packages: Vec<(PackageId, LazyCell<Package>)>,
sources: RefCell<SourceMap<'cfg>>,
}

impl PackageSet {
pub fn new(packages: &[Package]) -> PackageSet {
//assert!(packages.len() > 0,
// "PackageSet must be created with at least one package")
PackageSet { packages: packages.to_vec() }
}

pub fn is_empty(&self) -> bool {
self.packages.is_empty()
}

pub fn len(&self) -> usize {
self.packages.len()
}

pub fn pop(&mut self) -> Package {
self.packages.pop().expect("PackageSet.pop: empty set")
}

/// Get a package by name out of the set
pub fn get(&self, name: &str) -> &Package {
self.packages.iter().find(|pkg| name == pkg.name())
.expect("PackageSet.get: empty set")
}

pub fn get_all(&self, names: &[&str]) -> Vec<&Package> {
names.iter().map(|name| self.get(*name) ).collect()
}

pub fn packages(&self) -> &[Package] { &self.packages }

// For now, assume that the package set contains only one package with a
// given name
pub fn sort(&self) -> Option<PackageSet> {
let mut graph = graph::Graph::new();

for pkg in self.packages.iter() {
let deps: Vec<&str> = pkg.dependencies().iter()
.map(|dep| dep.name())
.collect();

graph.add(pkg.name(), &deps);
impl<'cfg> PackageSet<'cfg> {
pub fn new(package_ids: &[PackageId],
sources: SourceMap<'cfg>) -> PackageSet<'cfg> {
PackageSet {
packages: package_ids.iter().map(|id| {
(id.clone(), LazyCell::new(None))
}).collect(),
sources: RefCell::new(sources),
}

let pkgs = match graph.sort() {
Some(pkgs) => pkgs,
None => return None,
};
let pkgs = pkgs.iter().map(|name| {
self.get(*name).clone()
}).collect();

Some(PackageSet {
packages: pkgs
})
}

pub fn iter(&self) -> slice::Iter<Package> {
self.packages.iter()
pub fn package_ids<'a>(&'a self) -> Box<Iterator<Item=&'a PackageId> + 'a> {
Box::new(self.packages.iter().map(|&(ref p, _)| p))
}
}

impl Registry for PackageSet {
fn query(&mut self, name: &Dependency) -> CargoResult<Vec<Summary>> {
Ok(self.packages.iter()
.filter(|pkg| name.name() == pkg.name())
.map(|pkg| pkg.summary().clone())
.collect())
pub fn get(&self, id: &PackageId) -> CargoResult<&Package> {
let slot = try!(self.packages.iter().find(|p| p.0 == *id).chain_error(|| {
internal(format!("couldn't find `{}` in package set", id))
}));
let slot = &slot.1;
if let Some(pkg) = slot.borrow() {
return Ok(pkg)
}
let mut sources = self.sources.borrow_mut();
let source = try!(sources.get_mut(id.source_id()).chain_error(|| {
internal(format!("couldn't find source for `{}`", id))
}));
let pkg = try!(source.download(id).chain_error(|| {
human("unable to get packages from source")
}));
assert!(slot.fill(pkg).is_ok());
Ok(slot.borrow().unwrap())
}

pub fn sources(&self) -> Ref<SourceMap<'cfg>> {
self.sources.borrow()
}
}
29 changes: 4 additions & 25 deletions src/cargo/core/registry.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::collections::HashSet;
use std::collections::hash_map::HashMap;
use std::collections::{HashSet, HashMap};

use core::{Source, SourceId, SourceMap, Summary, Dependency, PackageId, Package};
use core::PackageSet;
use util::{CargoResult, ChainError, Config, human, profile};

/// Source of information about a group of packages.
Expand Down Expand Up @@ -85,30 +85,9 @@ impl<'cfg> PackageRegistry<'cfg> {
}
}

pub fn get(&mut self, package_ids: &[PackageId]) -> CargoResult<Vec<Package>> {
pub fn get(self, package_ids: &[PackageId]) -> PackageSet<'cfg> {
trace!("getting packages; sources={}", self.sources.len());

// TODO: Only call source with package ID if the package came from the
// source
let mut ret = Vec::new();

for (_, source) in self.sources.sources_mut() {
try!(source.download(package_ids));
let packages = try!(source.get(package_ids));

ret.extend(packages.into_iter());
}

// TODO: Return earlier if fail
assert!(package_ids.len() == ret.len(),
"could not get packages from registry; ids={:?}; ret={:?}",
package_ids, ret);

Ok(ret)
}

pub fn move_sources(self) -> SourceMap<'cfg> {
self.sources
PackageSet::new(package_ids, self.sources)
}

fn ensure_loaded(&mut self, namespace: &SourceId, kind: Kind) -> CargoResult<()> {
Expand Down
69 changes: 2 additions & 67 deletions src/cargo/core/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};

use url::Url;

use core::{Summary, Package, PackageId, Registry, Dependency};
use core::{Package, PackageId, Registry};
use sources::{PathSource, GitSource, RegistrySource};
use sources::git;
use util::{human, Config, CargoResult, ToUrl};
Expand All @@ -24,13 +24,7 @@ pub trait Source: Registry {

/// The download method fetches the full package for each name and
/// version specified.
fn download(&mut self, packages: &[PackageId]) -> CargoResult<()>;

/// The get method returns the Path of each specified package on the
/// local file system. It assumes that `download` was already called,
/// and that the packages are already locally available on the file
/// system.
fn get(&self, packages: &[PackageId]) -> CargoResult<Vec<Package>>;
fn download(&mut self, package: &PackageId) -> CargoResult<Package>;

/// Generates a unique string which represents the fingerprint of the
/// current state of the source.
Expand Down Expand Up @@ -443,65 +437,6 @@ impl<'a, 'src> Iterator for SourcesMut<'a, 'src> {
}
}

/// List of `Source` implementors. `SourceSet` itself implements `Source`.
pub struct SourceSet<'src> {
sources: Vec<Box<Source+'src>>
}

impl<'src> SourceSet<'src> {
pub fn new(sources: Vec<Box<Source+'src>>) -> SourceSet<'src> {
SourceSet { sources: sources }
}
}

impl<'src> Registry for SourceSet<'src> {
fn query(&mut self, name: &Dependency) -> CargoResult<Vec<Summary>> {
let mut ret = Vec::new();

for source in self.sources.iter_mut() {
ret.extend(try!(source.query(name)).into_iter());
}

Ok(ret)
}
}

impl<'src> Source for SourceSet<'src> {
fn update(&mut self) -> CargoResult<()> {
for source in self.sources.iter_mut() {
try!(source.update());
}

Ok(())
}

fn download(&mut self, packages: &[PackageId]) -> CargoResult<()> {
for source in self.sources.iter_mut() {
try!(source.download(packages));
}

Ok(())
}

fn get(&self, packages: &[PackageId]) -> CargoResult<Vec<Package>> {
let mut ret = Vec::new();

for source in self.sources.iter() {
ret.extend(try!(source.get(packages)).into_iter());
}

Ok(ret)
}

fn fingerprint(&self, id: &Package) -> CargoResult<String> {
let mut ret = String::new();
for source in self.sources.iter() {
ret.push_str(&try!(source.fingerprint(id))[..]);
}
Ok(ret)
}
}

#[cfg(test)]
mod tests {
use super::{SourceId, Kind, GitReference};
Expand Down
30 changes: 6 additions & 24 deletions src/cargo/ops/cargo_clean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ use std::default::Default;
use std::fs;
use std::path::Path;

use core::{Package, PackageSet, Profiles};
use core::source::{Source, SourceMap};
use core::registry::PackageRegistry;
use core::{Package, Profiles};
use core::source::Source;
use util::{CargoResult, human, ChainError, Config};
use ops::{self, Layout, Context, BuildConfig, Kind, Unit};

Expand All @@ -26,41 +25,24 @@ pub fn clean(manifest_path: &Path, opts: &CleanOptions) -> CargoResult<()> {
return rm_rf(&target_dir);
}

// Load the lockfile (if one's available)
let lockfile = root.root().join("Cargo.lock");
let source_id = root.package_id().source_id();
let resolve = match try!(ops::load_lockfile(&lockfile, source_id)) {
Some(resolve) => resolve,
None => bail!("a Cargo.lock must exist before cleaning")
};

// Create a compilation context to have access to information like target
// filenames and such
let srcs = SourceMap::new();
let pkgs = PackageSet::new(&[]);
let (resolve, packages) = try!(ops::fetch(manifest_path, opts.config));

let dest = if opts.release {"release"} else {"debug"};
let host_layout = Layout::new(opts.config, &root, None, dest);
let target_layout = opts.target.map(|target| {
Layout::new(opts.config, &root, Some(target), dest)
});

let cx = try!(Context::new(&resolve, &srcs, &pkgs, opts.config,
let cx = try!(Context::new(&resolve, &packages, opts.config,
host_layout, target_layout,
BuildConfig::default(),
root.manifest().profiles()));

let mut registry = PackageRegistry::new(opts.config);

// resolve package specs and remove the corresponding packages
for spec in opts.spec {
// Translate the spec to a Package
let pkgid = try!(resolve.query(spec));

// Translate the PackageId to a Package
let pkg = {
try!(registry.add_sources(&[pkgid.source_id().clone()]));
(try!(registry.get(&[pkgid.clone()]))).into_iter().next().unwrap()
};
let pkg = try!(packages.get(&pkgid));

// And finally, clean everything out!
for target in pkg.targets() {
Expand Down
20 changes: 10 additions & 10 deletions src/cargo/ops/cargo_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use std::path::{Path, PathBuf};
use std::sync::Arc;

use core::registry::PackageRegistry;
use core::{Source, SourceId, SourceMap, PackageSet, Package, Target};
use core::{Source, SourceId, PackageSet, Package, Target};
use core::{Profile, TargetKind, Profiles};
use core::resolver::{Method, Resolve};
use ops::{self, BuildOutput, ExecEngine};
Expand Down Expand Up @@ -102,7 +102,7 @@ pub fn resolve_dependencies<'a>(root_package: &Package,
source: Option<Box<Source + 'a>>,
features: Vec<String>,
no_default_features: bool)
-> CargoResult<(Vec<Package>, Resolve, SourceMap<'a>)> {
-> CargoResult<(PackageSet<'a>, Resolve)> {

let override_ids = try!(source_ids_from_config(config, root_package.root()));

Expand Down Expand Up @@ -133,10 +133,10 @@ pub fn resolve_dependencies<'a>(root_package: &Package,
try!(ops::resolve_with_previous(&mut registry, root_package,
method, Some(&resolve), None));

let packages = try!(ops::get_resolved_packages(&resolved_with_overrides,
&mut registry));
let packages = ops::get_resolved_packages(&resolved_with_overrides,
registry);

Ok((packages, resolved_with_overrides, registry.move_sources()))
Ok((packages, resolved_with_overrides))
}

pub fn compile_pkg<'a>(root_package: &Package,
Expand All @@ -158,7 +158,7 @@ pub fn compile_pkg<'a>(root_package: &Package,
bail!("jobs must be at least 1")
}

let (packages, resolve_with_overrides, sources) = {
let (packages, resolve_with_overrides) = {
try!(resolve_dependencies(root_package, config, source, features,
no_default_features))
};
Expand All @@ -180,8 +180,9 @@ pub fn compile_pkg<'a>(root_package: &Package,
invalid_spec.join(", "))
}

let to_builds = packages.iter().filter(|p| pkgids.contains(&p.package_id()))
.collect::<Vec<_>>();
let to_builds = try!(pkgids.iter().map(|id| {
packages.get(id)
}).collect::<CargoResult<Vec<_>>>());

let mut general_targets = Vec::new();
let mut package_targets = Vec::new();
Expand Down Expand Up @@ -245,9 +246,8 @@ pub fn compile_pkg<'a>(root_package: &Package,
}

try!(ops::compile_targets(&package_targets,
&PackageSet::new(&packages),
&packages,
&resolve_with_overrides,
&sources,
config,
build_config,
root_package.manifest().profiles(),
Expand Down
Loading