Allow users to add dependencies which are in alternate registries.
The alternate registries are listed in the .cargo/config, and must
there have a link to a conforming crate index.
let mut src = RegistrySource::remote(&src, config);
src.update()?;
let config = src.config()?.unwrap();
- let host = options.flag_host.clone().unwrap_or(config.api);
+ let host = options.flag_host.clone().unwrap_or(config.api.unwrap());
println!("please visit {}me and paste the API Token below", host);
let mut line = String::new();
let input = io::stdin();
//! })?;
//! ```
//!
-//! Notably you'll notice the `require` funciton called with your `Feature`, and
+//! Notably you'll notice the `require` function called with your `Feature`, and
//! then you use `chain_err` to tack on more context for why the feature was
//! required when the feature isn't activated.
//!
// A dummy feature that gates the usage of the `im-a-teapot` manifest
// entry. This is basically just intended for tests.
[unstable] test_dummy_unstable: bool,
+
+ // Downloading packages from alternative registry indexes.
+ [unstable] alternative_registries: bool,
}
}
+++ /dev/null
-use std::cmp::{self, Ordering};
-use std::collections::hash_map::{HashMap, Values, IterMut};
-use std::fmt::{self, Formatter};
-use std::hash::{self, Hash};
-use std::path::Path;
-use std::sync::Arc;
-use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT};
-use std::sync::atomic::Ordering::SeqCst;
-
-use serde::ser;
-use serde::de;
-use url::Url;
-
-use core::{Package, PackageId, Registry};
-use ops;
-use sources::git;
-use sources::{PathSource, GitSource, RegistrySource, CRATES_IO};
-use sources::DirectorySource;
-use util::{Config, CargoResult, ToUrl};
-
-/// A Source finds and downloads remote packages based on names and
-/// versions.
-pub trait Source: Registry {
- /// Returns the `SourceId` corresponding to this source
- fn source_id(&self) -> &SourceId;
-
- /// The update method performs any network operations required to
- /// get the entire list of all names, versions and dependencies of
- /// packages managed by the Source.
- fn update(&mut self) -> CargoResult<()>;
-
- /// The download method fetches the full package for each name and
- /// version specified.
- fn download(&mut self, package: &PackageId) -> CargoResult<Package>;
-
- /// Generates a unique string which represents the fingerprint of the
- /// current state of the source.
- ///
- /// This fingerprint is used to determine the "fresheness" of the source
- /// later on. It must be guaranteed that the fingerprint of a source is
- /// constant if and only if the output product will remain constant.
- ///
- /// The `pkg` argument is the package which this fingerprint should only be
- /// interested in for when this source may contain multiple packages.
- fn fingerprint(&self, pkg: &Package) -> CargoResult<String>;
-
- /// If this source supports it, verifies the source of the package
- /// specified.
- ///
- /// Note that the source may also have performed other checksum-based
- /// verification during the `download` step, but this is intended to be run
- /// just before a crate is compiled so it may perform more expensive checks
- /// which may not be cacheable.
- fn verify(&self, _pkg: &PackageId) -> CargoResult<()> {
- Ok(())
- }
-}
-
-impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
- /// Forwards to `Source::source_id`
- fn source_id(&self) -> &SourceId {
- (**self).source_id()
- }
-
- /// Forwards to `Source::update`
- fn update(&mut self) -> CargoResult<()> {
- (**self).update()
- }
-
- /// Forwards to `Source::download`
- fn download(&mut self, id: &PackageId) -> CargoResult<Package> {
- (**self).download(id)
- }
-
- /// Forwards to `Source::fingerprint`
- fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
- (**self).fingerprint(pkg)
- }
-
- /// Forwards to `Source::verify`
- fn verify(&self, pkg: &PackageId) -> CargoResult<()> {
- (**self).verify(pkg)
- }
-}
-
-/// The possible kinds of code source. Along with a URL, this fully defines the
-/// source
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-enum Kind {
- /// Kind::Git(<git reference>) represents a git repository
- Git(GitReference),
- /// represents a local path
- Path,
- /// represents the central registry
- Registry,
- /// represents a local filesystem-based registry
- LocalRegistry,
- /// represents a directory-based registry
- Directory,
-}
-
-/// Information to find a specific commit in a git repository
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum GitReference {
- /// from a tag
- Tag(String),
- /// from the HEAD of a branch
- Branch(String),
- /// from a specific revision
- Rev(String),
-}
-
-/// Unique identifier for a source of packages.
-#[derive(Clone, Eq, Debug)]
-pub struct SourceId {
- inner: Arc<SourceIdInner>,
-}
-
-/// Unique identifier for a source of packages.
-#[derive(Eq, Clone, Debug)]
-struct SourceIdInner {
- /// The source URL
- url: Url,
- /// `git::canonicalize_url(url)` for the url field
- canonical_url: Url,
- /// The source kind
- kind: Kind,
- // e.g. the exact git revision of the specified branch for a Git Source
- precise: Option<String>,
-}
-
-impl SourceId {
- /// Create a SourceId object from the kind and url.
- ///
- /// The canonical url will be calculated, but the precise field will not
- fn new(kind: Kind, url: Url) -> CargoResult<SourceId> {
- let source_id = SourceId {
- inner: Arc::new(SourceIdInner {
- kind: kind,
- canonical_url: git::canonicalize_url(&url)?,
- url: url,
- precise: None,
- }),
- };
- Ok(source_id)
- }
-
- /// Parses a source URL and returns the corresponding ID.
- ///
- /// ## Example
- ///
- /// ```
- /// use cargo::core::SourceId;
- /// SourceId::from_url("git+https://github.com/alexcrichton/\
- /// libssh2-static-sys#80e71a3021618eb05\
- /// 656c58fb7c5ef5f12bc747f");
- /// ```
- pub fn from_url(string: &str) -> CargoResult<SourceId> {
- let mut parts = string.splitn(2, '+');
- let kind = parts.next().unwrap();
- let url = parts.next().ok_or_else(|| format!("invalid source `{}`", string))?;
-
- match kind {
- "git" => {
- let mut url = url.to_url()?;
- let mut reference = GitReference::Branch("master".to_string());
- for (k, v) in url.query_pairs() {
- match &k[..] {
- // map older 'ref' to branch
- "branch" |
- "ref" => reference = GitReference::Branch(v.into_owned()),
-
- "rev" => reference = GitReference::Rev(v.into_owned()),
- "tag" => reference = GitReference::Tag(v.into_owned()),
- _ => {}
- }
- }
- let precise = url.fragment().map(|s| s.to_owned());
- url.set_fragment(None);
- url.set_query(None);
- Ok(SourceId::for_git(&url, reference)?.with_precise(precise))
- },
- "registry" => {
- let url = url.to_url()?;
- Ok(SourceId::new(Kind::Registry, url)?
- .with_precise(Some("locked".to_string())))
- }
- "path" => {
- let url = url.to_url()?;
- SourceId::new(Kind::Path, url)
- }
- kind => Err(format!("unsupported source protocol: {}", kind).into())
- }
- }
-
- /// A view of the `SourceId` that can be `Display`ed as a URL
- pub fn to_url(&self) -> SourceIdToUrl {
- SourceIdToUrl { inner: &*self.inner }
- }
-
- /// Create a SourceId from a filesystem path.
- ///
- /// Pass absolute path
- pub fn for_path(path: &Path) -> CargoResult<SourceId> {
- let url = path.to_url()?;
- SourceId::new(Kind::Path, url)
- }
-
- /// Crate a SourceId from a git reference
- pub fn for_git(url: &Url, reference: GitReference) -> CargoResult<SourceId> {
- SourceId::new(Kind::Git(reference), url.clone())
- }
-
- /// Create a SourceId from a registry url
- pub fn for_registry(url: &Url) -> CargoResult<SourceId> {
- SourceId::new(Kind::Registry, url.clone())
- }
-
- /// Create a SourceId from a local registry path
- pub fn for_local_registry(path: &Path) -> CargoResult<SourceId> {
- let url = path.to_url()?;
- SourceId::new(Kind::LocalRegistry, url)
- }
-
- /// Create a SourceId from a directory path
- pub fn for_directory(path: &Path) -> CargoResult<SourceId> {
- let url = path.to_url()?;
- SourceId::new(Kind::Directory, url)
- }
-
- /// Returns the `SourceId` corresponding to the main repository.
- ///
- /// This is the main cargo registry by default, but it can be overridden in
- /// a `.cargo/config`.
- pub fn crates_io(config: &Config) -> CargoResult<SourceId> {
- let cfg = ops::registry_configuration(config)?;
- let url = if let Some(ref index) = cfg.index {
- static WARNED: AtomicBool = ATOMIC_BOOL_INIT;
- if !WARNED.swap(true, SeqCst) {
- config.shell().warn("custom registry support via \
- the `registry.index` configuration is \
- being removed, this functionality \
- will not work in the future")?;
- }
- &index[..]
- } else {
- CRATES_IO
- };
- let url = url.to_url()?;
- SourceId::for_registry(&url)
- }
-
- /// Get this source URL
- pub fn url(&self) -> &Url {
- &self.inner.url
- }
-
- /// Is this source from a filesystem path
- pub fn is_path(&self) -> bool {
- self.inner.kind == Kind::Path
- }
-
- /// Is this source from a registry (either local or not)
- pub fn is_registry(&self) -> bool {
- self.inner.kind == Kind::Registry || self.inner.kind == Kind::LocalRegistry
- }
-
- /// Is this source from a git repository
- pub fn is_git(&self) -> bool {
- match self.inner.kind {
- Kind::Git(_) => true,
- _ => false,
- }
- }
-
- /// Creates an implementation of `Source` corresponding to this ID.
- pub fn load<'a>(&self, config: &'a Config) -> CargoResult<Box<Source + 'a>> {
- trace!("loading SourceId; {}", self);
- match self.inner.kind {
- Kind::Git(..) => Ok(Box::new(GitSource::new(self, config)?)),
- Kind::Path => {
- let path = match self.inner.url.to_file_path() {
- Ok(p) => p,
- Err(()) => panic!("path sources cannot be remote"),
- };
- Ok(Box::new(PathSource::new(&path, self, config)))
- }
- Kind::Registry => Ok(Box::new(RegistrySource::remote(self, config))),
- Kind::LocalRegistry => {
- let path = match self.inner.url.to_file_path() {
- Ok(p) => p,
- Err(()) => panic!("path sources cannot be remote"),
- };
- Ok(Box::new(RegistrySource::local(self, &path, config)))
- }
- Kind::Directory => {
- let path = match self.inner.url.to_file_path() {
- Ok(p) => p,
- Err(()) => panic!("path sources cannot be remote"),
- };
- Ok(Box::new(DirectorySource::new(&path, self, config)))
- }
- }
- }
-
- /// Get the value of the precise field
- pub fn precise(&self) -> Option<&str> {
- self.inner.precise.as_ref().map(|s| &s[..])
- }
-
- /// Get the git reference if this is a git source, otherwise None.
- pub fn git_reference(&self) -> Option<&GitReference> {
- match self.inner.kind {
- Kind::Git(ref s) => Some(s),
- _ => None,
- }
- }
-
- /// Create a new SourceId from this source with the given `precise`
- pub fn with_precise(&self, v: Option<String>) -> SourceId {
- SourceId {
- inner: Arc::new(SourceIdInner {
- precise: v,
- ..(*self.inner).clone()
- })
- }
- }
-
- /// Whether the remote registry is the standard https://crates.io
- pub fn is_default_registry(&self) -> bool {
- match self.inner.kind {
- Kind::Registry => {}
- _ => return false,
- }
- self.inner.url.to_string() == CRATES_IO
- }
-
- /// Hash `self`
- ///
- /// For paths, remove the workspace prefix so the same source will give the
- /// same hash in different locations.
- pub fn stable_hash<S: hash::Hasher>(&self, workspace: &Path, into: &mut S) {
- if self.is_path() {
- if let Ok(p) = self.inner.url.to_file_path().unwrap().strip_prefix(workspace) {
- self.inner.kind.hash(into);
- p.to_str().unwrap().hash(into);
- return
- }
- }
- self.hash(into)
- }
-}
-
-impl PartialEq for SourceId {
- fn eq(&self, other: &SourceId) -> bool {
- (*self.inner).eq(&*other.inner)
- }
-}
-
-impl PartialOrd for SourceId {
- fn partial_cmp(&self, other: &SourceId) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for SourceId {
- fn cmp(&self, other: &SourceId) -> Ordering {
- self.inner.cmp(&other.inner)
- }
-}
-
-impl ser::Serialize for SourceId {
- fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
- where S: ser::Serializer,
- {
- if self.is_path() {
- None::<String>.serialize(s)
- } else {
- s.collect_str(&self.to_url())
- }
- }
-}
-
-impl<'de> de::Deserialize<'de> for SourceId {
- fn deserialize<D>(d: D) -> Result<SourceId, D::Error>
- where D: de::Deserializer<'de>,
- {
- let string = String::deserialize(d)?;
- SourceId::from_url(&string).map_err(de::Error::custom)
- }
-}
-
-impl fmt::Display for SourceId {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- match *self.inner {
- SourceIdInner { kind: Kind::Path, ref url, .. } => {
- fmt::Display::fmt(url, f)
- }
- SourceIdInner { kind: Kind::Git(ref reference), ref url,
- ref precise, .. } => {
- write!(f, "{}", url)?;
- if let Some(pretty) = reference.pretty_ref() {
- write!(f, "?{}", pretty)?;
- }
-
- if let Some(ref s) = *precise {
- let len = cmp::min(s.len(), 8);
- write!(f, "#{}", &s[..len])?;
- }
- Ok(())
- }
- SourceIdInner { kind: Kind::Registry, ref url, .. } |
- SourceIdInner { kind: Kind::LocalRegistry, ref url, .. } => {
- write!(f, "registry {}", url)
- }
- SourceIdInner { kind: Kind::Directory, ref url, .. } => {
- write!(f, "dir {}", url)
- }
- }
- }
-}
-
-// This custom implementation handles situations such as when two git sources
-// point at *almost* the same URL, but not quite, even when they actually point
-// to the same repository.
-/// This method tests for self and other values to be equal, and is used by ==.
-///
-/// For git repositories, the canonical url is checked.
-impl PartialEq for SourceIdInner {
- fn eq(&self, other: &SourceIdInner) -> bool {
- if self.kind != other.kind {
- return false;
- }
- if self.url == other.url {
- return true;
- }
-
- match (&self.kind, &other.kind) {
- (&Kind::Git(ref ref1), &Kind::Git(ref ref2)) => {
- ref1 == ref2 && self.canonical_url == other.canonical_url
- }
- _ => false,
- }
- }
-}
-
-impl PartialOrd for SourceIdInner {
- fn partial_cmp(&self, other: &SourceIdInner) -> Option<Ordering> {
- Some(self.cmp(other))
- }
-}
-
-impl Ord for SourceIdInner {
- fn cmp(&self, other: &SourceIdInner) -> Ordering {
- match self.kind.cmp(&other.kind) {
- Ordering::Equal => {}
- ord => return ord,
- }
- match self.url.cmp(&other.url) {
- Ordering::Equal => {}
- ord => return ord,
- }
- match (&self.kind, &other.kind) {
- (&Kind::Git(ref ref1), &Kind::Git(ref ref2)) => {
- (ref1, &self.canonical_url).cmp(&(ref2, &other.canonical_url))
- }
- _ => self.kind.cmp(&other.kind),
- }
- }
-}
-
-// The hash of SourceId is used in the name of some Cargo folders, so shouldn't
-// vary. `as_str` gives the serialisation of a url (which has a spec) and so
-// insulates against possible changes in how the url crate does hashing.
-impl Hash for SourceId {
- fn hash<S: hash::Hasher>(&self, into: &mut S) {
- self.inner.kind.hash(into);
- match *self.inner {
- SourceIdInner { kind: Kind::Git(..), ref canonical_url, .. } => {
- canonical_url.as_str().hash(into)
- }
- _ => self.inner.url.as_str().hash(into),
- }
- }
-}
-
-/// A `Display`able view into a SourceId that will write it as a url
-pub struct SourceIdToUrl<'a> {
- inner: &'a SourceIdInner,
-}
-
-impl<'a> fmt::Display for SourceIdToUrl<'a> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self.inner {
- SourceIdInner { kind: Kind::Path, ref url, .. } => {
- write!(f, "path+{}", url)
- }
- SourceIdInner {
- kind: Kind::Git(ref reference), ref url, ref precise, ..
- } => {
- write!(f, "git+{}", url)?;
- if let Some(pretty) = reference.pretty_ref() {
- write!(f, "?{}", pretty)?;
- }
- if let Some(precise) = precise.as_ref() {
- write!(f, "#{}", precise)?;
- }
- Ok(())
- }
- SourceIdInner { kind: Kind::Registry, ref url, .. } => {
- write!(f, "registry+{}", url)
- }
- SourceIdInner { kind: Kind::LocalRegistry, ref url, .. } => {
- write!(f, "local-registry+{}", url)
- }
- SourceIdInner { kind: Kind::Directory, ref url, .. } => {
- write!(f, "directory+{}", url)
- }
- }
- }
-}
-
-impl GitReference {
- /// Returns a `Display`able view of this git reference, or None if using
- /// the head of the "master" branch
- pub fn pretty_ref(&self) -> Option<PrettyRef> {
- match *self {
- GitReference::Branch(ref s) if *s == "master" => None,
- _ => Some(PrettyRef { inner: self }),
- }
- }
-}
-
-/// A git reference that can be `Display`ed
-pub struct PrettyRef<'a> {
- inner: &'a GitReference,
-}
-
-impl<'a> fmt::Display for PrettyRef<'a> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self.inner {
- GitReference::Branch(ref b) => write!(f, "branch={}", b),
- GitReference::Tag(ref s) => write!(f, "tag={}", s),
- GitReference::Rev(ref s) => write!(f, "rev={}", s),
- }
- }
-}
-
-/// A `HashMap` of `SourceId` -> `Box<Source>`
-#[derive(Default)]
-pub struct SourceMap<'src> {
- map: HashMap<SourceId, Box<Source + 'src>>,
-}
-
-/// A `std::collection::hash_map::Values` for `SourceMap`
-pub type Sources<'a, 'src> = Values<'a, SourceId, Box<Source + 'src>>;
-
-/// A `std::collection::hash_map::IterMut` for `SourceMap`
-pub struct SourcesMut<'a, 'src: 'a> {
- inner: IterMut<'a, SourceId, Box<Source + 'src>>,
-}
-
-impl<'src> SourceMap<'src> {
- /// Create an empty map
- pub fn new() -> SourceMap<'src> {
- SourceMap { map: HashMap::new() }
- }
-
- /// Like `HashMap::contains_key`
- pub fn contains(&self, id: &SourceId) -> bool {
- self.map.contains_key(id)
- }
-
- /// Like `HashMap::get`
- pub fn get(&self, id: &SourceId) -> Option<&(Source + 'src)> {
- let source = self.map.get(id);
-
- source.map(|s| {
- let s: &(Source + 'src) = &**s;
- s
- })
- }
-
- /// Like `HashMap::get_mut`
- pub fn get_mut(&mut self, id: &SourceId) -> Option<&mut (Source + 'src)> {
- self.map.get_mut(id).map(|s| {
- let s: &mut (Source + 'src) = &mut **s;
- s
- })
- }
-
- /// Like `HashMap::get`, but first calculates the `SourceId` from a
- /// `PackageId`
- pub fn get_by_package_id(&self, pkg_id: &PackageId) -> Option<&(Source + 'src)> {
- self.get(pkg_id.source_id())
- }
-
- /// Like `HashMap::insert`, but derives the SourceId key from the Source
- pub fn insert(&mut self, source: Box<Source + 'src>) {
- let id = source.source_id().clone();
- self.map.insert(id, source);
- }
-
- /// Like `HashMap::is_empty`
- pub fn is_empty(&self) -> bool {
- self.map.is_empty()
- }
-
- /// Like `HashMap::len`
- pub fn len(&self) -> usize {
- self.map.len()
- }
-
- /// Like `HashMap::values`
- pub fn sources<'a>(&'a self) -> Sources<'a, 'src> {
- self.map.values()
- }
-
- /// Like `HashMap::iter_mut`
- pub fn sources_mut<'a>(&'a mut self) -> SourcesMut<'a, 'src> {
- SourcesMut { inner: self.map.iter_mut() }
- }
-}
-
-impl<'a, 'src> Iterator for SourcesMut<'a, 'src> {
- type Item = (&'a SourceId, &'a mut (Source + 'src));
- fn next(&mut self) -> Option<(&'a SourceId, &'a mut (Source + 'src))> {
- self.inner.next().map(|(a, b)| (a, &mut **b))
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::{SourceId, Kind, GitReference};
- use util::ToUrl;
-
- #[test]
- fn github_sources_equal() {
- let loc = "https://github.com/foo/bar".to_url().unwrap();
- let master = Kind::Git(GitReference::Branch("master".to_string()));
- let s1 = SourceId::new(master.clone(), loc).unwrap();
-
- let loc = "git://github.com/foo/bar".to_url().unwrap();
- let s2 = SourceId::new(master, loc.clone()).unwrap();
-
- assert_eq!(s1, s2);
-
- let foo = Kind::Git(GitReference::Branch("foo".to_string()));
- let s3 = SourceId::new(foo, loc).unwrap();
- assert!(s1 != s3);
- }
-}
--- /dev/null
+use std::collections::hash_map::{HashMap, Values, IterMut};
+
+use core::{Package, PackageId, Registry};
+use util::CargoResult;
+
+mod source_id;
+
+pub use self::source_id::{SourceId, GitReference};
+
+/// A Source finds and downloads remote packages based on names and
+/// versions.
+pub trait Source: Registry {
+ /// Returns the `SourceId` corresponding to this source
+ fn source_id(&self) -> &SourceId;
+
+ /// The update method performs any network operations required to
+ /// get the entire list of all names, versions and dependencies of
+ /// packages managed by the Source.
+ fn update(&mut self) -> CargoResult<()>;
+
+ /// The download method fetches the full package for each name and
+ /// version specified.
+ fn download(&mut self, package: &PackageId) -> CargoResult<Package>;
+
+ /// Generates a unique string which represents the fingerprint of the
+ /// current state of the source.
+ ///
+ /// This fingerprint is used to determine the "fresheness" of the source
+ /// later on. It must be guaranteed that the fingerprint of a source is
+ /// constant if and only if the output product will remain constant.
+ ///
+ /// The `pkg` argument is the package which this fingerprint should only be
+ /// interested in for when this source may contain multiple packages.
+ fn fingerprint(&self, pkg: &Package) -> CargoResult<String>;
+
+ /// If this source supports it, verifies the source of the package
+ /// specified.
+ ///
+ /// Note that the source may also have performed other checksum-based
+ /// verification during the `download` step, but this is intended to be run
+ /// just before a crate is compiled so it may perform more expensive checks
+ /// which may not be cacheable.
+ fn verify(&self, _pkg: &PackageId) -> CargoResult<()> {
+ Ok(())
+ }
+}
+
+impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
+ /// Forwards to `Source::source_id`
+ fn source_id(&self) -> &SourceId {
+ (**self).source_id()
+ }
+
+ /// Forwards to `Source::update`
+ fn update(&mut self) -> CargoResult<()> {
+ (**self).update()
+ }
+
+ /// Forwards to `Source::download`
+ fn download(&mut self, id: &PackageId) -> CargoResult<Package> {
+ (**self).download(id)
+ }
+
+ /// Forwards to `Source::fingerprint`
+ fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
+ (**self).fingerprint(pkg)
+ }
+
+ /// Forwards to `Source::verify`
+ fn verify(&self, pkg: &PackageId) -> CargoResult<()> {
+ (**self).verify(pkg)
+ }
+}
+
+/// A `HashMap` of `SourceId` -> `Box<Source>`
+#[derive(Default)]
+pub struct SourceMap<'src> {
+ map: HashMap<SourceId, Box<Source + 'src>>,
+}
+
+/// A `std::collection::hash_map::Values` for `SourceMap`
+pub type Sources<'a, 'src> = Values<'a, SourceId, Box<Source + 'src>>;
+
+/// A `std::collection::hash_map::IterMut` for `SourceMap`
+pub struct SourcesMut<'a, 'src: 'a> {
+ inner: IterMut<'a, SourceId, Box<Source + 'src>>,
+}
+
+impl<'src> SourceMap<'src> {
+ /// Create an empty map
+ pub fn new() -> SourceMap<'src> {
+ SourceMap { map: HashMap::new() }
+ }
+
+ /// Like `HashMap::contains_key`
+ pub fn contains(&self, id: &SourceId) -> bool {
+ self.map.contains_key(id)
+ }
+
+ /// Like `HashMap::get`
+ pub fn get(&self, id: &SourceId) -> Option<&(Source + 'src)> {
+ let source = self.map.get(id);
+
+ source.map(|s| {
+ let s: &(Source + 'src) = &**s;
+ s
+ })
+ }
+
+ /// Like `HashMap::get_mut`
+ pub fn get_mut(&mut self, id: &SourceId) -> Option<&mut (Source + 'src)> {
+ self.map.get_mut(id).map(|s| {
+ let s: &mut (Source + 'src) = &mut **s;
+ s
+ })
+ }
+
+ /// Like `HashMap::get`, but first calculates the `SourceId` from a
+ /// `PackageId`
+ pub fn get_by_package_id(&self, pkg_id: &PackageId) -> Option<&(Source + 'src)> {
+ self.get(pkg_id.source_id())
+ }
+
+ /// Like `HashMap::insert`, but derives the SourceId key from the Source
+ pub fn insert(&mut self, source: Box<Source + 'src>) {
+ let id = source.source_id().clone();
+ self.map.insert(id, source);
+ }
+
+ /// Like `HashMap::is_empty`
+ pub fn is_empty(&self) -> bool {
+ self.map.is_empty()
+ }
+
+ /// Like `HashMap::len`
+ pub fn len(&self) -> usize {
+ self.map.len()
+ }
+
+ /// Like `HashMap::values`
+ pub fn sources<'a>(&'a self) -> Sources<'a, 'src> {
+ self.map.values()
+ }
+
+ /// Like `HashMap::iter_mut`
+ pub fn sources_mut<'a>(&'a mut self) -> SourcesMut<'a, 'src> {
+ SourcesMut { inner: self.map.iter_mut() }
+ }
+}
+
+impl<'a, 'src> Iterator for SourcesMut<'a, 'src> {
+ type Item = (&'a SourceId, &'a mut (Source + 'src));
+ fn next(&mut self) -> Option<(&'a SourceId, &'a mut (Source + 'src))> {
+ self.inner.next().map(|(a, b)| (a, &mut **b))
+ }
+}
+
--- /dev/null
+use std::cmp::{self, Ordering};
+use std::fmt::{self, Formatter};
+use std::hash::{self, Hash};
+use std::path::Path;
+use std::sync::Arc;
+use std::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT};
+use std::sync::atomic::Ordering::SeqCst;
+
+use serde::ser;
+use serde::de;
+use url::Url;
+
+use ops;
+use sources::git;
+use sources::{PathSource, GitSource, RegistrySource, CRATES_IO};
+use sources::DirectorySource;
+use util::{Config, ConfigValue as CV, CargoResult, ToUrl};
+
+/// Unique identifier for a source of packages.
+#[derive(Clone, Eq, Debug)]
+pub struct SourceId {
+ inner: Arc<SourceIdInner>,
+}
+
+#[derive(Eq, Clone, Debug)]
+struct SourceIdInner {
+ /// The source URL
+ url: Url,
+ /// `git::canonicalize_url(url)` for the url field
+ canonical_url: Url,
+ /// The source kind
+ kind: Kind,
+ // e.g. the exact git revision of the specified branch for a Git Source
+ precise: Option<String>,
+}
+
+/// The possible kinds of code source. Along with a URL, this fully defines the
+/// source
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+enum Kind {
+ /// Kind::Git(<git reference>) represents a git repository
+ Git(GitReference),
+ /// represents a local path
+ Path,
+ /// represents a remote registry
+ Registry,
+ /// represents a local filesystem-based registry
+ LocalRegistry,
+ /// represents a directory-based registry
+ Directory,
+}
+
+/// Information to find a specific commit in a git repository
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum GitReference {
+ /// from a tag
+ Tag(String),
+ /// from the HEAD of a branch
+ Branch(String),
+ /// from a specific revision
+ Rev(String),
+}
+
+impl SourceId {
+ /// Create a SourceId object from the kind and url.
+ ///
+ /// The canonical url will be calculated, but the precise field will not
+ fn new(kind: Kind, url: Url) -> CargoResult<SourceId> {
+ let source_id = SourceId {
+ inner: Arc::new(SourceIdInner {
+ kind: kind,
+ canonical_url: git::canonicalize_url(&url)?,
+ url: url,
+ precise: None,
+ }),
+ };
+ Ok(source_id)
+ }
+
+ /// Parses a source URL and returns the corresponding ID.
+ ///
+ /// ## Example
+ ///
+ /// ```
+ /// use cargo::core::SourceId;
+ /// SourceId::from_url("git+https://github.com/alexcrichton/\
+ /// libssh2-static-sys#80e71a3021618eb05\
+ /// 656c58fb7c5ef5f12bc747f");
+ /// ```
+ pub fn from_url(string: &str) -> CargoResult<SourceId> {
+ let mut parts = string.splitn(2, '+');
+ let kind = parts.next().unwrap();
+ let url = parts.next().ok_or_else(|| format!("invalid source `{}`", string))?;
+
+ match kind {
+ "git" => {
+ let mut url = url.to_url()?;
+ let mut reference = GitReference::Branch("master".to_string());
+ for (k, v) in url.query_pairs() {
+ match &k[..] {
+ // map older 'ref' to branch
+ "branch" |
+ "ref" => reference = GitReference::Branch(v.into_owned()),
+
+ "rev" => reference = GitReference::Rev(v.into_owned()),
+ "tag" => reference = GitReference::Tag(v.into_owned()),
+ _ => {}
+ }
+ }
+ let precise = url.fragment().map(|s| s.to_owned());
+ url.set_fragment(None);
+ url.set_query(None);
+ Ok(SourceId::for_git(&url, reference)?.with_precise(precise))
+ },
+ "registry" => {
+ let url = url.to_url()?;
+ Ok(SourceId::new(Kind::Registry, url)?
+ .with_precise(Some("locked".to_string())))
+ }
+ "path" => {
+ let url = url.to_url()?;
+ SourceId::new(Kind::Path, url)
+ }
+ kind => Err(format!("unsupported source protocol: {}", kind).into())
+ }
+ }
+
+ /// A view of the `SourceId` that can be `Display`ed as a URL
+ pub fn to_url(&self) -> SourceIdToUrl {
+ SourceIdToUrl { inner: &*self.inner }
+ }
+
+ /// Create a SourceId from a filesystem path.
+ ///
+ /// Pass absolute path
+ pub fn for_path(path: &Path) -> CargoResult<SourceId> {
+ let url = path.to_url()?;
+ SourceId::new(Kind::Path, url)
+ }
+
+ /// Crate a SourceId from a git reference
+ pub fn for_git(url: &Url, reference: GitReference) -> CargoResult<SourceId> {
+ SourceId::new(Kind::Git(reference), url.clone())
+ }
+
+ /// Create a SourceId from a registry url
+ pub fn for_registry(url: &Url) -> CargoResult<SourceId> {
+ SourceId::new(Kind::Registry, url.clone())
+ }
+
+ /// Create a SourceId from a local registry path
+ pub fn for_local_registry(path: &Path) -> CargoResult<SourceId> {
+ let url = path.to_url()?;
+ SourceId::new(Kind::LocalRegistry, url)
+ }
+
+ /// Create a SourceId from a directory path
+ pub fn for_directory(path: &Path) -> CargoResult<SourceId> {
+ let url = path.to_url()?;
+ SourceId::new(Kind::Directory, url)
+ }
+
+ /// Returns the `SourceId` corresponding to the main repository.
+ ///
+ /// This is the main cargo registry by default, but it can be overridden in
+ /// a `.cargo/config`.
+ pub fn crates_io(config: &Config) -> CargoResult<SourceId> {
+ let cfg = ops::registry_configuration(config)?;
+ let url = if let Some(ref index) = cfg.index {
+ static WARNED: AtomicBool = ATOMIC_BOOL_INIT;
+ if !WARNED.swap(true, SeqCst) {
+ config.shell().warn("custom registry support via \
+ the `registry.index` configuration is \
+ being removed, this functionality \
+ will not work in the future")?;
+ }
+ &index[..]
+ } else {
+ CRATES_IO
+ };
+ let url = url.to_url()?;
+ SourceId::for_registry(&url)
+ }
+
+ pub fn alt_registry(config: &Config, key: &str) -> CargoResult<SourceId> {
+ let registries = config.get_table("registries")?;
+ match registries.as_ref().and_then(|registries| registries.val.get(key)) {
+ Some(registry) => {
+ let index = match *registry {
+ CV::Table(ref registry, _) => {
+ match registry.get("index") {
+ Some(index) => index.string(&format!("registries.{}", key))?.0,
+ None => return Err(format!("No index for registry `{}`", key).into()),
+ }
+ }
+ _ => registry.expected("table", &format!("registries.{}", key))?
+ };
+
+ let url = index.to_url()?;
+
+ Ok(SourceId {
+ inner: Arc::new(SourceIdInner {
+ kind: Kind::Registry,
+ canonical_url: git::canonicalize_url(&url)?,
+ url: url,
+ precise: None,
+ }),
+ })
+ }
+ None => Err(format!("Required unknown registry source: `{}`", key).into())
+ }
+ }
+
+ /// Get this source URL
+ pub fn url(&self) -> &Url {
+ &self.inner.url
+ }
+
+ pub fn display_registry(&self) -> String {
+ format!("registry `{}`", self.url())
+ }
+
+ /// Is this source from a filesystem path
+ pub fn is_path(&self) -> bool {
+ self.inner.kind == Kind::Path
+ }
+
+ /// Is this source from a registry (either local or not)
+ pub fn is_registry(&self) -> bool {
+ match self.inner.kind {
+ Kind::Registry | Kind::LocalRegistry => true,
+ _ => false,
+ }
+ }
+
+ /// Is this source from a git repository
+ pub fn is_git(&self) -> bool {
+ match self.inner.kind {
+ Kind::Git(_) => true,
+ _ => false,
+ }
+ }
+
+ /// Creates an implementation of `Source` corresponding to this ID.
+ pub fn load<'a>(&self, config: &'a Config) -> CargoResult<Box<super::Source + 'a>> {
+ trace!("loading SourceId; {}", self);
+ match self.inner.kind {
+ Kind::Git(..) => Ok(Box::new(GitSource::new(self, config)?)),
+ Kind::Path => {
+ let path = match self.inner.url.to_file_path() {
+ Ok(p) => p,
+ Err(()) => panic!("path sources cannot be remote"),
+ };
+ Ok(Box::new(PathSource::new(&path, self, config)))
+ }
+ Kind::Registry => Ok(Box::new(RegistrySource::remote(self, config))),
+ Kind::LocalRegistry => {
+ let path = match self.inner.url.to_file_path() {
+ Ok(p) => p,
+ Err(()) => panic!("path sources cannot be remote"),
+ };
+ Ok(Box::new(RegistrySource::local(self, &path, config)))
+ }
+ Kind::Directory => {
+ let path = match self.inner.url.to_file_path() {
+ Ok(p) => p,
+ Err(()) => panic!("path sources cannot be remote"),
+ };
+ Ok(Box::new(DirectorySource::new(&path, self, config)))
+ }
+ }
+ }
+
+ /// Get the value of the precise field
+ pub fn precise(&self) -> Option<&str> {
+ self.inner.precise.as_ref().map(|s| &s[..])
+ }
+
+ /// Get the git reference if this is a git source, otherwise None.
+ pub fn git_reference(&self) -> Option<&GitReference> {
+ match self.inner.kind {
+ Kind::Git(ref s) => Some(s),
+ _ => None,
+ }
+ }
+
+ /// Create a new SourceId from this source with the given `precise`
+ pub fn with_precise(&self, v: Option<String>) -> SourceId {
+ SourceId {
+ inner: Arc::new(SourceIdInner {
+ precise: v,
+ ..(*self.inner).clone()
+ })
+ }
+ }
+
+ /// Whether the remote registry is the standard https://crates.io
+ pub fn is_default_registry(&self) -> bool {
+ match self.inner.kind {
+ Kind::Registry => {}
+ _ => return false,
+ }
+ self.inner.url.to_string() == CRATES_IO
+ }
+
+ /// Hash `self`
+ ///
+ /// For paths, remove the workspace prefix so the same source will give the
+ /// same hash in different locations.
+ pub fn stable_hash<S: hash::Hasher>(&self, workspace: &Path, into: &mut S) {
+ if self.is_path() {
+ if let Ok(p) = self.inner.url.to_file_path().unwrap().strip_prefix(workspace) {
+ self.inner.kind.hash(into);
+ p.to_str().unwrap().hash(into);
+ return
+ }
+ }
+ self.hash(into)
+ }
+}
+
+impl PartialEq for SourceId {
+ fn eq(&self, other: &SourceId) -> bool {
+ (*self.inner).eq(&*other.inner)
+ }
+}
+
+impl PartialOrd for SourceId {
+ fn partial_cmp(&self, other: &SourceId) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for SourceId {
+ fn cmp(&self, other: &SourceId) -> Ordering {
+ self.inner.cmp(&other.inner)
+ }
+}
+
+impl ser::Serialize for SourceId {
+ fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
+ where S: ser::Serializer,
+ {
+ if self.is_path() {
+ None::<String>.serialize(s)
+ } else {
+ s.collect_str(&self.to_url())
+ }
+ }
+}
+
+impl<'de> de::Deserialize<'de> for SourceId {
+ fn deserialize<D>(d: D) -> Result<SourceId, D::Error>
+ where D: de::Deserializer<'de>,
+ {
+ let string = String::deserialize(d)?;
+ SourceId::from_url(&string).map_err(de::Error::custom)
+ }
+}
+
+impl fmt::Display for SourceId {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ match *self.inner {
+ SourceIdInner { kind: Kind::Path, ref url, .. } => {
+ fmt::Display::fmt(url, f)
+ }
+ SourceIdInner { kind: Kind::Git(ref reference), ref url,
+ ref precise, .. } => {
+ write!(f, "{}", url)?;
+ if let Some(pretty) = reference.pretty_ref() {
+ write!(f, "?{}", pretty)?;
+ }
+
+ if let Some(ref s) = *precise {
+ let len = cmp::min(s.len(), 8);
+ write!(f, "#{}", &s[..len])?;
+ }
+ Ok(())
+ }
+ SourceIdInner { kind: Kind::Registry, ref url, .. } |
+ SourceIdInner { kind: Kind::LocalRegistry, ref url, .. } => {
+ write!(f, "registry `{}`", url)
+ }
+ SourceIdInner { kind: Kind::Directory, ref url, .. } => {
+ write!(f, "dir {}", url)
+ }
+ }
+ }
+}
+
+// This custom implementation handles situations such as when two git sources
+// point at *almost* the same URL, but not quite, even when they actually point
+// to the same repository.
+/// This method tests for self and other values to be equal, and is used by ==.
+///
+/// For git repositories, the canonical url is checked.
+impl PartialEq for SourceIdInner {
+ fn eq(&self, other: &SourceIdInner) -> bool {
+ if self.kind != other.kind {
+ return false;
+ }
+ if self.url == other.url {
+ return true;
+ }
+
+ match (&self.kind, &other.kind) {
+ (&Kind::Git(ref ref1), &Kind::Git(ref ref2)) => {
+ ref1 == ref2 && self.canonical_url == other.canonical_url
+ }
+ _ => false,
+ }
+ }
+}
+
+impl PartialOrd for SourceIdInner {
+ fn partial_cmp(&self, other: &SourceIdInner) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for SourceIdInner {
+ fn cmp(&self, other: &SourceIdInner) -> Ordering {
+ match self.kind.cmp(&other.kind) {
+ Ordering::Equal => {}
+ ord => return ord,
+ }
+ match self.url.cmp(&other.url) {
+ Ordering::Equal => {}
+ ord => return ord,
+ }
+ match (&self.kind, &other.kind) {
+ (&Kind::Git(ref ref1), &Kind::Git(ref ref2)) => {
+ (ref1, &self.canonical_url).cmp(&(ref2, &other.canonical_url))
+ }
+ _ => self.kind.cmp(&other.kind),
+ }
+ }
+}
+
+// The hash of SourceId is used in the name of some Cargo folders, so shouldn't
+// vary. `as_str` gives the serialisation of a url (which has a spec) and so
+// insulates against possible changes in how the url crate does hashing.
+impl Hash for SourceId {
+ fn hash<S: hash::Hasher>(&self, into: &mut S) {
+ self.inner.kind.hash(into);
+ match *self.inner {
+ SourceIdInner { kind: Kind::Git(..), ref canonical_url, .. } => {
+ canonical_url.as_str().hash(into)
+ }
+ _ => self.inner.url.as_str().hash(into),
+ }
+ }
+}
+
+/// A `Display`able view into a SourceId that will write it as a url
+pub struct SourceIdToUrl<'a> {
+ inner: &'a SourceIdInner,
+}
+
+impl<'a> fmt::Display for SourceIdToUrl<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self.inner {
+ SourceIdInner { kind: Kind::Path, ref url, .. } => {
+ write!(f, "path+{}", url)
+ }
+ SourceIdInner {
+ kind: Kind::Git(ref reference), ref url, ref precise, ..
+ } => {
+ write!(f, "git+{}", url)?;
+ if let Some(pretty) = reference.pretty_ref() {
+ write!(f, "?{}", pretty)?;
+ }
+ if let Some(precise) = precise.as_ref() {
+ write!(f, "#{}", precise)?;
+ }
+ Ok(())
+ }
+ SourceIdInner { kind: Kind::Registry, ref url, .. } => {
+ write!(f, "registry+{}", url)
+ }
+ SourceIdInner { kind: Kind::LocalRegistry, ref url, .. } => {
+ write!(f, "local-registry+{}", url)
+ }
+ SourceIdInner { kind: Kind::Directory, ref url, .. } => {
+ write!(f, "directory+{}", url)
+ }
+ }
+ }
+}
+
+impl GitReference {
+ /// Returns a `Display`able view of this git reference, or None if using
+ /// the head of the "master" branch
+ pub fn pretty_ref(&self) -> Option<PrettyRef> {
+ match *self {
+ GitReference::Branch(ref s) if *s == "master" => None,
+ _ => Some(PrettyRef { inner: self }),
+ }
+ }
+}
+
+/// A git reference that can be `Display`ed
+pub struct PrettyRef<'a> {
+ inner: &'a GitReference,
+}
+
+impl<'a> fmt::Display for PrettyRef<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self.inner {
+ GitReference::Branch(ref b) => write!(f, "branch={}", b),
+ GitReference::Tag(ref s) => write!(f, "tag={}", s),
+ GitReference::Rev(ref s) => write!(f, "rev={}", s),
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{SourceId, Kind, GitReference};
+ use util::ToUrl;
+
+ #[test]
+ fn github_sources_equal() {
+ let loc = "https://github.com/foo/bar".to_url().unwrap();
+ let master = Kind::Git(GitReference::Branch("master".to_string()));
+ let s1 = SourceId::new(master.clone(), loc).unwrap();
+
+ let loc = "git://github.com/foo/bar".to_url().unwrap();
+ let s2 = SourceId::new(master, loc.clone()).unwrap();
+
+ assert_eq!(s1, s2);
+
+ let foo = Kind::Git(GitReference::Branch("foo".to_string()));
+ let s3 = SourceId::new(foo, loc).unwrap();
+ assert!(s1 != s3);
+ }
+}
a version", dep.name())
}
} else if dep.source_id() != registry_src {
- bail!("crates cannot be published to crates.io with dependencies sourced from \
- a repository\neither publish `{}` as its own crate on crates.io and \
- specify a crates.io version as a dependency or pull it into this \
- repository and specify it with a path and version\n(crate `{}` has \
- repository path `{}`)", dep.name(), dep.name(), dep.source_id());
+ if dep.source_id().is_registry() {
+ bail!("crates cannot be published to crates.io with dependencies sourced from other\n\
+ registries either publish `{}` on crates.io or pull it into this repository\n\
+ and specify it with a path and version\n\
+ (crate `{}` is pulled from {}", dep.name(), dep.name(), dep.source_id());
+ } else {
+ bail!("crates cannot be published to crates.io with dependencies sourced from \
+ a repository\neither publish `{}` as its own crate on crates.io and \
+ specify a crates.io version as a dependency or pull it into this \
+ repository and specify it with a path and version\n(crate `{}` has \
+ repository path `{}`)", dep.name(), dep.name(), dep.source_id());
+ }
}
}
Ok(())
src.update().chain_err(|| {
format!("failed to update {}", sid)
})?;
- (src.config()?).unwrap().api
+ (src.config()?).unwrap().api.unwrap()
};
let handle = http_handle(config)?;
Ok((Registry::new_handle(api_host, token, handle), sid))
/// API endpoint for the registry. This is what's actually hit to perform
/// operations like yanks, owner modifications, publish new crates, etc.
- pub api: String,
+ pub api: Option<String>,
}
#[derive(Deserialize)]
let _lock = self.index_path.open_rw(Path::new(INDEX_LOCK),
self.config,
"the registry index")?;
- self.config.shell().status("Updating",
- format!("registry `{}`", self.source_id.url()))?;
+ self.config.shell().status("Updating", self.source_id.display_registry())?;
// git fetch origin master
let url = self.source_id.url();
}
}
- fn expected<T>(&self, wanted: &str, key: &str) -> CargoResult<T> {
+ pub fn expected<T>(&self, wanted: &str, key: &str) -> CargoResult<T> {
Err(format!("expected a {}, but found a {} for `{}` in {}",
wanted, self.desc(), key,
self.definition_path().display()).into())
use core::{SourceId, Profiles, PackageIdSpec, GitReference, WorkspaceConfig};
use core::{Summary, Manifest, Target, Dependency, PackageId};
-use core::{EitherManifest, VirtualManifest, Features};
+use core::{EitherManifest, VirtualManifest, Features, Feature};
use core::dependency::{Kind, Platform};
use core::manifest::{LibKind, Profile, ManifestMetadata};
use sources::CRATES_IO;
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
pub struct DetailedTomlDependency {
version: Option<String>,
+ registry: Option<String>,
path: Option<String>,
git: Option<String>,
branch: Option<String>,
warnings: &'a mut Vec<String>,
platform: Option<Platform>,
root: &'a Path,
+ features: &'a Features,
}
impl TomlManifest {
let mut warnings = vec![];
let mut errors = vec![];
+ // Parse features first so they will be available when parsing other parts of the toml
+ let empty = Vec::new();
+ let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
+ let features = Features::new(&cargo_features, &mut warnings)?;
+
let project = me.project.as_ref().or_else(|| me.package.as_ref());
let project = project.ok_or_else(|| {
CargoError::from("no `package` or `project` section found.")
nested_paths: &mut nested_paths,
config: config,
warnings: &mut warnings,
+ features: &features,
platform: None,
root: package_root,
};
};
let profiles = build_profiles(&me.profile);
let publish = project.publish.unwrap_or(true);
- let empty = Vec::new();
- let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
- let features = Features::new(cargo_features, &mut warnings)?;
let mut manifest = Manifest::new(summary,
targets,
exclude,
config: config,
warnings: &mut warnings,
platform: None,
+ features: &Features::default(), // @alex: is this right?
root: root
};
(me.replace(&mut cx)?, me.patch(&mut cx)?)
}
}
- let new_source_id = match (details.git.as_ref(), details.path.as_ref()) {
- (Some(git), maybe_path) => {
+ let new_source_id = match (details.git.as_ref(), details.path.as_ref(), details.registry.as_ref()) {
+ (Some(_), _, Some(_)) => bail!("dependency ({}) specification is ambiguous. \
+ Only one of `git` or `registry` is allowed.", name),
+ (_, Some(_), Some(_)) => bail!("dependency ({}) specification is ambiguous. \
+ Only one of `path` or `registry` is allowed.", name),
+ (Some(git), maybe_path, _) => {
if maybe_path.is_some() {
let msg = format!("dependency ({}) specification is ambiguous. \
Only one of `git` or `path` is allowed. \
let loc = git.to_url()?;
SourceId::for_git(&loc, reference)?
},
- (None, Some(path)) => {
+ (None, Some(path), _) => {
cx.nested_paths.push(PathBuf::from(path));
// If the source id for the package we're parsing is a path
// source, then we normalize the path here to get rid of
cx.source_id.clone()
}
},
- (None, None) => SourceId::crates_io(cx.config)?,
+ (None, None, Some(registry)) => {
+ cx.features.require(Feature::alternative_registries())?;
+ SourceId::alt_registry(cx.config, registry)?
+ }
+ (None, None, None) => SourceId::crates_io(cx.config)?,
};
let version = details.version.as_ref().map(|v| &v[..]);
--- /dev/null
+extern crate cargotest;
+extern crate hamcrest;
+
+use cargotest::ChannelChanger;
+use cargotest::support::registry::{self, Package};
+use cargotest::support::{project, execs};
+use hamcrest::assert_that;
+
+#[test]
+fn is_feature_gated() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bar]
+ version = "0.0.1"
+ registry = "alternative"
+ "#)
+ .file("src/main.rs", "fn main() {}");
+ p.build();
+
+ Package::new("bar", "0.0.1").alternative(true).publish();
+
+ assert_that(p.cargo("build").masquerade_as_nightly_cargo(),
+ execs().with_status(101)
+ .with_stderr_contains(" feature `alternative-registries` is required"));
+}
+
+#[test]
+fn depend_on_alt_registry() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ cargo-features = ["alternative-registries"]
+
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bar]
+ version = "0.0.1"
+ registry = "alternative"
+ "#)
+ .file("src/main.rs", "fn main() {}");
+ p.build();
+
+ Package::new("bar", "0.0.1").alternative(true).publish();
+
+ assert_that(p.cargo("build").masquerade_as_nightly_cargo(),
+ execs().with_status(0).with_stderr(&format!("\
+[UPDATING] registry `{reg}`
+[DOWNLOADING] bar v0.0.1 (registry `file://[..]`)
+[COMPILING] bar v0.0.1 (registry `file://[..]`)
+[COMPILING] foo v0.0.1 ({dir})
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] secs
+",
+ dir = p.url(),
+ reg = registry::alt_registry())));
+
+ assert_that(p.cargo("clean").masquerade_as_nightly_cargo(), execs().with_status(0));
+
+ // Don't download a second time
+ assert_that(p.cargo("build").masquerade_as_nightly_cargo(),
+ execs().with_status(0).with_stderr(&format!("\
+[COMPILING] bar v0.0.1 (registry `file://[..]`)
+[COMPILING] foo v0.0.1 ({dir})
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] secs
+",
+ dir = p.url())));
+}
+
+#[test]
+fn registry_incompatible_with_path() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ cargo-features = ["alternative-registries"]
+
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bar]
+ path = ""
+ registry = "alternative"
+ "#)
+ .file("src/main.rs", "fn main() {}");
+ p.build();
+
+ assert_that(p.cargo("build").masquerade_as_nightly_cargo(),
+ execs().with_status(101)
+ .with_stderr_contains(" dependency (bar) specification is ambiguous. Only one of `path` or `registry` is allowed."));
+}
+
+#[test]
+fn registry_incompatible_with_git() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ cargo-features = ["alternative-registries"]
+
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bar]
+ git = ""
+ registry = "alternative"
+ "#)
+ .file("src/main.rs", "fn main() {}");
+ p.build();
+
+ assert_that(p.cargo("build").masquerade_as_nightly_cargo(),
+ execs().with_status(101)
+ .with_stderr_contains(" dependency (bar) specification is ambiguous. Only one of `git` or `registry` is allowed."));
+}
+
+
+#[test]
+fn cannot_publish_with_registry_dependency() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ cargo-features = ["alternative-registries"]
+
+ [project]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies.bar]
+ version = "0.0.1"
+ registry = "alternative"
+ "#)
+ .file("src/main.rs", "fn main() {}");
+ p.build();
+
+ Package::new("bar", "0.0.1").alternative(true).publish();
+
+ assert_that(p.cargo("publish").masquerade_as_nightly_cargo()
+ .arg("--index").arg(registry::alt_registry().to_string()),
+ execs().with_status(101));
+}
error: failed to load source for a dependency on `bar`
Caused by:
- Unable to update registry https://[..]
+ Unable to update registry `https://[..]`
Caused by:
could not find a configured source with the name `bar` \
error: failed to load source for a dependency on `bar`
Caused by:
- Unable to update registry https://[..]
+ Unable to update registry `https://[..]`
Caused by:
detected a cycle of `replace-with` sources, [..]
error: failed to load source for a dependency on `bar`
Caused by:
- Unable to update registry https://[..]
+ Unable to update registry `https://[..]`
Caused by:
detected a cycle of `replace-with` sources, the source `crates-io` is \
pub fn registry() -> Url { Url::from_file_path(&*registry_path()).ok().unwrap() }
pub fn dl_path() -> PathBuf { paths::root().join("dl") }
pub fn dl_url() -> Url { Url::from_file_path(&*dl_path()).ok().unwrap() }
+pub fn alt_registry_path() -> PathBuf { paths::root().join("alternative-registry") }
+pub fn alt_registry() -> Url { Url::from_file_path(&*alt_registry_path()).ok().unwrap() }
pub struct Package {
name: String,
yanked: bool,
features: HashMap<String, Vec<String>>,
local: bool,
+ alternative: bool,
}
struct Dependency {
[source.dummy-registry]
registry = '{reg}'
- "#, reg = registry()).as_bytes()));
+
+ [registries.alternative]
+ index = '{alt}'
+ "#, reg = registry(), alt = alt_registry()).as_bytes()));
// Init a new registry
repo(®istry_path())
"#, dl_url()))
.build();
fs::create_dir_all(dl_path().join("api/v1/crates")).unwrap();
+
+ // Init an alt registry
+ repo(&alt_registry_path())
+ .file("config.json", &format!(r#"
+ {{"dl":"{0}","api":"{0}"}}
+ "#, dl_url()))
+ .build();
+ fs::create_dir_all(dl_path().join("api/v1/crates")).unwrap();
}
impl Package {
yanked: false,
features: HashMap::new(),
local: false,
+ alternative: false,
}
}
self
}
+ pub fn alternative(&mut self, alternative: bool) -> &mut Package {
+ self.alternative = alternative;
+ self
+ }
+
pub fn file(&mut self, name: &str, contents: &str) -> &mut Package {
self.files.push((name.to_string(), contents.to_string()));
self
_ => format!("{}/{}/{}", &self.name[0..2], &self.name[2..4], self.name),
};
+ let registry_path = if self.alternative { alt_registry_path() } else { registry_path() };
+
// Write file/line in the index
let dst = if self.local {
- registry_path().join("index").join(&file)
+ registry_path.join("index").join(&file)
} else {
- registry_path().join(&file)
+ registry_path.join(&file)
};
let mut prev = String::new();
let _ = File::open(&dst).and_then(|mut f| f.read_to_string(&mut prev));
// Add the new file to the index
if !self.local {
- let repo = t!(git2::Repository::open(®istry_path()));
+ let repo = t!(git2::Repository::open(®istry_path));
let mut index = t!(repo.index());
t!(index.add_path(Path::new(&file)));
t!(index.write());