Implement a fallible PackageSet::get
authorAlex Crichton <alex@alexcrichton.com>
Sat, 20 Feb 2016 18:36:09 +0000 (10:36 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Mon, 22 Feb 2016 04:39:42 +0000 (20:39 -0800)
This function will lazily download the package specified and fill a cell with
that package. Currently this is always infallible because the `PackageSet` is
initialized with all packages, but eventually this will not be true.

src/cargo/core/package.rs
src/cargo/ops/cargo_clean.rs
src/cargo/ops/cargo_compile.rs
src/cargo/ops/cargo_output_metadata.rs
src/cargo/ops/cargo_rustc/context.rs
src/cargo/ops/cargo_rustc/fingerprint.rs
src/cargo/ops/cargo_rustc/job_queue.rs
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/util/dependency_queue.rs
src/cargo/util/lazy_cell.rs [new file with mode: 0644]
src/cargo/util/mod.rs

index e671a15a60eb83fcb07904945242455429934197..b0c0c24617ae7b9f53a353fbc52ac71d962a1139 100644 (file)
@@ -1,15 +1,15 @@
+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.
@@ -120,30 +120,43 @@ impl hash::Hash for Package {
 }
 
 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()
     }
 }
index 1359438277d0186f6526a8d13c6d8147f8dfe3f8..c4918f9c352d2df317b80ea9fd208c9a2851ba55 100644 (file)
@@ -42,7 +42,7 @@ pub fn clean(manifest_path: &Path, opts: &CleanOptions) -> CargoResult<()> {
     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() {
index 3286db42f775f28d8afd9820323fdaf29750e9eb..7d0f858eacf33c2bba097951769dc68d7182470d 100644 (file)
@@ -180,7 +180,9 @@ pub fn compile_pkg<'a>(root_package: &Package,
               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();
index 7902ea3e953455ae9bb31b71cec3c3e4c862c7fa..7a037828d0383a1bc4543c65d243ccb7836d2122 100644 (file)
@@ -51,10 +51,9 @@ fn metadata_full(opt: OutputMetadataOptions, config: &Config) -> CargoResult<Exp
                                          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,
index 4d7cc115786925f16953ef2ff6fa2f00e4f0b43d..3b8fd290bbf5b1b05cbf62e7fc4ec653461c8dcc 100644 (file)
@@ -560,7 +560,8 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
 
     /// 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
index 09d3300cceed8325f944a926d3773763015f1c51..386ed7792e117138bcf839017670d5110a87d9cf 100644 (file)
@@ -562,7 +562,8 @@ fn dep_info_mtime_if_fresh(dep_info: &Path) -> CargoResult<Option<FileTime>> {
 
 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)
index 30d6394c28ff8cedb93b4bfdf75c056380c5a2e7..6ef8f7d3561fa71e0e7fe23aaffb6f0a987a5756 100644 (file)
@@ -8,7 +8,7 @@ use term::color::YELLOW;
 
 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;
@@ -68,10 +68,13 @@ impl<'a> JobQueue<'a> {
         }
     }
 
-    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;
     }
 
@@ -230,10 +233,17 @@ impl<'a> JobQueue<'a> {
     }
 }
 
-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,
@@ -252,17 +262,6 @@ impl<'a> Dependency for Key<'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,
-        }
-    }
-}
-
 impl<'a> fmt::Debug for Key<'a> {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         write!(f, "{} => {}/{} => {:?}", self.pkg, self.target, self.profile,
index 7f2deb917f67bf42e411e0d4fd2f8ad59de75806..0306c29b3132a412b995ef05ddae9e61c45a4192 100644 (file)
@@ -79,7 +79,7 @@ pub fn compile_targets<'a, 'cfg: 'a>(pkg_targets: &'a PackagesToBuild<'a>,
     }).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)
index 9db5d103c5978aad7bf1f0260ee14165a0ff4cc2..5165adc249a2a5b338fb649fa6852e64e02a98df 100644 (file)
@@ -46,19 +46,13 @@ pub enum Freshness {
     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 {
@@ -73,8 +67,11 @@ impl<K: Dependency, V> DependencyQueue<K, V> {
     ///
     /// 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,
@@ -85,9 +82,10 @@ impl<K: Dependency, V> DependencyQueue<K, 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
diff --git a/src/cargo/util/lazy_cell.rs b/src/cargo/util/lazy_cell.rs
new file mode 100644 (file)
index 0000000..a13eaf3
--- /dev/null
@@ -0,0 +1,55 @@
+//! 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()
+        }
+    }
+}
index e2f2cb971c2a81beaf51d579c87345c66737a82b..da0f63a6a012025ea5fb891b03e438a74aa18a0a 100644 (file)
@@ -1,6 +1,5 @@
 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};
@@ -8,6 +7,7 @@ pub use self::errors::{Human, caused_human};
 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};
@@ -37,3 +37,4 @@ mod rustc;
 mod sha256;
 mod shell_escape;
 mod vcs;
+mod lazy_cell;