rust-lang/cargo

Allow dependencies that only apply to specific cargo targets (bin, example, etc.)

tbu- opened this issue ยท 78 comments

tbu- commented

Use case: I have a library that also produces an executable. The executable needs env_logger in order to be able to log, the library shouldn't carry that dependency though, as it pulls in all sorts of crates (e.g. regex (?)).

@tbu- I have a branch with this mostly working, I'm trying to get it cleaned up and committed "soon"

tbu- commented

Sounds great!

Any updates?

+1, would be great to have it

Also consider as possible filters for dependencies:

  • Examples
  • Integration tests
  • Benchmarks

Also, should it be per-binary (when multiple are provided)?

@gyscos all of that can fall into dev-dependencies bucket. I see no reason for giving ability to set dependency only for benchmarks or something.

@alexcrichton is this a desired feature? Would a PR proposing this get merged?

My personal opinion is that it would be useful for libraries exposing a little cli alongside of a lib (e.g. pulldown-cmark) because it would allow them to, for example, pull in an argument parser (e.g. Clap) without imposing that dependency on all the library users and without the need to create a separate crate for the small binary.

Also, should it be per-binary (when multiple are provided)?

What is your opinion on this and how should it be represented in Cargo.toml?

Would love to have this.

@azerupi yeah I suspect Cargo will end up with something like this eventually, it's certainly a well motivated feature! I wouldn't got he route of dependencies-per-binary yet, though, as we don't similarly also have things like dependencies-per-example or dependencies-per-test

Any progress on this yet?

@alexreg unfortunately, no

This would likely require an RFC as it's an extension to Cargo.toml (e.g. a whole new section for dependencies)

Hello, while this is being worked on is there a temporary "best practices" for handling this kind of project structure (root library crate -> member bin with additional dependencies)? Right now, is the only way to avoid polluting the library dependencies with the binary dependencies is to have each binary be a separate (non-workspaced) crate?

@rminderhoud in the meantime the "best solution" (it's not really that great, but it works) is to split up into separate Cargo projects which can each have their own set of dependencies.

The most general solution would be to allow optional dependencies for every bin target. TOML makes this relatively easy to express:

[[bin]]
name = "foo"

[bin.dependencies]
clap = "1"
term = "0.5"

[[bin]]
name = "bar"

[bin.dependencies.cooldep]
version = "2.0"
default-features = false
features = ["foo"]

In the future, this general concept could also be expanded to examples/tests/etc., if deemed appropriate.

I agree, the syntax & semantics proposed by @crumblingstatue look pretty sensible.

cc #3870 a sample implementation of this.

Another workaround I found to work well, is add an optional feature binaries, and add a required-features field with that to the bin. As far as I know this hasn't been mentioned here before.

Example:

[[bin]]
name = "mybinary"
path = "src/main.rs"
required-features = ["binaries"]

[dependencies]
docopt = { version = "0.8.1", optional = true }

[features]
binaries = ["docopt"]

Trying to install the crate with cargo install mycrate fails with a pretty descriptive error, and as long as you document that it should be installed with cargo install mycrate --features=binaries, I don't really see a big downside to it.

A more consice way would of course be nice though ;)

@hobofan this is sweet

@alexcrichton #3870 was closed with

we may wish to continue to discuss this externally outside of a PR before resubmitting.

Has any more discussion taken place?

Not a significant amount as far as I know at least

To build on this, it would be handy to have a way of declaring dependencies for only some binaries. For example, I have one binary that does EC2 orchestration, and depends on much of rusoto. It'd be sad to have to compile all of that if you're not running that binary.

@hobofan's workaround with required-features is nice, but it's inconvenient to use features for this as it also forces the library crate to be recompiled, which can take a non-trivial amount of time for larger crates. It of course also means that you need to add an additional flag for every binary you want to compile.

This issue has been open some time now. Does anyone plan on tackling it?

@crumblingstatue For consistency, wouldn't "[cooldep.bin.dependencies]" make more sense as it keeps the most specific item on the left, getting more general as you go to the right?

stale commented

As there hasn't been any activity here in over 6 months I've marked this as stale and if no further activity happens for 7 days I will close it.

I'm a bot so this may be in error! If this issue should remain open, could someone (the author, a team member, or any interested party) please comment to that effect?

The team would be especially grateful if such a comment included details such as:

  • Is this still relevant?
  • If so, what is blocking it?
  • Is it known what could be done to help move this forward?

Thank you for contributing!

If you're reading this comment from the distant future, fear not if this was closed automatically. If you believe it's still an issue please leave a comment and a team member can reopen this issue. Opening a new issue is also acceptable!

@Stale bump. (whoops, apparently bots live in a separate namespace)

There is real demand for this feature, so let's keep this open as a reminder that somebody should implement it someday.

@crumblingstatue Looks like it will need RFC. Fancy writing one? I'd be glad to assist and review.

@alexreg Well, I do want this feature, but I'm not sure if I'm up to writing an RFC with a concrete implementation plan.

I think it's useful to track features that are in demand, but I understand if this issue tracker isn't for feature requests.

What do you mean? All I'm saying is that if you really want this feature, go write an RFC. That's the best way to get it. People will assist you too.

@alexreg I'm interested in writing an RFC if you're still up for helping.

@hwchen Great. And yes, I'd be glad to. Would you like to draft it then get my feedback, or discuss it on Discord/Zulip first? (That's where a lot of we Rust maintainers meet.)

@alexreg awesome! let me read over this thread more carefully this weekend, then I'll ping you.

Is there an RFC for this now?

@OxyDeadbeef Afraid not. @hwchen How are things with you? I forgot about this thread until now. Are you interested in drafting something up still, with my assistance?

@alexreg if hwchen isn't interested, I would be

Ok, I'm going to try and get a rough draft of an RFC to @alexreg in the next couple days

@pwoolcoc That's super. I look forward to seeing it. If you have any questions in the meanwhile, tag me here or pop into the #design channel on Rust's Discord server.

Is there anywhere I can be a fly on the wall for that so I can understand how that process works?

@paulevans The process of RFC creation? The README on the RFCs repo provides a good overview, which @pwoolcoc will be following I'm sure. Usually the content of such RFC drafts is guided either my one's own ideas and musings or by pre-RFC discussion (over at internals.rust-lang.org). Sometimes they're a collaborative effort. Eventually an RFC PR is opened on the official Rust RFCs repo, when the community and in particular the Rust Lang (Design) team provides feedback on it. Usually the PR author(s) tag people who have been involved in the process, or expressed an interest. In this case we'll also post a link to the draft RFC here, so you can subscribe to that and follow the discussion and process.

@alexreg I am still working on this, just taking longer than I expected

@pwoolcoc No problem. Here (or on Discord/Zulip) for advice if you need it, otherwise just ping me when you have a draft ready.

Excited Rust beginner here, also eager to try and help in any way i can. Writing a side project & bumped into this issue. I'm on both of the Discords with same username, feel free to ping! โค๏ธ

@Walther We'll post a link to the RFC (draft) PR here when it's ready, which will hopefully be soon. :-)

Has any progress been made with the RFC? I'd be willing to try writing one if it's been dropped again.

@dmarcuse It seems that it has been dropped yes. If you'd be interested in taking it up, then great. I'm happy to provide advice or help review it.

I'll start working on it this weekend and keep you updated!

Super. You can contact me here, or on Discord/Zulip if you like.

@alexreg I started drafting the RFC and sent you a friend request on Discord, could you accept it so I could ask some questions?

@dmarcuse Okay thanks! Accepted now. (I think you can PM me regardless, but yeah, ask away...)

djc commented

@dmarcuse @alexreg have you made any progress since your last comments here?

@djc Yep, @dmarcuse has begun a draft, and I've looked at a bit. Will review some more today.

@djc I have been (very slowly) working on a draft available here - feedback or contributions would be appreciated!

djc commented

@dmarcuse what you have so far looks good! For drawbacks and alternatives, it would be probably be useful to dig into what require_features currently does and how it would interact with your proposed feature. Otherwise this so far seems like a fairly straightforward feature and a mostly natural extension to what we already have, so there's probably not a lot of prior art and future possibilities to talk about.

My advice would be not to try to polish it all to perfection before you submit it, just write up whatever's in your mind and push it, then the community can help you with more feedback.

Thanks for the tips @djc! I'm going to try to work on it more later today.

Yes, we'll specifically get Alex Crichton's feedback on it once we have it in a decent state. :-)

majg0 commented

What are the main reasons for not going with using a workspace or dev-dependencies here?

dev-dependencies are for tests and benchmarks really. Workspaces are designed to solve another problem, and are a cannon for the fly in any case.

Hello @dmarcuse! Any updates on this issue?

I've been meaning to finish up and submit the RFC, but have been really busy with coursework lately - sorry for the delays. The current draft version is available here if you want to read through it, I'd appreciate feedback.

Currently in this exact situation: I have a library, and a binary that uses said library. I'd prefer for them both to be in the same crate, but I also don't want the library to depend on clap, just the binary.

est31 commented

There's an RFC now: rust-lang/rfcs#2887

Created a new RFC for this, rust-lang/rfcs#3020 with the discussion from the previous one.

it'd be nice to have this as you can no longer rely on extern crate in edition 2018.

BTW, this affects bindgen - it optionally depends on clap because of binary but libraries using it in build scripts don't need clap and may forget to turn off default features.. This happened in case of rocksdb for instance.

Perhaps we can enable all features specified by required-features in [[bin]] and [[test]]?

epage commented

#4663 is the issue for auto-enabling required-features, at least for examples with cargo run.

Could we just tag dependencies with the application scope when declaring them? So the compiler/cargo would do the rest:

[dependencies]
foo = { scope = ["bin"], version = "*" } 
bar = { scope = ["lib"], version = "*" }
foobar = { scope = ["bin", "lib"], version = "*" }

Here to chime in with my 2 cents
Since we allready have
[target.*.dependicies]
We could mabey have something like
[target.bin.bin-target.dependicies]
Or if that is not liked there is also the option for
[[bin.bin-target.dependicies]]
Either way this would make deploying
For Example
A client and a server from the same repo a little bit faster and easier to do

fogti commented

@walksanatora I think [bin.dependencies] (which will put a dependency table into the [[bin]] array-table) would be more appropriate.

@hauleth

all of that can fall into dev-dependencies bucket. I see no reason for giving ability to set dependency only for benchmarks or something.

No, there is benefit -- reduce build time of tests, for example.
And reduce bloat and work on linker to strip uneded code. If bin would require test deps, we would need to buld all tests related stuff for binary.

I got hit by this when developing segevfiner/keepawake-rs (segevfiner/keepawake-rs#3), and I'm not sure how to restructure/split my crate in the mean time to avoid this issue. In terms of which part of the crate I should split into a sub-directory in a Cargo workspace and how to handle all the project metadata, README.md and so on, cause I'll have to write two, and/or how to correctly publish in such a configuration...

I tried the users forum but got no answer there as well https://users.rust-lang.org/t/whats-the-convention-for-handling-a-hybrid-library-and-binary-crates-dependencies/84174

New RFC has been proposed: rust-lang/rfcs#3374

detly commented

For the specific use-case of binaries that exist to help with integration tests, I've made a test-binary crate that might help. It's admittedly pretty niche, but it's why I'm watching this issue, so maybe it'll prove useful to others too.

@tbu- I have a branch with this mostly working, I'm trying to get it cleaned up and committed "soon"

"soon"?

New RFC has been proposed: rust-lang/rfcs#3374

This is not the same feature of this issue

I hate to bump a stale thread, but I was curious about this today and google led me here. Is this a "dead" effort, or is there some blocker to implementing this that is not mentioned here?

With rust-lang/rfcs#2887 officially closed, should this issue be closed as not planned? Just wondering.