Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement renaming dependencies in the manifest #4953

Merged
merged 1 commit into from
Feb 19, 2018

Conversation

alexcrichton
Copy link
Member

@alexcrichton alexcrichton commented Jan 17, 2018

This commit implements a new unstable feature for manifests which allows
renaming a crate when depending on it. For example you can do:

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:

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!

cc #1311

@rust-highfive
Copy link

r? @matklad

(rust_highfive has picked a reviewer for you, use r? to override)

@sfackler
Copy link
Member

This may have already been discussed elsewhere, but I think it would be a bit less confusing to do something like this:

[dependencies]
foo = "0.1"
bar = { version = "0.1", registry = "custom", name = "foo" }
baz = { git = "https://github.com/foo/bar", name = "foo" }

It avoids the extra array, and makes it so that the set of names you're importing (modulo crate/lib naming differences) are all in the same place.

@alexcrichton
Copy link
Member Author

alexcrichton commented Jan 17, 2018

Heh that's actually the first syntax I thought of as well, although it was changed due to rust-lang/rfcs#2126 (comment), namely:

the desired crate alias as the leading key in Cargo.toml conflates packages and crates (a common conflation that we should probably work on separately).

For example today you actually specify package names as the key in [dependencies] whereas the as/alias business we want is to rename the crate. (it's possible for packages to be named differently from their library crate).

That being said I also just realized something that can be troubling. How, for example, do we enable features for these dependencies? For example:

[dependencies]
foo = [
   { version = "0.1" },
   { version = "0.1", registry = "custom", as = "bar" },
   { git = 'https://github.com/foo/bar', as = "baz" },
]

[features]
some-feature = ["foo/another-feature"] # what version of `foo`?

This I think is much clearer with what @sfackler is thinking:

[dependencies]
foo = "0.1"
bar = { version = "0.1", registry = "custom", name = "foo" }
baz = { git = "https://github.com/foo/bar", name = "foo" }

[features]
some-feature = ["foo/another-feature"] # aha, the crates.io version!

That may tip my own opinion towards the latter!

@sfackler
Copy link
Member

Oh yeah, good point. It's too bad you're allowed to set the library name separately from the crate name :(

@@ -125,6 +125,9 @@ features! {

// Downloading packages from alternative registry indexes.
[unstable] alternative_registries: bool,

// Downloading packages from alternative registry indexes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong comment

.filter_map(|d| d.rename())
.next();

v.push(name.unwrap_or(&dep.target.crate_name()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we make this .unwrap_or an utility method of something? Like fn crate_name(&self) -> String, which deals with as and, probably, with translating - to _ as well.

bar = [
{ version = "0.1.0" },
{ version = "0.2.0", as = "baz" },
]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm the syntax does not look too natural to me. Is the following a valid TOML?

[dependencies]
bar = "0.1.0"
bar = { version = "0.2.0", as = "baz"}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately TOML-the-spec doesn't allow for something like that b/c it's counted as a redefinition of an existing key :(

@bors
Copy link
Contributor

bors commented Jan 20, 2018

☔ The latest upstream changes (presumably #4844) made this pull request unmergeable. Please resolve the merge conflicts.

@bors
Copy link
Contributor

bors commented Jan 25, 2018

☔ The latest upstream changes (presumably #4957) made this pull request unmergeable. Please resolve the merge conflicts.

@bors
Copy link
Contributor

bors commented Feb 6, 2018

☔ The latest upstream changes (presumably #5011) made this pull request unmergeable. Please resolve the merge conflicts.

@retep998
Copy link
Member

retep998 commented Feb 8, 2018

If we wanted to make it clear where we're specifying the package name vs the crate name, perhaps we could do something like...

[dependencies]
foo = "0.1"
bar = { version = "0.1", registry = "custom", package = "foo" }
baz = { git = "https://github.com/foo/bar", package = "foo" }

[features]
some-feature = ["foo/another-feature"] # aha, the crates.io version!

@alexreg
Copy link
Contributor

alexreg commented Feb 8, 2018

@alexcrichton Great work on this. I think this feature is very much due. Funnily enough, I was just going to suggest the key package (or pkg), but @retep998 just beat me to it. The semantics would be changed from your original approach, and the current method, with keys now representing user-facing crate names rather than package names, but it would at least be operationally backwards-compatible (in the case of no package value, that is). In other words, the value of the package field would default to the user-facing crate name (i.e. alias). This seems quite nice to me.

It's also worth noting that this PR helps with rust-lang/rust#44660.

Finally, maybe add a "fixes #1311" to your original PR message?

@alexcrichton
Copy link
Member Author

I've updated with the feedback so far (and switched to the non-array syntax using a package key), re-r? @matklad

@alexreg oh we typically like to keep the issues open to track the feature, but I should have cc'd it in the original message for sure!

@alexreg
Copy link
Contributor

alexreg commented Feb 12, 2018

@alexcrichton Yeah, that's fair enough. Anyway, the new semantics are with the alias on the LHS of the =, right? (i.e. what I elaborated on above.)

@alexcrichton
Copy link
Member Author

@alexreg, correct!

@alexreg
Copy link
Contributor

alexreg commented Feb 12, 2018

Super. Hopefully @matklad can review soon and then we can get this merged.

Copy link
Member

@matklad matklad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here are a couple of tests I think we could add

  1. Using exactly the same library twice. This presumably should be an error

  2. Renaming a package whose library name does not match the package namme

@@ -27,6 +27,7 @@ struct Inner {
specified_req: bool,
kind: Kind,
only_match_name: bool,
rename: Option<String>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd call it package, to be consistent with the package in the surface syntax, and to make it more clear in which direction renaming goes (I.e, in theory you can have a crate foo which is renamed to pacakge baz, or, the other way around, a package baz which is renamed to crate foo).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh this is slightly different through b/c it's not actually the package, but rather what the dependency is renaming the crate to

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, even though there's a one-to-one correspondence with the package attribute, rename probably makes more sense in code (perhaps with a comment?)... that's my thought, anyway.

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!
@alexcrichton
Copy link
Member Author

@matklad sure yeah, although for specifying one crate twice we're already pretty bad about that today unfortunately. For example something like:

[dependencies]
foo = "0.1"

[target.'cfg(unix)'.dependencies]
foo = "0.1"

is accepted and works, so I figure we can probably just let rustc sort it out if it's specified multiple times.

@matklad
Copy link
Member

matklad commented Feb 19, 2018

@bors r+

@bors
Copy link
Contributor

bors commented Feb 19, 2018

📌 Commit 79942fe has been approved by matklad

@alexreg
Copy link
Contributor

alexreg commented Feb 19, 2018

Good work with this @alexcrichton! Thanks. 👍🏻

@bors
Copy link
Contributor

bors commented Feb 19, 2018

⌛ Testing commit 79942fe with merge 4f1c6db...

bors added a commit that referenced this pull request Feb 19, 2018
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!

cc #1311
@bors
Copy link
Contributor

bors commented Feb 19, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: matklad
Pushing 4f1c6db to master...

@bors bors merged commit 79942fe into rust-lang:master Feb 19, 2018
@aaronsky
Copy link

aaronsky commented Jul 21, 2018

With this merged, does this mean that the item under rust-lang/rust#44931 should be updated? Apologies if this is the wrong place to ask about this

@alexreg
Copy link
Contributor

alexreg commented Jul 21, 2018

@aaronsky Yep, it should be. Maybe ping @aturon on that thread to update the checkbox(es). :-)

@aldanor
Copy link

aldanor commented Nov 15, 2018

Here's a curious (but not so improbable) edge case with proc macros. Wondering how should this be handled? (or am I misunderstanding something)

  • There's three crates, foo, foo-derive and bar
  • foo-derive is a proc-macro crate which depends on crate foo (without any renaming) and mentions ::foo::Foo in the resulting token stream
  • bar is a top-level crate that depends on both foo and foo-derive
  • Using the rename feature, in bar's manifest we have baz = { package = "foo" }

Now, if we use the derive proc macro in crate bar, it seems to be broken upon use, like so:

^^^^^^ Could not find `foo` in `{{root}}`

What would be a proper way to refer to it then?..

@aldanor
Copy link

aldanor commented Nov 15, 2018

Here's a concrete example, using num-derive.

Cargo.toml:

cargo-features = ["rename-dependency"]

[package]
name = "rename-test"
version = "0.1.0"
edition = "2018"

[dependencies]
foo = { package = "num-traits", version = "0.2" }
num-derive = "0.2"

src/lib.rs:

use num_derive::{FromPrimitive, ToPrimitive};

#[derive(FromPrimitive, ToPrimitive)]
enum Color { Red, Blue, Green }

... which results in...

error[E0463]: can't find crate for `_num_traits`
 --> src/lib.rs:3:25
  |
3 | #[derive(FromPrimitive, ToPrimitive)]
  |                         ^^^^^^^^^^^ can't find crate

error: aborting due to previous error

For more information about this error, try `rustc --explain E0463`.

@alexcrichton
Copy link
Member Author

@aldanor that's a general problem with hygiene and procedural macros, and most procedural macros only work if the corresponding library crate is imported with one precise name (and not renamed). There's no way, for example, to get importing the serde crate under a different name to work with its derive modes.

@aldanor
Copy link

aldanor commented Nov 16, 2018

@alexcrichton Yea, I understand (and this is quite sad because most derive macros depend on some other crate; maybe there should be some crate-resolving syntax like $crate(num-traits)?.. This will keep being an annoying problem in the future. This is out of scope of this PR though).

With the example above though, if you revert back to 2015-edition way and remove renaming from the manifest, it does work with

[dependencies]
num-traits = "0.2"
num-derive = "0.2"

and

extern crate num_traits as foo;  // <--
#[macro_use] extern crate num_derive;

#[derive(FromPrimitive, ToPrimitive)]
enum Color { Red, Blue, Green }

(because of extern crate num_traits as _num_traits line in the macro itself)

Does it mean this particular case is a rename-dependency regression then?

@alexcrichton
Copy link
Member Author

I don't think this is really a regression, you can always remove renaming in the manifest and have use foo as bar; in the code. The usability annoyance would be fixed with a better form of hygiene in procedural macros, and there's not much that Cargo itself can do

@ehuss
Copy link
Contributor

ehuss commented Nov 16, 2018

maybe there should be some crate-resolving syntax

There is an issue open for this at rust-lang/rust#54363.

@updogliu
Copy link

updogliu commented May 8, 2019

Can this enable depending on the same version of a crate but using conflicting features at the same time (with different renaming of course)?

@ehuss
Copy link
Contributor

ehuss commented May 8, 2019

@updogliu A package cannot have two dependencies to the same version of a crate.

@updogliu
Copy link

updogliu commented May 8, 2019

@ehuss What I am trying to achieve is using a crate of the same version but with conflicting features in two members of a workspace. Is that a valid motivation?

@ehuss
Copy link
Contributor

ehuss commented May 8, 2019

If the two members are built separately, then they will each use a different copy of the dependent crate (with different features activated). If the two members are being linked together, then it will unify the features and use a single copy. Conflicting or mutually exclusive features aren't really supported. There are a number of issues in the tracker here to make this more flexible, but we're still in the design phase.

@updogliu
Copy link

updogliu commented May 9, 2019

@ehuss I have an example at here. There are three members in a workspace: foo, bar, common. foo and bar do not depend on each other, but they both depend on common and using mutually exclusive features. Is this kind of use to be supported or not?

@retep998
Copy link
Member

retep998 commented May 9, 2019

Mutually exclusive features are not supported.

@updogliu
Copy link

updogliu commented May 9, 2019

@retep998 I understand that it is currently not supported. But since this nice renaming dependencies feature is added to support using different versions of a crate, maybe it can be augmented a bit to support exclusive features under different renaming as well?

@burdges
Copy link

burdges commented Aug 13, 2019

I presume cargo features only work on nightly installs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.