+use std::cell::{Ref, RefCell};
use std::collections::HashMap;
use std::fmt;
use std::hash;
-use std::iter;
use std::path::{Path, PathBuf};
-use std::slice;
+
use semver::Version;
use core::{Dependency, Manifest, PackageId, SourceId, Target};
use core::{Summary, Metadata, SourceMap};
use ops;
-use util::{CargoResult, Config};
+use util::{CargoResult, Config, LazyCell, ChainError, internal};
use rustc_serialize::{Encoder,Encodable};
/// Information about a package that is available somewhere in the file system.
}
pub struct PackageSet<'cfg> {
- packages: Vec<Package>,
- sources: SourceMap<'cfg>,
+ packages: Vec<(PackageId, LazyCell<Package>)>,
+ sources: RefCell<SourceMap<'cfg>>,
}
impl<'cfg> PackageSet<'cfg> {
pub fn new(packages: Vec<Package>,
sources: SourceMap<'cfg>) -> PackageSet<'cfg> {
PackageSet {
- packages: packages,
- sources: sources,
+ packages: packages.into_iter().map(|pkg| {
+ (pkg.package_id().clone(), LazyCell::new(Some(pkg)))
+ }).collect(),
+ sources: RefCell::new(sources),
}
}
- pub fn package_ids(&self) -> iter::Map<slice::Iter<Package>,
- fn(&Package) -> &PackageId> {
- let f = Package::package_id as fn(&Package) -> &PackageId;
- self.packages.iter().map(f)
+ pub fn package_ids<'a>(&'a self) -> Box<Iterator<Item=&'a PackageId> + 'a> {
+ Box::new(self.packages.iter().map(|&(ref p, _)| p))
}
- pub fn get(&self, id: &PackageId) -> &Package {
- self.packages.iter().find(|pkg| pkg.package_id() == id).unwrap()
+ 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));
+ assert!(slot.fill(pkg).is_ok());
+ Ok(slot.borrow().unwrap())
}
- pub fn sources(&self) -> &SourceMap<'cfg> {
- &self.sources
+ pub fn sources(&self) -> Ref<SourceMap<'cfg>> {
+ self.sources.borrow()
}
}
for spec in opts.spec {
// Translate the spec to a Package
let pkgid = try!(resolve.query(spec));
- let pkg = packages.get(&pkgid);
+ let pkg = try!(packages.get(&pkgid));
// And finally, clean everything out!
for target in pkg.targets() {
invalid_spec.join(", "))
}
- let to_builds = pkgids.iter().map(|id| packages.get(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();
opt.no_default_features));
let (packages, resolve) = deps;
- let packages = packages.package_ids()
- .map(|i| packages.get(i))
- .cloned()
- .collect();
+ let packages = try!(packages.package_ids()
+ .map(|i| packages.get(i).map(|p| p.clone()))
+ .collect());
Ok(ExportInfo {
packages: packages,
/// Gets a package for the given package id.
pub fn get_package(&self, id: &PackageId) -> &'a Package {
- self.packages.get(id)
+ // TODO: remove this unwrap() -- happens in a later commit
+ self.packages.get(id).unwrap()
}
/// Get the user-specified linker for a particular host or target
fn pkg_fingerprint(cx: &Context, pkg: &Package) -> CargoResult<String> {
let source_id = pkg.package_id().source_id();
- let source = try!(cx.packages.sources().get(source_id).chain_error(|| {
+ let sources = cx.packages.sources();
+ let source = try!(sources.get(source_id).chain_error(|| {
internal("missing package source")
}));
source.fingerprint(pkg)
use core::{PackageId, Target, Profile};
use util::{Config, DependencyQueue, Fresh, Dirty, Freshness};
-use util::{CargoResult, Dependency, profile, internal};
+use util::{CargoResult, profile, internal};
use super::{Context, Kind, Unit};
use super::job::Job;
}
}
- pub fn enqueue(&mut self, cx: &Context<'a, 'a>,
- unit: &Unit<'a>, job: Job, fresh: Freshness) {
+ pub fn enqueue<'cfg>(&mut self, cx: &Context<'a, 'cfg>,
+ unit: &Unit<'a>, job: Job, fresh: Freshness) {
let key = Key::new(unit);
- self.queue.queue(cx, Fresh, key, Vec::new()).push((job, fresh));
+ self.queue.queue(Fresh,
+ key,
+ Vec::new(),
+ &key.dependencies(cx)).push((job, fresh));
*self.counts.entry(key.pkg).or_insert(0) += 1;
}
}
}
-impl<'a> Dependency for Key<'a> {
- type Context = Context<'a, 'a>;
+impl<'a> Key<'a> {
+ fn new(unit: &Unit<'a>) -> Key<'a> {
+ Key {
+ pkg: unit.pkg.package_id(),
+ target: unit.target,
+ profile: unit.profile,
+ kind: unit.kind,
+ }
+ }
- fn dependencies(&self, cx: &Context<'a, 'a>) -> Vec<Key<'a>> {
+ fn dependencies<'cfg>(&self, cx: &Context<'a, 'cfg>) -> Vec<Key<'a>> {
let unit = Unit {
pkg: cx.get_package(self.pkg),
target: self.target,
}
}
-impl<'a> Key<'a> {
- fn new(unit: &Unit<'a>) -> Key<'a> {
- Key {
- pkg: unit.pkg.package_id(),
- target: unit.target,
- profile: unit.profile,
- kind: unit.kind,
- }
- }
-}
-
impl<'a> fmt::Debug for Key<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} => {}/{} => {:?}", self.pkg, self.target, self.profile,
}).collect::<Vec<_>>();
let dest = if build_config.release {"release"} else {"debug"};
- let root = packages.get(resolve.root());
+ let root = try!(packages.get(resolve.root()));
let host_layout = Layout::new(config, root, None, &dest);
let target_layout = build_config.requested_target.as_ref().map(|target| {
layout::Layout::new(config, root, Some(&target), &dest)
Dirty,
}
-/// A trait for discovering the dependencies of a piece of data.
-pub trait Dependency: Hash + Eq + Clone {
- type Context;
- fn dependencies(&self, cx: &Self::Context) -> Vec<Self>;
-}
-
impl Freshness {
pub fn combine(&self, other: Freshness) -> Freshness {
match *self { Fresh => other, Dirty => Dirty }
}
}
-impl<K: Dependency, V> DependencyQueue<K, V> {
+impl<K: Hash + Eq + Clone, V> DependencyQueue<K, V> {
/// Creates a new dependency queue with 0 packages.
pub fn new() -> DependencyQueue<K, V> {
DependencyQueue {
///
/// It is assumed that any dependencies of this package will eventually also
/// be added to the dependency queue.
- pub fn queue(&mut self, cx: &K::Context, fresh: Freshness,
- key: K, value: V) -> &mut V {
+ pub fn queue(&mut self,
+ fresh: Freshness,
+ key: K,
+ value: V,
+ dependencies: &[K]) -> &mut V {
let slot = match self.dep_map.entry(key.clone()) {
Occupied(v) => return &mut v.into_mut().1,
Vacant(v) => v,
}
let mut my_dependencies = HashSet::new();
- for dep in key.dependencies(cx).into_iter() {
+ for dep in dependencies {
assert!(my_dependencies.insert(dep.clone()));
- let rev = self.reverse_dep_map.entry(dep).or_insert(HashSet::new());
+ let rev = self.reverse_dep_map.entry(dep.clone())
+ .or_insert(HashSet::new());
assert!(rev.insert(key.clone()));
}
&mut slot.insert((my_dependencies, value)).1
--- /dev/null
+//! A lazily fill Cell, but with frozen contents.
+//!
+//! With a `RefCell`, the inner contents cannot be borrowed for the lifetime of
+//! the entire object, but only of the borrows returned. A `LazyCell` is a
+//! variation on `RefCell` which allows borrows tied to the lifetime of the
+//! outer object.
+//!
+//! The limitation of a `LazyCell` is that after initialized, it can never be
+//! modified.
+
+use std::cell::UnsafeCell;
+
+pub struct LazyCell<T> {
+ inner: UnsafeCell<Option<T>>,
+}
+
+impl<T> LazyCell<T> {
+ /// Creates a new empty lazy cell.
+ pub fn new(init: Option<T>) -> LazyCell<T> {
+ LazyCell { inner: UnsafeCell::new(init) }
+ }
+
+ /// Put a value into this cell.
+ ///
+ /// This function will fail if the cell has already been filled.
+ pub fn fill(&self, t: T) -> Result<(), T> {
+ unsafe {
+ let slot = self.inner.get();
+ if (*slot).is_none() {
+ *slot = Some(t);
+ Ok(())
+ } else {
+ Err(t)
+ }
+ }
+ }
+
+ /// Borrows the contents of this lazy cell for the duration of the cell
+ /// itself.
+ ///
+ /// This function will return `Some` if the cell has been previously
+ /// initialized, and `None` if it has not yet been initialized.
+ pub fn borrow(&self) -> Option<&T> {
+ unsafe {
+ (*self.inner.get()).as_ref()
+ }
+ }
+
+ /// Consumes this `LazyCell`, returning the underlying value.
+ pub fn into_inner(self) -> Option<T> {
+ unsafe {
+ self.inner.into_inner()
+ }
+ }
+}
pub use self::cfg::{Cfg, CfgExpr};
pub use self::config::Config;
-pub use self::dependency_queue::Dependency;
pub use self::dependency_queue::{DependencyQueue, Fresh, Dirty, Freshness};
pub use self::errors::{CargoResult, CargoError, ChainError, CliResult};
pub use self::errors::{CliError, ProcessError, CargoTestError};
pub use self::errors::{process_error, internal_error, internal, human};
pub use self::graph::Graph;
pub use self::hex::{to_hex, short_hash, hash_u64};
+pub use self::lazy_cell::LazyCell;
pub use self::lev_distance::{lev_distance};
pub use self::paths::{join_paths, path2bytes, bytes2path, dylib_path};
pub use self::paths::{normalize_path, dylib_path_envvar, without_prefix};
mod sha256;
mod shell_escape;
mod vcs;
+mod lazy_cell;