-
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
RFC: Allow packages to specify a set of supported targets #3759
base: master
Are you sure you want to change the base?
Conversation
Somtimes use "crate" instead of "cargo-target" for better readability added section on handling cfgs miscellaneous fixes testing fix dead links
fix links and typos fix example fix note remove note more fixes fixes minor fixes Third draft add note to cargo-target level remove todo
fixes
- fix GH ui bug (indented codeblocks)
I think that there should be a little more explanation about how docs generation works with this. Specifically: Can I build docs for a target that's unsupported, such as if my host machine isn't supported, can i |
This would be a godsend for crates that link against third party libraries, thus limiting supported targets. |
Indeed, and this is especially important since docs.rs needs to be able to generate the docs for all crates. I added it here. |
This comment was marked as off-topic.
This comment was marked as off-topic.
Crates assuming that they're running on one of several targets could even end up making unsafe code decisions based on that fact. Forcing the code to "just build anyway" would naturally lead to problems. And crates can already force themselves to only build only on a specific target, this would not be a new ability, but instead it's only a way to better organize that information. |
The author already mentions in the Prior art that the following Rust can be written: #[cfg(target_arch = "lol"))]
compile_error!("experience bij)"; Please do not make comments on the RFC which do not engage with the RFC's content. |
text/3759-cargo-supported-targets.md
Outdated
|
||
User experience is enhanced by raising an error that fails compilation when the supported targets | ||
of a package are not satisfied by the selected target. A package's `supported-targets` must be a subset | ||
of its dependencies' `supported-targets`, otherwise the build also fails. |
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.
This seems helpful, but at the same time, it may prove annoying to have to copy these across, if a dependency has a very specific list. And it may be non-trivial to enforce.
I think we should downgrade this to a lint, and say that it's best-effort, not mandatory.
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.
This also seems complicated by the fact that I might have a dependency that only supports (say) wasm, but I'm using it solely as a dev-dep (e.g., in tests). I don't think that case merits also setting supported targets on the containing package as a whole, since downstream consumers might not care about that limitation for running on tests.
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.
If it's just a lint then unsafe coders can't depend on this, and they will still need to just use a compile_error!
or something if they're really trying to avoid unsoundness.
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.
This is effectively saying that if you have a dependency that can't build on a target you claim to support, the build will fail and you should instead move it to a target.*.dependencies
table.
imo that seems like something that should be a hard error to me.
I could see loosening the restriction on
When supported-targets is not specified, any target is accepted, so all dependencies must support all targets.
To me "we don't check", like package.rust-version
, requiring supported-targets = ["cfg(true)"]
to get the checking.
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 totally agree with @epage here.
Using the same logic as package.rust-version
is also something I have not thought of but is a great idea.
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.
A problem I could see however is that if a package foo
does not use supported-targets
but one of its dependencies does, then when depending on foo
errors of incompatible targets can be hard to solve since they come from transitive dependencies.
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.
Not quiet sure the problem. When building foo
, you are building for a specific target and the error would be for that target. With package.rust-version
, we check what all packages aren't compatible with the current toolchain and provide a single error message, see https://github.com/rust-lang/cargo/blob/9589831f61a8259919e64c6d68c1a36efc6efd20/src/cargo/ops/cargo_compile/mod.rs#L494-L543
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.
If it's just a lint then unsafe coders can't depend on this, and they will still need to just use a
compile_error!
or something if they're really trying to avoid unsoundness.
supported-targets would still be enforced (modulo some kind of force option); I'm talking about softening the enforcement that a crate's supported-targets is a subset of its dependencies.
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.
FYI this has moved to a Future Possibility
This looks excellent! |
Let's go ahead and start the process of asynchronously checking for consensus. @rfcbot merge |
Team member @joshtriplett has proposed to merge this. The next step is review by the rest of the tagged team members: Concerns:
Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
@rfcbot concern should-crates-have-to-set-supported-targets-to-match-their-dependencies |
text/3759-cargo-supported-targets.md
Outdated
- `required-targets`. Pro: it matches with the naming of `required-features`. Con: `required-features` is a list of features | ||
that must _all_ be enabled (conjunction), whereas `supported-targets` is a list of targets | ||
where _any_ is allowed (disjunction). |
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.
For me the important precedence is that the field means "skip if the qualification is not met" and for that reason I favor using this name.
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.
Apparently, this is different than required-features
as this errors, rather than skips.
I raised this at https://github.com/rust-lang/rfcs/pull/3759/files#r1909208702
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 also feel like using the word "support" carries a lot of unnecessary connotations that complicate the conversation. For example, with MSRV, the Cargo team has been leaning in the direction that "support" is an active process that gets tested. However, applying that here would lead to people over-constraining their targets.
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.
The difference with this and required-features
is that supported-targets
is at the package level, not at the cargo-target level.
So if I run cargo build
in a package with a binary foo
which does not have its required-features
met, then cargo
can still possibly "do work" if there is another cargo-target which has its required-features
met (for instance a library cannot have any required-features
).
However if the supported-targets
are not met for a package, then there is no chance of cargo doing compilation work for that package. That is why the packages are skipped when in a workspace, but an error is raised in a single package. This is just how if I ran cargo build --bin foo
in the previous example, then cargo
errors instead of skipping.
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 do not have a preference for the name, I kept it as is to not confuse people who had read the Pre-RFC. I would not be against changing it.
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 don't think this being at the package or build-target level makes much of a difference. The RFC is starting at the package level and has build-target as a future possibility. In that case, we can treat this as the package is providing the default for all build-targets like package.edition
@rfcbot concern field-name See https://github.com/rust-lang/rfcs/pull/3759/files#r1909195446 |
Co-authored-by: Ed Page <[email protected]>
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.
Is there a feature needed for proc-macros in a workspace like this? With #14903, this would also extend to workspace members that exist to be build scripts.
I'm wondering if there is any design overlap here that could benefit from considering proc-macros / build script cargo check --workspace
filtering in the future.
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 I think this is a viable option. I'm not sure how this should be specified but it is clearly relevant.
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.
This comment is not directly related but I think that a proc-macro is like a build script in that it theoretically should allow any target to build it, because it might be run on any host imaginable.
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, a proc-macro is buildable in any host-supported target.
What I'm trying to highlight is if I run cargo check --target <tuple> --workspace
, then a proc-macro will be built twice
- Once for the host toolchain for being used in the workspace members that depend on it
- Once for the target toolchain because that was requested
I was wondering if there could be a way for people to opt-in to cargo check --target <tuple> --workspace
only building the proc-macro once. This RFC has some similar concepts (skipping of packages based on platform-related info) which is why I was wondering if (1) people find value in this idea and (2) if its close enough to this that we should see if we can design with this in mind as a future possibility
text/3759-cargo-supported-targets.md
Outdated
Even then, it will happen that crates unnecessarily limit their dependents and users, because of | ||
over restrictive `supported-targets`. So, users must be able to disable the lint or error. | ||
To alleviate this, a flag like `--ignore-supported-targets` could be added to `cargo` to ignore the `supported-targets` of a | ||
package, and a field like | ||
```toml | ||
[dependencies] | ||
overrestrictive-dep = { version = "0.1.0", ignore-supported-targets = true } | ||
``` | ||
could be added to ignore the `supported-targets` of a specific dependency. This functionality | ||
could otherwise be added to the [`[patch]` table](https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html#the-patch-section), | ||
or be added as mutable metadata in package registries | ||
([related discussion](https://blog.rust-lang.org/inside-rust/2024/03/26/this-development-cycle-in-cargo-1.78.html#why-is-this-yanked)). |
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.
A question I had related to the interop of ignoring errors and Cargo.lock
trimming:
A package may add restrictions to supported-targets
even though the package builds fine without the restrictions for the sake of Cargo.lock
trimming. Let's say then someone builds the package for a target that is not accepted by ignoring supported-targets
somehow (say with --ignore-supported-targets
). Should the package still trim out dependencies?
I would argue that yes, because I think its important for a package to generate the same lockfile without regards for which target was selected. Also, there was clear intent behind the supported-targets
specification, even if it may not be obvious why. I would argue that a user should be able to bypass the supported-targets
of a package, but should not be able to change the behavior of the package (bringing in extra deps) without making modifications to the code. Similarly, a library should be able to guarantee which dependencies it has no matter the target used. So ignore-supported-targets
on a dependency should not change the dependency graph of the dependency.
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.
But then what happens if cargo build
is run on an arch that's been trimmed out? Does it force-ignore supported-targets
and amend Cargo.lock
? Does it result in the generation of some kind of Cargo.lock.local
? Does it result in every build potentially causing an implicit cargo update
?
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 feel like this is covered previously (#3759 (comment)) and was part of what I was asking to have included in #3759 (comment)
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.
But then what happens if
cargo build
is run on an arch that's been trimmed out? Does it force-ignoresupported-targets
and amendCargo.lock
? Does it result in the generation of some kind ofCargo.lock.local
? Does it result in every build potentially causing an implicitcargo update
?
It does none of that, it builds as if the supported-targets
were respected and prunes the dependencies accordingly. If the build fails or does not behave normally at runtime because a dependency is missing then the user should realize that the supported-targets
were there for a reason.
Keep in mind, this is a future possibility.
# Unresolved questions | ||
[unresolved-questions]: #unresolved-questions | ||
|
||
- Should we strip the `cfg` prefix from the field e.g., `supported-targets = 'target_os = "linux"'`? |
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 don't think we should. This makes the form of the syntax explicit, follows the pattern of how cfg
syntax is used everywhere else, and gives us room to evolve this field
Co-authored-by: Ed Page <[email protected]>
Co-authored-by: Ed Page <[email protected]>
Summary
The addition of
supported-targets
toCargo.toml
. This field is an array oftarget-triple
/cfg
specifications that restricts the set of targets which a package supports. Packages must meet thesupported-targets
of their dependencies, and they can only be built for targets that satisfy theirsupported-targets
.Rendered