-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Profile specific feature selection. #1956
Conversation
text/0000-profile-features.md
Outdated
features = ["foo"] | ||
|
||
[profile.dev.dependencies.foo] | ||
version = ["bar"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you got a mistake here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes that should say features
😊
text/0000-profile-features.md
Outdated
|
||
* The `features` table for a profile is *merged* with the base `features` | ||
table; each member of the `features` table defined in the profile-specific | ||
table *replaces* the member in the base table (they are not merged together). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the difference of this proposal to simply replacing the features table?
Also, the toml standard calls them arrays btw, and calls tables what you call objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merging the table means that you don't need to repeat all of its elements. For example:
[features]
foo = []
bar = []
[profile.dev.features]
bar = ["foo"]
If we replaced the table, this wouln't be sensical because there would no longer be a foo
feature.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I think I got confused a bit, and thought you meant the features array (the features = ["foo", "bar", "baz"] thing), nvm.
Regarding the motivation section I couldn't find any mentions of concrete use cases in the motivation section, only general "sometimes users wish to control". I'm interested in examples for all profiles, but especially in examples in dev vs release. Regarding replacing of settings (features table, dependencies table, ...) in the default config What to do in the case when two profiles are used simultaneously? E.g. test and release? Which setup wins? Also, replacing settings would be inconsistent with what dev dependencies do. ATM you get an error when you include a crate both into dev dependencies and dependencies, and those two inclusions vary in any way (like source of inclusion, or version). IMO the only place where we should allow replacements is by the choice of features. Regarding dev-dependencies I think if we allow dev-dependencies to be set per profile, this addition would lead to a great deal of confusion. First about whether something should be put into the default dev-dependencies or into the dependencies of a profile, and then second about whether some profile specific dependency should be put into its dev-dependencies or not. Giving an example: As author of a crate A, you will likely ask yourself whether to put some dependency B which is only needed to test A itself into the dev-dependencies section or into the dependencies section of the test profile. I'd say the dev-dependencies section should be preferred, as in (at least I think so, haven't confirmed it) when you This example doesn't just expose a source for confusion, but maybe also hints to whether we should have a Another question worth considering is that there is the dev-dependencies use case for deps only needed by the examples you have. Obviously, if we chose to deprecate dev-dependencies, those need a new home as well. Maybe we could introduce an Generally I would support deprecating dev-dependencies overall (as they are mostly a simpler version of the proposed per profile dependencies) as long as the proposed replacement is simple and supports all use cases dev-dependencies supported. I'd like to propose that cargo should error if it encounters both per-profile and dev-dependencies. This is 100% compatible with existing |
Yea, the use cases are mentioned in detailed design and should be prefigured in the motivation section. I'll edit.
As we discussed in IRC, I don't think this happens today (
This is the motivation for the proposal, anything else would be quite questionable (such as changing the version of a dependency in a profile). I didn't rule them out because it seemed to me like trying to do those things would be self-deterring & maybe there are valid use cases I haven't imagined. Will think more about your dev dependencies comments and respond to that later. |
Repeating what I've pointed out on IRC: if that's the case (very reasonable I think), then the new proposal would break the |
@est31 to clarify you mean it doesn't subsume the use case of I think the conclusion I've drawn is that the answer to the question "can we deprecate dev-dependencies?" is "no." |
Yes. Of course, we could tie the dependencies to the harness and not the profile. But that would obviously be a much bigger change than the one proposed, and be more confusing upfront (although the confusion later on would be lower). I think it would be a bad idea to do it.
Okay, but there is still the question whether the dev-dependencies should be included into the profiles. Thinking this further, with the fact that the harness is separate from the profile, does putting custom features and dependencies, things that actually modify behaviour, into the profile even make sense? I mean essentially it would mean that in profile features and dependencies should only change things like speed or logging verbosity or such, but not actual behaviour or anything that may break the tests. I'm not sure whether we will be able to communicate that, and whether people will actually adopt that change. We could also maybe solve the situation by deprecating the test, bench and doc profiles, idk. |
At first I was really against this. Then I thought some more, and decided the real problem is it's unclear what profiles are supposed to be at all. In any event, my confusion here I think a perfect example of what @eternaleye brought up in #1953 of Cargo's current manifest format |
I don't think I'm in favor of this, since it seems like it tries to make an orthogonal axis out of something that really isn't. In particular, tests seem to me like a when (one that can be skipped), rather than a new axis orthogonal to "release". Tests require a superset of what's needed at runtime - both whatever's needed for normal usage, and whatever harness it uses to exercise it. However, test-time is distinct from build-time - one may need (say) a C compiler, while the other does not. Or testing may require a mocking library that pulls in deps that someone who is building wants to avoid. Meanwhile, "release" is almost totally unconnected to time. In fact, I'd consider it a significant antipattern to have different dependencies in "release" mode, as then you fundamentally are not testing the same code as you release. |
@Ericson2314 & @eternaleye both of your points are well taken, but I do have a response to them. @Ericson2314 like you, I am discomfited at the number of not-entirely-orthogonal knobs that cargo exposes to the user. In particular, dev-deps and profiles seem to be overlapping in purpose, with the relationship between different 'harnesses' (tests, examples, doc tests) and profiles extremely muddy (to put a point on it: I discovered, only by reading the source, that But that's why I wrote this RFC as a minimal extension of an existing feature, with potential to cover multiple use cases, rather than creating a new feature that narrowly covered my own use case (conditional compilation in dev vs not dev). I do not think it would be accurate to describe this RFC as "optimizing for the common case and avoiding a combinatory explosion of possibilities." If you want to deal with extricating the relationship between profiles, harnesses, and the three different dependency categories, to create a more orthogonal system, that seems like a potentially fruitful endeavor. That's just not what this RFC is for.
This is certainly a reasonable position. But your position is that conditional compilation is an antipattern. We already have conditional compilation, this RFC is about making it more controllable by end users. My specific use case is to compile different code in release and dev modes. This is a library. In dev I want to dynamically load certain assets, so that users can edit them without recompiling their project. In release I want to include those assets into the source binary - not only for performance reasons but so that the final artefact is a single binary. Now this might sound terrible to you, but put that aside - I'm going to do this regardless. But without this RFC, the way to do it is to use I'd much rather create a |
Whew!
Sorry I worded that poorly, "my confusion here" is over profiles today, not this RFC. My concern about over-optimizing for the common case is also with the current schema, not this RFC.
That is where I get worried. I think the current schema is confusing enough that we really ought to back to the drawing board before making any more piecemeal changes. It may be that simple schema changes are appropriate, but I just don't think we have the design foundation to fully justify them at the time. I rather have an overarching roadmap or something for what knobs Cargo ought to expose, and then think about what syntax (e.g. in light of what exists already) those knobs will take. |
@withoutboats reading over your written motivation in response to @eternaleye's points, I think I am noticing something that might be useful. Crucially there is a tension between describing a development environment for the library, and packaging a library for reuse. In the former case, as with a binary, the Cargo file is the root crate in the dependency dag, and thus we should be pretty lax about what is allowed so as not to get in the developer's way. In the latter case however, we have to consider the needs of downstream developers, who need not concern themselves with the idiosyncrasies of upstream developers. For their sake, we should really cramp down on what libraries are allowed to do to enforce consistency / best practices. I think the solution here is a virtual workspace root. The (perhaps single) library in the workspace should not contain any of these sorts of things, and crates.io should enforce that uploaded libraries don't either. The workspace root however would have all these sorts of local developer conveniences that don't affect the library's definition, but do aid it's development. [As an aside, I am saddened that a crate can serve as a workspace root, and that this appears to be the most common usage. If we didn't allow this, the non-normative-stuff-in-workspace-root approach would be more obvious.] And FWIW, I agree with @eternaleye that, if we indeed ought to have this notion of "profiles", it should be for tweaking "unobservable" things like optimizations, incremental compilation granularity, etc. |
this is a feature for downstream developers' environment |
@withoutboats Hmm? To specifically narrow in on your example, wouldn't downstream always want the assets included into the library, as they are not changing them so no point bothering with reloading on the fly? |
No these are their assets which the library (a web framework) loads for them (e.g. html/css templates) |
Hmm, so transitive usage is very much the intention. |
cc rust-lang/cargo#3870 and rust-lang/cargo#1982, another case for more targeted dependencies, in that case for binaries. (I think this syntax would basically work there as well) |
Overall this looks pretty good to me, thanks @withoutboats! I think it may be worth considering the (cargo) target specific dependencies here (e.g. deps specific to one test, binary, benchmark, etc). This'd cover the PR/issue I cc'd above, and I feel like it would pretty naturally fit into this as well. I sympathize with @eternaleye's concerns about features being different between release/dev in terms of then you're not testing what you're releasing, but I think you also bring up a compelling use case. I continue to want my deploy to "build release mode" and have that do all the right bits by default, and this'd be a good way to configure that. One question I'd have is how would you use release-specific dependencies? Presumably you'd have to write down something in the code to have an One part I've never been super happy with in Cargo is how profiles turned out. The profiles used today are pretty muddied (as you've discovered with cc @matklad, @wycats, @brson, @carols10cents |
For differences like the one I raised, you probably want testing to behave like release (since you're not changing assets during tests!). Of course, someone could still not do that, but that's technically already true (by having significantly different behavior configured on
You raise a good point about profile-specific dependencies, but I think it connects to your next point. It might be worth trying to revamp the profile system before accepting this RFC, possibly connecting it to features, letting you define your own profiles (I could see |
I would love to see more concrete examples in the text of the RFC itself. The one in #1956 (comment) is great! It would be helpful to document existing workarounds as well. Why the existing Let's say you've created a library |
@withoutboats oh so to be clear I think the case of a framework here is a solid one for different behavior. A local library would likely never want to test differently in release/production, but switching that for a well tested framework from a different party seems totally fine. It's also true yeah that release differs from debug mode, not only with I also don't necessarily want to block this RFC on a revamp of the profile system, that's likely to take a long time! Having different sets of default features in different profiles seemed reasonable to me (easy to implement today, doesn't require a revamp) and seems like it would solve a number of your use cases? It was just profile-specific dependencies I wasn't sure how to implement. |
@matklad The intent would be to make
Yes, if we just scoped this RFC to adding features objects (or even just default features) to the profile objects that would be enough for my use case! I agree it makes sense to deal with the holistic issues later. |
I've redrafted this RFC to only control selecting features in specific profiles, rather than defining features & dependencies. It now has two components. First, a default-features key in each profile, which overrides the default features when in that profile. Second, the I've also included a more concrete example in the motivation (the dynamic templates feature I described above). |
I think we must define and document when exactly the current profiles are activated before we implement this RFC. The current behavior is not documented anywhere as far as I know, and is very surprising.
I would love to try to restrict this feature to only |
@matklad That is very surprising, and despite inspecting the code my belief about what profiles were used was wrong. I would like to see this changed so that at very least everything is run under a single profile under each command, |
Yeah a good bit of the current behavior around profiles is relatively historical. Now that we cache binaries based on profile configuration the door's more open to doing more flavorful things (and still having a reasonable experience). The main goal was to have I think it'd be fine to clarify that in this RFC as @matklad was saying, basically saying "these commands map to these profiles" and we can fix the bugs in Cargo in parallel perhaps |
@alexcrichton Sounds good. Should |
Do you say that we want to change what profiles are activated when by cargo? I definitely want this, but it's not exactly 100% backwards compatible :( And if we do want the change, then we'll need to put some more thought into what profiles are exactly. As |
Oh well I don't necessarily want to open the can of worms here, but I think we'd definitely be up for straightening out the story of profiles in cargo... somehow. Not precisely sure how but surely there's a way to do it! |
Sometimes, users wish to control the build of their crates in a manner which | ||
is dependent on which profile it is being built for. Today, this is not easy to | ||
achieve directly; what users tend to do is use a `cfg` option that *usually* | ||
corresponds to the profile being run in, suhc as using `debug_assertions` to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a typo here. suhc
should be such
I believe this is effectively blocked on a forthcoming rationalization of profiles. Closing as postponed for now. |
Rendered