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

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

This comment has been minimized.

Copy link

commented Jan 17, 2018

r? @matklad

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

@sfackler

This comment has been minimized.

Copy link
Member

commented Jan 17, 2018

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

This comment has been minimized.

Copy link
Member Author

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

This comment has been minimized.

Copy link
Member

commented Jan 17, 2018

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.

This comment has been minimized.

Copy link
@matklad

matklad Jan 19, 2018

Member

Wrong comment

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

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

This comment has been minimized.

Copy link
@matklad

matklad Jan 19, 2018

Member

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" },
]

This comment has been minimized.

Copy link
@matklad

matklad Jan 19, 2018

Member

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"}

This comment has been minimized.

Copy link
@alexcrichton

alexcrichton Jan 19, 2018

Author Member

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

@bors

This comment has been minimized.

Copy link
Contributor

commented Jan 20, 2018

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

@alexcrichton alexcrichton force-pushed the alexcrichton:rename-via-as branch from 0addcd2 to 6e90f36 Jan 22, 2018
@bors

This comment has been minimized.

Copy link
Contributor

commented Jan 25, 2018

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

@alexcrichton alexcrichton force-pushed the alexcrichton:rename-via-as branch from 6e90f36 to 102ee8a Jan 25, 2018
@bors

This comment has been minimized.

Copy link
Contributor

commented Feb 6, 2018

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

@retep998

This comment has been minimized.

Copy link
Member

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

This comment has been minimized.

Copy link
Contributor

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 alexcrichton force-pushed the alexcrichton:rename-via-as branch from 102ee8a to 3d7dcdf Feb 12, 2018
@alexcrichton

This comment has been minimized.

Copy link
Member Author

commented Feb 12, 2018

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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link
Member Author

commented Feb 12, 2018

@alexreg, correct!

@alexreg

This comment has been minimized.

Copy link
Contributor

commented Feb 12, 2018

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

Copy link
Member

left a comment

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>,

This comment has been minimized.

Copy link
@matklad

matklad Feb 12, 2018

Member

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).

This comment has been minimized.

Copy link
@alexcrichton

alexcrichton Feb 12, 2018

Author Member

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

This comment has been minimized.

Copy link
@alexreg

alexreg Feb 13, 2018

Contributor

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 alexcrichton force-pushed the alexcrichton:rename-via-as branch from 3d7dcdf to 79942fe Feb 12, 2018
@alexcrichton

This comment has been minimized.

Copy link
Member Author

commented Feb 12, 2018

@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

This comment has been minimized.

Copy link
Member

commented Feb 19, 2018

@bors r+

@bors

This comment has been minimized.

Copy link
Contributor

commented Feb 19, 2018

📌 Commit 79942fe has been approved by matklad

@alexreg

This comment has been minimized.

Copy link
Contributor

commented Feb 19, 2018

Good work with this @alexcrichton! Thanks. 👍🏻

@bors

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link
Contributor

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
3 checks passed
3 checks passed
continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
homu Test successful
Details
@aaronsky

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Contributor

commented Jul 21, 2018

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

@aldanor

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Member Author

commented Nov 16, 2018

@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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Member Author

commented Nov 16, 2018

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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Contributor

commented May 8, 2019

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

@updogliu

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link
Member

commented May 9, 2019

Mutually exclusive features are not supported.

@updogliu

This comment has been minimized.

Copy link

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

This comment has been minimized.

Copy link

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
Projects
None yet
You can’t perform that action at this time.