Skip to content

Commit 8fc3fd8

Browse files
committed
Auto merge of #2406 - alexcrichton:download-less, r=brson
Currently Cargo will download an entire resolution graph all at once when in fact most packages may not be relevant to a compilation. For example target-specific dependencies and dev-dependencies are unconditionally downloaded regardless of whether they're actually needed or not. This commit alters the internals of Cargo to avoid downloading everything immediately and just switches to lazily downloading packages. This involved adding a new `LazyCell` primitive (similar to the one in use on crates.io) and also propagates `CargoResult` in a few more locations. Overall this ended up being a pretty large refactoring so the commits are separated in bite-sized chunks as much as possible with the end goal being this PR itself. Closes #2394
2 parents 582dcb7 + f994592 commit 8fc3fd8

23 files changed

+396
-435
lines changed

src/cargo/core/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use self::package_id_spec::PackageIdSpec;
66
pub use self::registry::Registry;
77
pub use self::resolver::Resolve;
88
pub use self::shell::{Shell, MultiShell, ShellConfig, Verbosity, ColorConfig};
9-
pub use self::source::{Source, SourceId, SourceMap, SourceSet, GitReference};
9+
pub use self::source::{Source, SourceId, SourceMap, GitReference};
1010
pub use self::summary::Summary;
1111

1212
pub mod source;

src/cargo/core/package.rs

+39-70
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1+
use std::cell::{Ref, RefCell};
12
use std::collections::HashMap;
23
use std::fmt;
34
use std::hash;
4-
use std::slice;
55
use std::path::{Path, PathBuf};
6+
67
use semver::Version;
78

8-
use core::{Dependency, Manifest, PackageId, SourceId, Registry, Target, Summary, Metadata};
9+
use core::{Dependency, Manifest, PackageId, SourceId, Target};
10+
use core::{Summary, Metadata, SourceMap};
911
use ops;
10-
use util::{CargoResult, graph, Config};
12+
use util::{CargoResult, Config, LazyCell, ChainError, internal, human};
1113
use rustc_serialize::{Encoder,Encodable};
12-
use core::source::Source;
1314

1415
/// Information about a package that is available somewhere in the file system.
1516
///
@@ -118,78 +119,46 @@ impl hash::Hash for Package {
118119
}
119120
}
120121

121-
#[derive(PartialEq,Clone,Debug)]
122-
pub struct PackageSet {
123-
packages: Vec<Package>,
122+
pub struct PackageSet<'cfg> {
123+
packages: Vec<(PackageId, LazyCell<Package>)>,
124+
sources: RefCell<SourceMap<'cfg>>,
124125
}
125126

126-
impl PackageSet {
127-
pub fn new(packages: &[Package]) -> PackageSet {
128-
//assert!(packages.len() > 0,
129-
// "PackageSet must be created with at least one package")
130-
PackageSet { packages: packages.to_vec() }
131-
}
132-
133-
pub fn is_empty(&self) -> bool {
134-
self.packages.is_empty()
135-
}
136-
137-
pub fn len(&self) -> usize {
138-
self.packages.len()
139-
}
140-
141-
pub fn pop(&mut self) -> Package {
142-
self.packages.pop().expect("PackageSet.pop: empty set")
143-
}
144-
145-
/// Get a package by name out of the set
146-
pub fn get(&self, name: &str) -> &Package {
147-
self.packages.iter().find(|pkg| name == pkg.name())
148-
.expect("PackageSet.get: empty set")
149-
}
150-
151-
pub fn get_all(&self, names: &[&str]) -> Vec<&Package> {
152-
names.iter().map(|name| self.get(*name) ).collect()
153-
}
154-
155-
pub fn packages(&self) -> &[Package] { &self.packages }
156-
157-
// For now, assume that the package set contains only one package with a
158-
// given name
159-
pub fn sort(&self) -> Option<PackageSet> {
160-
let mut graph = graph::Graph::new();
161-
162-
for pkg in self.packages.iter() {
163-
let deps: Vec<&str> = pkg.dependencies().iter()
164-
.map(|dep| dep.name())
165-
.collect();
166-
167-
graph.add(pkg.name(), &deps);
127+
impl<'cfg> PackageSet<'cfg> {
128+
pub fn new(package_ids: &[PackageId],
129+
sources: SourceMap<'cfg>) -> PackageSet<'cfg> {
130+
PackageSet {
131+
packages: package_ids.iter().map(|id| {
132+
(id.clone(), LazyCell::new(None))
133+
}).collect(),
134+
sources: RefCell::new(sources),
168135
}
169-
170-
let pkgs = match graph.sort() {
171-
Some(pkgs) => pkgs,
172-
None => return None,
173-
};
174-
let pkgs = pkgs.iter().map(|name| {
175-
self.get(*name).clone()
176-
}).collect();
177-
178-
Some(PackageSet {
179-
packages: pkgs
180-
})
181136
}
182137

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

188-
impl Registry for PackageSet {
189-
fn query(&mut self, name: &Dependency) -> CargoResult<Vec<Summary>> {
190-
Ok(self.packages.iter()
191-
.filter(|pkg| name.name() == pkg.name())
192-
.map(|pkg| pkg.summary().clone())
193-
.collect())
142+
pub fn get(&self, id: &PackageId) -> CargoResult<&Package> {
143+
let slot = try!(self.packages.iter().find(|p| p.0 == *id).chain_error(|| {
144+
internal(format!("couldn't find `{}` in package set", id))
145+
}));
146+
let slot = &slot.1;
147+
if let Some(pkg) = slot.borrow() {
148+
return Ok(pkg)
149+
}
150+
let mut sources = self.sources.borrow_mut();
151+
let source = try!(sources.get_mut(id.source_id()).chain_error(|| {
152+
internal(format!("couldn't find source for `{}`", id))
153+
}));
154+
let pkg = try!(source.download(id).chain_error(|| {
155+
human("unable to get packages from source")
156+
}));
157+
assert!(slot.fill(pkg).is_ok());
158+
Ok(slot.borrow().unwrap())
159+
}
160+
161+
pub fn sources(&self) -> Ref<SourceMap<'cfg>> {
162+
self.sources.borrow()
194163
}
195164
}

src/cargo/core/registry.rs

+4-25
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use std::collections::HashSet;
2-
use std::collections::hash_map::HashMap;
1+
use std::collections::{HashSet, HashMap};
32

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

77
/// Source of information about a group of packages.
@@ -85,30 +85,9 @@ impl<'cfg> PackageRegistry<'cfg> {
8585
}
8686
}
8787

88-
pub fn get(&mut self, package_ids: &[PackageId]) -> CargoResult<Vec<Package>> {
88+
pub fn get(self, package_ids: &[PackageId]) -> PackageSet<'cfg> {
8989
trace!("getting packages; sources={}", self.sources.len());
90-
91-
// TODO: Only call source with package ID if the package came from the
92-
// source
93-
let mut ret = Vec::new();
94-
95-
for (_, source) in self.sources.sources_mut() {
96-
try!(source.download(package_ids));
97-
let packages = try!(source.get(package_ids));
98-
99-
ret.extend(packages.into_iter());
100-
}
101-
102-
// TODO: Return earlier if fail
103-
assert!(package_ids.len() == ret.len(),
104-
"could not get packages from registry; ids={:?}; ret={:?}",
105-
package_ids, ret);
106-
107-
Ok(ret)
108-
}
109-
110-
pub fn move_sources(self) -> SourceMap<'cfg> {
111-
self.sources
90+
PackageSet::new(package_ids, self.sources)
11291
}
11392

11493
fn ensure_loaded(&mut self, namespace: &SourceId, kind: Kind) -> CargoResult<()> {

src/cargo/core/source.rs

+2-67
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
99

1010
use url::Url;
1111

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

2525
/// The download method fetches the full package for each name and
2626
/// version specified.
27-
fn download(&mut self, packages: &[PackageId]) -> CargoResult<()>;
28-
29-
/// The get method returns the Path of each specified package on the
30-
/// local file system. It assumes that `download` was already called,
31-
/// and that the packages are already locally available on the file
32-
/// system.
33-
fn get(&self, packages: &[PackageId]) -> CargoResult<Vec<Package>>;
27+
fn download(&mut self, package: &PackageId) -> CargoResult<Package>;
3428

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

446-
/// List of `Source` implementors. `SourceSet` itself implements `Source`.
447-
pub struct SourceSet<'src> {
448-
sources: Vec<Box<Source+'src>>
449-
}
450-
451-
impl<'src> SourceSet<'src> {
452-
pub fn new(sources: Vec<Box<Source+'src>>) -> SourceSet<'src> {
453-
SourceSet { sources: sources }
454-
}
455-
}
456-
457-
impl<'src> Registry for SourceSet<'src> {
458-
fn query(&mut self, name: &Dependency) -> CargoResult<Vec<Summary>> {
459-
let mut ret = Vec::new();
460-
461-
for source in self.sources.iter_mut() {
462-
ret.extend(try!(source.query(name)).into_iter());
463-
}
464-
465-
Ok(ret)
466-
}
467-
}
468-
469-
impl<'src> Source for SourceSet<'src> {
470-
fn update(&mut self) -> CargoResult<()> {
471-
for source in self.sources.iter_mut() {
472-
try!(source.update());
473-
}
474-
475-
Ok(())
476-
}
477-
478-
fn download(&mut self, packages: &[PackageId]) -> CargoResult<()> {
479-
for source in self.sources.iter_mut() {
480-
try!(source.download(packages));
481-
}
482-
483-
Ok(())
484-
}
485-
486-
fn get(&self, packages: &[PackageId]) -> CargoResult<Vec<Package>> {
487-
let mut ret = Vec::new();
488-
489-
for source in self.sources.iter() {
490-
ret.extend(try!(source.get(packages)).into_iter());
491-
}
492-
493-
Ok(ret)
494-
}
495-
496-
fn fingerprint(&self, id: &Package) -> CargoResult<String> {
497-
let mut ret = String::new();
498-
for source in self.sources.iter() {
499-
ret.push_str(&try!(source.fingerprint(id))[..]);
500-
}
501-
Ok(ret)
502-
}
503-
}
504-
505440
#[cfg(test)]
506441
mod tests {
507442
use super::{SourceId, Kind, GitReference};

src/cargo/ops/cargo_clean.rs

+6-24
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@ use std::default::Default;
22
use std::fs;
33
use std::path::Path;
44

5-
use core::{Package, PackageSet, Profiles};
6-
use core::source::{Source, SourceMap};
7-
use core::registry::PackageRegistry;
5+
use core::{Package, Profiles};
6+
use core::source::Source;
87
use util::{CargoResult, human, ChainError, Config};
98
use ops::{self, Layout, Context, BuildConfig, Kind, Unit};
109

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

29-
// Load the lockfile (if one's available)
30-
let lockfile = root.root().join("Cargo.lock");
31-
let source_id = root.package_id().source_id();
32-
let resolve = match try!(ops::load_lockfile(&lockfile, source_id)) {
33-
Some(resolve) => resolve,
34-
None => bail!("a Cargo.lock must exist before cleaning")
35-
};
36-
37-
// Create a compilation context to have access to information like target
38-
// filenames and such
39-
let srcs = SourceMap::new();
40-
let pkgs = PackageSet::new(&[]);
28+
let (resolve, packages) = try!(ops::fetch(manifest_path, opts.config));
4129

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

48-
let cx = try!(Context::new(&resolve, &srcs, &pkgs, opts.config,
36+
let cx = try!(Context::new(&resolve, &packages, opts.config,
4937
host_layout, target_layout,
5038
BuildConfig::default(),
5139
root.manifest().profiles()));
5240

53-
let mut registry = PackageRegistry::new(opts.config);
54-
5541
// resolve package specs and remove the corresponding packages
5642
for spec in opts.spec {
43+
// Translate the spec to a Package
5744
let pkgid = try!(resolve.query(spec));
58-
59-
// Translate the PackageId to a Package
60-
let pkg = {
61-
try!(registry.add_sources(&[pkgid.source_id().clone()]));
62-
(try!(registry.get(&[pkgid.clone()]))).into_iter().next().unwrap()
63-
};
45+
let pkg = try!(packages.get(&pkgid));
6446

6547
// And finally, clean everything out!
6648
for target in pkg.targets() {

src/cargo/ops/cargo_compile.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use std::path::{Path, PathBuf};
2828
use std::sync::Arc;
2929

3030
use core::registry::PackageRegistry;
31-
use core::{Source, SourceId, SourceMap, PackageSet, Package, Target};
31+
use core::{Source, SourceId, PackageSet, Package, Target};
3232
use core::{Profile, TargetKind, Profiles};
3333
use core::resolver::{Method, Resolve};
3434
use ops::{self, BuildOutput, ExecEngine};
@@ -102,7 +102,7 @@ pub fn resolve_dependencies<'a>(root_package: &Package,
102102
source: Option<Box<Source + 'a>>,
103103
features: Vec<String>,
104104
no_default_features: bool)
105-
-> CargoResult<(Vec<Package>, Resolve, SourceMap<'a>)> {
105+
-> CargoResult<(PackageSet<'a>, Resolve)> {
106106

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

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

136-
let packages = try!(ops::get_resolved_packages(&resolved_with_overrides,
137-
&mut registry));
136+
let packages = ops::get_resolved_packages(&resolved_with_overrides,
137+
registry);
138138

139-
Ok((packages, resolved_with_overrides, registry.move_sources()))
139+
Ok((packages, resolved_with_overrides))
140140
}
141141

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

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

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

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

247248
try!(ops::compile_targets(&package_targets,
248-
&PackageSet::new(&packages),
249+
&packages,
249250
&resolve_with_overrides,
250-
&sources,
251251
config,
252252
build_config,
253253
root_package.manifest().profiles(),

0 commit comments

Comments
 (0)