]> git.proxmox.com Git - cargo.git/commitdiff
Auto merge of #2385 - alexcrichton:top-level-overrides, r=wycats
authorbors <bors@rust-lang.org>
Thu, 7 Apr 2016 21:17:56 +0000 (14:17 -0700)
committerbors <bors@rust-lang.org>
Thu, 7 Apr 2016 21:17:56 +0000 (14:17 -0700)
Implement top-level overrides

This commit is an implementation of top-level overrides to be encoded into the
manifest itself directly. This style of override is distinct from the existing
`paths` support in `.cargo/config` in two important ways:

* Top level overrides are intended to be checked in and shared amongst all
  developers of a project.
* Top level overrides are reflected in `Cargo.lock`.

The second point is crucially important here as it will ensure that an override
on one machine behaves the same as an override on another machine. This solves
many long-standing problems with `paths`-based overrides which suffer from some
level of nondeterminism as they're not encoded.

From a syntactical point of view, an override looks like:

```toml
[replace]
"libc:0.2.0" = { git = 'https://github.com/my-username/libc';, branch = '0.2-fork' }
```

This declaration indicates that whenever resolution would otherwise encounter
the `libc` package version 0.2.0 from crates.io, it should instead replace it
with the custom git dependency on a specific branch.

The key "libc:0.2.0" here is actually a package id specification which will
allow selecting various components of a graph. For example the same named
package coming from two distinct locations can be selected against, as well as
multiple versions of one crate in a dependency graph. The replacement dependency
has the same syntax as the `[dependencies]` section of Cargo.toml.

One of the major uses of this syntax will be, for example, using a temporary
fork of a crate while the changes are pushed upstream to the original repo. This
will avoid the need to change the intermediate projects immediately, and over
time once fixes have landed upstream the `[replace]` section in a `Cargo.toml`
can be removed.

There are also two crucial restrictions on overrides.

* A crate with the name `foo` can only get overridden with packages also of
  the name `foo`.
* A crate can only get overridden with a crate of the exact same version.

A consequence of these restrictions is that crates.io cannot be used to replace
anything from crates.io. There's only one version of something on crates.io, so
there's nothing else to replace it with (name/version are a unique key).

Closes #942

1  2 
src/cargo/core/registry.rs
src/cargo/ops/cargo_generate_lockfile.rs
src/cargo/ops/lockfile.rs
src/cargo/ops/resolve.rs
src/cargo/util/toml.rs
tests/tests.rs

Simple merge
Simple merge
Simple merge
index 44b24705acc51b94388ccdf1418a62e05260089c,dcaed14762fa4b86ebb82788414380a658330578..191e48b34e6fc2ecaca518b5a60218a50f2fa29e
@@@ -538,20 -540,38 +540,50 @@@ impl TomlManifest 
                                                Some(Kind::Development)));
                  }
              }
+             if let Some(ref map) = self.replace {
+                 for (spec, replacement) in map {
+                     let spec = try!(PackageIdSpec::parse(spec));
+                     let version_specified = match *replacement {
+                         TomlDependency::Detailed(ref d) => d.version.is_some(),
+                         TomlDependency::Simple(..) => true,
+                     };
+                     if version_specified {
+                         bail!("replacements cannot specify a version \
+                                requirement, but found one for `{}`", spec);
+                     }
+                     let dep = try!(replacement.to_dependency(spec.name(),
+                                                              &mut cx,
+                                                              None));
+                     let dep = {
+                         let version = try!(spec.version().chain_error(|| {
+                             human(format!("replacements must specify a version \
+                                            to replace, but `{}` does not",
+                                           spec))
+                         }));
+                         let req = VersionReq::exact(version);
+                         dep.clone_inner().set_version_req(req)
+                            .into_dependency()
+                     };
+                     replace.push((spec, dep));
+                 }
+             }
          }
  
 +        {
 +            let mut names_sources = HashMap::new();
 +            for dep in deps.iter() {
 +                let name = dep.name();
 +                let prev = names_sources.insert(name, dep.source_id());
 +                if prev.is_some() && prev != Some(dep.source_id()) {
 +                    bail!("found duplicate dependency name {}, but all \
 +                           dependencies must have a unique name", name);
 +                }
 +            }
 +        }
 +
          let exclude = project.exclude.clone().unwrap_or(Vec::new());
          let include = project.include.clone().unwrap_or(Vec::new());
  
diff --cc tests/tests.rs
Simple merge