mirror of
https://github.com/rust-lang/cargo
synced 2024-10-13 19:22:33 +00:00
Implement renaming dependencies in the manifest
This commit implements a new unstable feature for manifests which allows renaming a crate when depending on it. For example you can do: ```toml cargo-features = ["dependencies-as"] ... [dependencies] foo = "0.1" bar = { version = "0.1", registry = "custom", package = "foo" } baz = { git = "https://github.com/foo/bar", package = "foo" } ``` Here three crates will be imported but they'll be made available to the Rust source code via the names `foo` (crates.io), `bar` (the custom registry), and `baz` (the git dependency). The *package* name, however, will be `foo` for all of them. In other words the git repository here would be searched for a crate called `foo`. For example: ```rust extern crate foo; // crates.io extern crate bar; // registry `custom` extern crate baz; // git repository ``` The intention here is to enable a few use cases: * Enable depending on the same named crate from different registries * Allow depending on multiple versions of a crate from one registry * Removing the need for `extern crate foo as bar` syntactically in Rust source Currently I don't think we're ready to stabilize this so it's just a nightly feature, but I'm hoping we can continue to iterate on it!
This commit is contained in:
parent
cb30fba4a3
commit
79942fea1b
|
@ -27,6 +27,7 @@ struct Inner {
|
||||||
specified_req: bool,
|
specified_req: bool,
|
||||||
kind: Kind,
|
kind: Kind,
|
||||||
only_match_name: bool,
|
only_match_name: bool,
|
||||||
|
rename: Option<String>,
|
||||||
|
|
||||||
optional: bool,
|
optional: bool,
|
||||||
default_features: bool,
|
default_features: bool,
|
||||||
|
@ -49,6 +50,7 @@ struct SerializedDependency<'a> {
|
||||||
source: &'a SourceId,
|
source: &'a SourceId,
|
||||||
req: String,
|
req: String,
|
||||||
kind: Kind,
|
kind: Kind,
|
||||||
|
rename: Option<&'a str>,
|
||||||
|
|
||||||
optional: bool,
|
optional: bool,
|
||||||
uses_default_features: bool,
|
uses_default_features: bool,
|
||||||
|
@ -69,6 +71,7 @@ impl ser::Serialize for Dependency {
|
||||||
uses_default_features: self.uses_default_features(),
|
uses_default_features: self.uses_default_features(),
|
||||||
features: self.features(),
|
features: self.features(),
|
||||||
target: self.platform(),
|
target: self.platform(),
|
||||||
|
rename: self.rename(),
|
||||||
}.serialize(s)
|
}.serialize(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,6 +185,7 @@ impl Dependency {
|
||||||
default_features: true,
|
default_features: true,
|
||||||
specified_req: false,
|
specified_req: false,
|
||||||
platform: None,
|
platform: None,
|
||||||
|
rename: None,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -221,6 +225,10 @@ impl Dependency {
|
||||||
self.inner.platform.as_ref()
|
self.inner.platform.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn rename(&self) -> Option<&str> {
|
||||||
|
self.inner.rename.as_ref().map(|s| &**s)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_kind(&mut self, kind: Kind) -> &mut Dependency {
|
pub fn set_kind(&mut self, kind: Kind) -> &mut Dependency {
|
||||||
Rc::make_mut(&mut self.inner).kind = kind;
|
Rc::make_mut(&mut self.inner).kind = kind;
|
||||||
self
|
self
|
||||||
|
@ -261,6 +269,11 @@ impl Dependency {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_rename(&mut self, rename: &str) -> &mut Dependency {
|
||||||
|
Rc::make_mut(&mut self.inner).rename = Some(rename.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Lock this dependency to depending on the specified package id
|
/// Lock this dependency to depending on the specified package id
|
||||||
pub fn lock_to(&mut self, id: &PackageId) -> &mut Dependency {
|
pub fn lock_to(&mut self, id: &PackageId) -> &mut Dependency {
|
||||||
assert_eq!(self.inner.source_id, *id.source_id());
|
assert_eq!(self.inner.source_id, *id.source_id());
|
||||||
|
|
|
@ -159,6 +159,9 @@ features! {
|
||||||
|
|
||||||
// Using epochs
|
// Using epochs
|
||||||
[unstable] epoch: bool,
|
[unstable] epoch: bool,
|
||||||
|
|
||||||
|
// Renaming a package in the manifest via the `package` key
|
||||||
|
[unstable] rename_dependency: bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -947,12 +947,12 @@ Cargo.toml. This warning might turn into a hard error in the future.",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for unit in dep_targets {
|
for dep in dep_targets {
|
||||||
if unit.profile.run_custom_build {
|
if dep.profile.run_custom_build {
|
||||||
cmd.env("OUT_DIR", &cx.build_script_out_dir(&unit));
|
cmd.env("OUT_DIR", &cx.build_script_out_dir(&dep));
|
||||||
}
|
}
|
||||||
if unit.target.linkable() && !unit.profile.doc {
|
if dep.target.linkable() && !dep.profile.doc {
|
||||||
link_to(cmd, cx, &unit)?;
|
link_to(cmd, cx, &unit, &dep)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -960,15 +960,31 @@ Cargo.toml. This warning might turn into a hard error in the future.",
|
||||||
|
|
||||||
fn link_to<'a, 'cfg>(cmd: &mut ProcessBuilder,
|
fn link_to<'a, 'cfg>(cmd: &mut ProcessBuilder,
|
||||||
cx: &mut Context<'a, 'cfg>,
|
cx: &mut Context<'a, 'cfg>,
|
||||||
unit: &Unit<'a>) -> CargoResult<()> {
|
current: &Unit<'a>,
|
||||||
for &(ref dst, _, file_type) in cx.target_filenames(unit)?.iter() {
|
dep: &Unit<'a>) -> CargoResult<()> {
|
||||||
|
for &(ref dst, _, file_type) in cx.target_filenames(dep)?.iter() {
|
||||||
if file_type != TargetFileType::Linkable {
|
if file_type != TargetFileType::Linkable {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let mut v = OsString::new();
|
let mut v = OsString::new();
|
||||||
v.push(&unit.target.crate_name());
|
|
||||||
|
// Unfortunately right now Cargo doesn't have a great way to get a
|
||||||
|
// 1:1 mapping of entries in `dependencies()` to the actual crate
|
||||||
|
// we're depending on. Instead we're left to do some guesswork here
|
||||||
|
// to figure out what `Dependency` the `dep` unit corresponds to in
|
||||||
|
// `current` to see if we're renaming it.
|
||||||
|
//
|
||||||
|
// This I believe mostly works out for now, but we'll likely want
|
||||||
|
// to tighten up this in the future.
|
||||||
|
let name = current.pkg.dependencies()
|
||||||
|
.iter()
|
||||||
|
.filter(|d| d.matches_ignoring_source(dep.pkg.summary()))
|
||||||
|
.filter_map(|d| d.rename())
|
||||||
|
.next();
|
||||||
|
|
||||||
|
v.push(name.unwrap_or(&dep.target.crate_name()));
|
||||||
v.push("=");
|
v.push("=");
|
||||||
v.push(cx.out_dir(unit));
|
v.push(cx.out_dir(dep));
|
||||||
v.push(&path::MAIN_SEPARATOR.to_string());
|
v.push(&path::MAIN_SEPARATOR.to_string());
|
||||||
v.push(&dst.file_name().unwrap());
|
v.push(&dst.file_name().unwrap());
|
||||||
cmd.arg("--extern").arg(&v);
|
cmd.arg("--extern").arg(&v);
|
||||||
|
|
|
@ -142,7 +142,7 @@ type TomlBenchTarget = TomlTarget;
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum TomlDependency {
|
pub enum TomlDependency {
|
||||||
Simple(String),
|
Simple(String),
|
||||||
Detailed(DetailedTomlDependency)
|
Detailed(DetailedTomlDependency),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> de::Deserialize<'de> for TomlDependency {
|
impl<'de> de::Deserialize<'de> for TomlDependency {
|
||||||
|
@ -193,6 +193,7 @@ pub struct DetailedTomlDependency {
|
||||||
default_features: Option<bool>,
|
default_features: Option<bool>,
|
||||||
#[serde(rename = "default_features")]
|
#[serde(rename = "default_features")]
|
||||||
default_features2: Option<bool>,
|
default_features2: Option<bool>,
|
||||||
|
package: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
@ -917,16 +918,28 @@ impl TomlDependency {
|
||||||
cx: &mut Context,
|
cx: &mut Context,
|
||||||
kind: Option<Kind>)
|
kind: Option<Kind>)
|
||||||
-> CargoResult<Dependency> {
|
-> CargoResult<Dependency> {
|
||||||
let details = match *self {
|
match *self {
|
||||||
TomlDependency::Simple(ref version) => DetailedTomlDependency {
|
TomlDependency::Simple(ref version) => {
|
||||||
version: Some(version.clone()),
|
DetailedTomlDependency {
|
||||||
.. Default::default()
|
version: Some(version.clone()),
|
||||||
},
|
..Default::default()
|
||||||
TomlDependency::Detailed(ref details) => details.clone(),
|
}.to_dependency(name, cx, kind)
|
||||||
};
|
}
|
||||||
|
TomlDependency::Detailed(ref details) => {
|
||||||
|
details.to_dependency(name, cx, kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if details.version.is_none() && details.path.is_none() &&
|
impl DetailedTomlDependency {
|
||||||
details.git.is_none() {
|
fn to_dependency(&self,
|
||||||
|
name: &str,
|
||||||
|
cx: &mut Context,
|
||||||
|
kind: Option<Kind>)
|
||||||
|
-> CargoResult<Dependency> {
|
||||||
|
if self.version.is_none() && self.path.is_none() &&
|
||||||
|
self.git.is_none() {
|
||||||
let msg = format!("dependency ({}) specified without \
|
let msg = format!("dependency ({}) specified without \
|
||||||
providing a local path, Git repository, or \
|
providing a local path, Git repository, or \
|
||||||
version to use. This will be considered an \
|
version to use. This will be considered an \
|
||||||
|
@ -934,11 +947,11 @@ impl TomlDependency {
|
||||||
cx.warnings.push(msg);
|
cx.warnings.push(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if details.git.is_none() {
|
if self.git.is_none() {
|
||||||
let git_only_keys = [
|
let git_only_keys = [
|
||||||
(&details.branch, "branch"),
|
(&self.branch, "branch"),
|
||||||
(&details.tag, "tag"),
|
(&self.tag, "tag"),
|
||||||
(&details.rev, "rev")
|
(&self.rev, "rev")
|
||||||
];
|
];
|
||||||
|
|
||||||
for &(key, key_name) in &git_only_keys {
|
for &(key, key_name) in &git_only_keys {
|
||||||
|
@ -951,7 +964,7 @@ impl TomlDependency {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let registry_id = match details.registry {
|
let registry_id = match self.registry {
|
||||||
Some(ref registry) => {
|
Some(ref registry) => {
|
||||||
cx.features.require(Feature::alternative_registries())?;
|
cx.features.require(Feature::alternative_registries())?;
|
||||||
SourceId::alt_registry(cx.config, registry)?
|
SourceId::alt_registry(cx.config, registry)?
|
||||||
|
@ -960,10 +973,10 @@ impl TomlDependency {
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_source_id = match (
|
let new_source_id = match (
|
||||||
details.git.as_ref(),
|
self.git.as_ref(),
|
||||||
details.path.as_ref(),
|
self.path.as_ref(),
|
||||||
details.registry.as_ref(),
|
self.registry.as_ref(),
|
||||||
details.registry_index.as_ref(),
|
self.registry_index.as_ref(),
|
||||||
) {
|
) {
|
||||||
(Some(_), _, Some(_), _) |
|
(Some(_), _, Some(_), _) |
|
||||||
(Some(_), _, _, Some(_))=> bail!("dependency ({}) specification is ambiguous. \
|
(Some(_), _, _, Some(_))=> bail!("dependency ({}) specification is ambiguous. \
|
||||||
|
@ -978,7 +991,7 @@ impl TomlDependency {
|
||||||
cx.warnings.push(msg)
|
cx.warnings.push(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
let n_details = [&details.branch, &details.tag, &details.rev]
|
let n_details = [&self.branch, &self.tag, &self.rev]
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|d| d.is_some())
|
.filter(|d| d.is_some())
|
||||||
.count();
|
.count();
|
||||||
|
@ -990,9 +1003,9 @@ impl TomlDependency {
|
||||||
cx.warnings.push(msg)
|
cx.warnings.push(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
let reference = details.branch.clone().map(GitReference::Branch)
|
let reference = self.branch.clone().map(GitReference::Branch)
|
||||||
.or_else(|| details.tag.clone().map(GitReference::Tag))
|
.or_else(|| self.tag.clone().map(GitReference::Tag))
|
||||||
.or_else(|| details.rev.clone().map(GitReference::Rev))
|
.or_else(|| self.rev.clone().map(GitReference::Rev))
|
||||||
.unwrap_or_else(|| GitReference::Branch("master".to_string()));
|
.unwrap_or_else(|| GitReference::Branch("master".to_string()));
|
||||||
let loc = git.to_url()?;
|
let loc = git.to_url()?;
|
||||||
SourceId::for_git(&loc, reference)?
|
SourceId::for_git(&loc, reference)?
|
||||||
|
@ -1023,24 +1036,33 @@ impl TomlDependency {
|
||||||
(None, None, None, None) => SourceId::crates_io(cx.config)?,
|
(None, None, None, None) => SourceId::crates_io(cx.config)?,
|
||||||
};
|
};
|
||||||
|
|
||||||
let version = details.version.as_ref().map(|v| &v[..]);
|
let (pkg_name, rename) = match self.package {
|
||||||
|
Some(ref s) => (&s[..], Some(name)),
|
||||||
|
None => (name, None),
|
||||||
|
};
|
||||||
|
|
||||||
|
let version = self.version.as_ref().map(|v| &v[..]);
|
||||||
let mut dep = match cx.pkgid {
|
let mut dep = match cx.pkgid {
|
||||||
Some(id) => {
|
Some(id) => {
|
||||||
Dependency::parse(name, version, &new_source_id,
|
Dependency::parse(pkg_name, version, &new_source_id,
|
||||||
id, cx.config)?
|
id, cx.config)?
|
||||||
}
|
}
|
||||||
None => Dependency::parse_no_deprecated(name, version, &new_source_id)?,
|
None => Dependency::parse_no_deprecated(name, version, &new_source_id)?,
|
||||||
};
|
};
|
||||||
dep.set_features(details.features.unwrap_or_default())
|
dep.set_features(self.features.clone().unwrap_or_default())
|
||||||
.set_default_features(details.default_features
|
.set_default_features(self.default_features
|
||||||
.or(details.default_features2)
|
.or(self.default_features2)
|
||||||
.unwrap_or(true))
|
.unwrap_or(true))
|
||||||
.set_optional(details.optional.unwrap_or(false))
|
.set_optional(self.optional.unwrap_or(false))
|
||||||
.set_platform(cx.platform.clone())
|
.set_platform(cx.platform.clone())
|
||||||
.set_registry_id(®istry_id);
|
.set_registry_id(®istry_id);
|
||||||
if let Some(kind) = kind {
|
if let Some(kind) = kind {
|
||||||
dep.set_kind(kind);
|
dep.set_kind(kind);
|
||||||
}
|
}
|
||||||
|
if let Some(rename) = rename {
|
||||||
|
cx.features.require(Feature::rename_dependency())?;
|
||||||
|
dep.set_rename(rename);
|
||||||
|
}
|
||||||
Ok(dep)
|
Ok(dep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,7 +193,8 @@ fn cargo_metadata_with_deps_and_version() {
|
||||||
"req": "^0.0.1",
|
"req": "^0.0.1",
|
||||||
"source": "registry+[..]",
|
"source": "registry+[..]",
|
||||||
"target": null,
|
"target": null,
|
||||||
"uses_default_features": true
|
"uses_default_features": true,
|
||||||
|
"rename": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"features": {},
|
"features": {},
|
||||||
|
@ -228,7 +229,8 @@ fn cargo_metadata_with_deps_and_version() {
|
||||||
"req": "*",
|
"req": "*",
|
||||||
"source": "registry+[..]",
|
"source": "registry+[..]",
|
||||||
"target": null,
|
"target": null,
|
||||||
"uses_default_features": true
|
"uses_default_features": true,
|
||||||
|
"rename": null
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"features": {},
|
"features": {},
|
||||||
|
|
120
tests/rename-deps.rs
Normal file
120
tests/rename-deps.rs
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
extern crate cargo;
|
||||||
|
extern crate cargotest;
|
||||||
|
extern crate hamcrest;
|
||||||
|
|
||||||
|
use cargotest::support::{project, execs};
|
||||||
|
use cargotest::support::registry::Package;
|
||||||
|
use cargotest::ChannelChanger;
|
||||||
|
use hamcrest::assert_that;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gated() {
|
||||||
|
let p = project("foo")
|
||||||
|
.file("Cargo.toml", r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bar = { package = "foo", version = "0.1" }
|
||||||
|
"#)
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_that(p.cargo("build").masquerade_as_nightly_cargo(),
|
||||||
|
execs().with_status(101)
|
||||||
|
.with_stderr("\
|
||||||
|
error: failed to parse manifest at `[..]`
|
||||||
|
|
||||||
|
Caused by:
|
||||||
|
feature `rename-dependency` is required
|
||||||
|
|
||||||
|
consider adding `cargo-features = [\"rename-dependency\"]` to the manifest
|
||||||
|
"));
|
||||||
|
|
||||||
|
let p = project("bar")
|
||||||
|
.file("Cargo.toml", r#"
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bar = { version = "0.1", package = "baz" }
|
||||||
|
"#)
|
||||||
|
.file("src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_that(p.cargo("build").masquerade_as_nightly_cargo(),
|
||||||
|
execs().with_status(101)
|
||||||
|
.with_stderr("\
|
||||||
|
error: failed to parse manifest at `[..]`
|
||||||
|
|
||||||
|
Caused by:
|
||||||
|
feature `rename-dependency` is required
|
||||||
|
|
||||||
|
consider adding `cargo-features = [\"rename-dependency\"]` to the manifest
|
||||||
|
"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rename_dependency() {
|
||||||
|
Package::new("bar", "0.1.0").publish();
|
||||||
|
Package::new("bar", "0.2.0").publish();
|
||||||
|
|
||||||
|
let p = project("foo")
|
||||||
|
.file("Cargo.toml", r#"
|
||||||
|
cargo-features = ["rename-dependency"]
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bar = { version = "0.1.0" }
|
||||||
|
baz = { version = "0.2.0", package = "bar" }
|
||||||
|
"#)
|
||||||
|
.file("src/lib.rs", "
|
||||||
|
extern crate bar;
|
||||||
|
extern crate baz;
|
||||||
|
")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_that(p.cargo("build").masquerade_as_nightly_cargo(),
|
||||||
|
execs().with_status(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rename_with_different_names() {
|
||||||
|
let p = project("foo")
|
||||||
|
.file("Cargo.toml", r#"
|
||||||
|
cargo-features = ["rename-dependency"]
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "foo"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
baz = { path = "bar", package = "bar" }
|
||||||
|
"#)
|
||||||
|
.file("src/lib.rs", "
|
||||||
|
extern crate baz;
|
||||||
|
")
|
||||||
|
.file("bar/Cargo.toml", r#"
|
||||||
|
[project]
|
||||||
|
name = "bar"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = []
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "random_name"
|
||||||
|
"#)
|
||||||
|
.file("bar/src/lib.rs", "")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
assert_that(p.cargo("build").masquerade_as_nightly_cargo(),
|
||||||
|
execs().with_status(0));
|
||||||
|
}
|
Loading…
Reference in a new issue