-
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: Set CARGO_CHECK environment variable when type checking #3748
base: master
Are you sure you want to change the base?
Changes from 2 commits
424ffc2
80d6c3f
aadc16a
a637be2
19c72e3
7d28a8b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,107 @@ | ||||||
- Feature Name: `cargo_check_environment_variable` | ||||||
- Start Date: 2024-12-20 | ||||||
- RFC PR: [rust-lang/rfcs#3748](https://github.com/rust-lang/rfcs/pull/3748) | ||||||
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) | ||||||
|
||||||
# Summary | ||||||
[summary]: #summary | ||||||
|
||||||
Add a new environment variable `CARGO_CHECK` that is set to `true` when running `cargo check` or similar type-checking operations so build scripts can skip expensive compilation steps that are unnecessary for Rust type checking, such as compiling external C++ code in cxx based projects. | ||||||
|
||||||
# Motivation | ||||||
[motivation]: #motivation | ||||||
|
||||||
Rust development heavily relies on IDE tooling like rust-analyzer, which frequently invokes `cargo check` to provide real-time type information and diagnostics. Many projects use build scripts (`build.rs`) to generate Rust code and compile external dependencies. For example: | ||||||
|
||||||
- cxx-rs generates Rust bindings for C++ code and compiles C++ source files | ||||||
- cxx-qt generates Rust bindings for Qt code and runs the Qt Meta-Object Compiler (MOC) | ||||||
- Projects using Protocol Buffers generate Rust code from .proto files | ||||||
- bindgen generates Rust bindings from C/C++ headers | ||||||
|
||||||
Currently, every time rust-analyzer runs `cargo check`, all build scripts must execute their full build process, including steps like compiling C++ code that are only needed for linking but not for type checking. This significantly impacts IDE responsiveness, especially in projects with complex build scripts. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Something seems missing from this description. In my understanding of how to use Cargo, a well-behaved build script should produce an appropriate set of There are of course specific scenarios where it will be rerun regardless, such as if you are editing one of the Can you clarify this? Is the build script in this scenario unable to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are correct, I miswrote that paragraph. I changed it to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But that’s still not true in general. If a build script reruns, then one of the following situations applies:
Changing the Rust code of a crate does not guarantee that its build script will rerun. Build scripts are rerun only if one of their dependencies changes, or if they failed to declare dependencies. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah I see what you mean now. The I'll add a paragraph explaining that detail. I didn't realize the issue I was experiencing is so specific to |
||||||
|
||||||
This is particularly important for projects using cxx-qt and similar frameworks where the build scripts perform extensive code generation and compilation. | ||||||
|
||||||
# Guide-level explanation | ||||||
[guide-level-explanation]: #guide-level-explanation | ||||||
|
||||||
When writing a build script (`build.rs`), you can now check the `CARGO_CHECK` environment variable to determine if the build is being performed for type checking purposes: | ||||||
|
||||||
```rust | ||||||
fn main() { | ||||||
generate_rust_bindings(); | ||||||
|
||||||
// Only compile external code when not type checking | ||||||
if std::env::var("CARGO_CHECK").unwrap() != "true" { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The text below says “The environment variable will not be set…” but this code panics if the environment variable is not set.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, thank you |
||||||
compile_cpp_code(); | ||||||
} | ||||||
} | ||||||
``` | ||||||
|
||||||
This allows build scripts to optimize their behavior based on the build context. When rust-analyzer or a developer runs `cargo check`, the build script can skip time-consuming steps that aren't necessary for type checking. | ||||||
|
||||||
This feature primarily benefits library authors who maintain build scripts, especially those working with external code generation and compilation. Regular Rust developers using these libraries will automatically benefit from improved IDE performance without needing to modify their code. | ||||||
|
||||||
# Reference-level explanation | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should address things like caching and output generation. Quoting @alexcrichton in rust-lang/cargo#4001 (comment):
High level proposal: for a build script,
So for C code,
For crates, Footnotes
|
||||||
[reference-level-explanation]: #reference-level-explanation | ||||||
|
||||||
Cargo will set the `CARGO_CHECK` environment variable to `true` when running `cargo check` | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is unwise to name and describe this as for
I think it would be better to name and describe as something like:
This does change the behavior because the definition includes all of (I specified the value to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are great points! I think your alternative is a better approach, especially not to step on the toes of the parallel cargo modes discussion. I'll wait for more input and then probably update the RFC to the alternative method after the holidays There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are a handful of environment variables that don't have the A name like List of the existing environment variables for reference https://doc.rust-lang.org/cargo/reference/environment-variables.html |
||||||
|
||||||
The environment variable will not be set (or will be set to `false`) for commands that require full compilation: | ||||||
- `cargo build` | ||||||
- `cargo run` | ||||||
- `cargo test` | ||||||
|
||||||
# Drawbacks | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Related to #3748 (comment): as proposed, this is only beneficial in cases where whatever the build script does is more significant than compiling the build script + deps itself. IME this is indeed the common case since often a crate like |
||||||
[drawbacks]: #drawbacks | ||||||
|
||||||
1. **Potential for Inconsistencies**: Build scripts might behave differently during type checking vs. full compilation, which could theoretically lead to different type checking results compared to the final build. | ||||||
|
||||||
2. **Increased Complexity**: Build script authors need to consider an additional factor when determining their behavior, which adds some complexity to the build system. On the other hand, they can ignore the feature entirely and just run all build steps regardless. | ||||||
|
||||||
3. **Maintenance Burden**: The Rust and Cargo teams will need to maintain this feature and ensure it remains consistent across different commands and contexts. | ||||||
|
||||||
# Rationale and alternatives | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rust-lang/cargo#10126 proposed |
||||||
[rationale-and-alternatives]: #rationale-alternatives | ||||||
|
||||||
Alternative designs considered: | ||||||
|
||||||
1. Define a standard environment variable that isn't set by `cargo check` but is officially encouraged by Rust for RLS and other IDE tooling. This would avoid any unexpected behavior from build scripts with other `cargo check` consumers but still provide a standard way for build scripts to skip unnecessary steps. | ||||||
|
||||||
2. Do Nothing: If we do nothing, build scripts will continue to run all build steps even when it's not necessary, significantly impacting Rust ergonomics when interfacing with exernal languages. | ||||||
|
||||||
# Prior art | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mind providing link to each prior art so we can find reference easier? |
||||||
[prior-art]: #prior-art | ||||||
|
||||||
1. **Go Build Tags**: Go allows conditional compilation using build tags, which can be used to skip certain build steps based on the build context. | ||||||
|
||||||
2. **Bazel's Configuration Transitions**: Bazel provides mechanisms to modify build behavior based on the target being built. | ||||||
|
||||||
3. **Cargo Features**: The existing feature flag system in Cargo demonstrates the value of conditional build behavior. | ||||||
|
||||||
4. **Other Cargo Environment Variables**: Cargo already sets several environment variables during builds: | ||||||
- `CARGO_CFG_TARGET_OS` | ||||||
- `CARGO_MANIFEST_DIR` | ||||||
- `OUT_DIR` | ||||||
|
||||||
This proposal follows the established pattern of using environment variables to communicate build context to scripts. | ||||||
|
||||||
# Unresolved questions | ||||||
[unresolved-questions]: #unresolved-questions | ||||||
|
||||||
1. Should the environment variable be set for other commands that don't require full compilation? | ||||||
- `cargo doc` | ||||||
- `cargo clippy` | ||||||
|
||||||
2. How should this interact with parallel builds where some targets need full compilation and others only need type checking? (Is this even a thing?) | ||||||
|
||||||
3. Should we provide additional variables to distinguish between different types of type-checking operations (IDE, clippy, etc.)? | ||||||
|
||||||
4. How do we ensure build scripts don't diverge too much between type checking and full compilation modes? | ||||||
|
||||||
# Future possibilities | ||||||
[future-possibilities]: #future-possibilities | ||||||
|
||||||
1. **Extended Build Contexts**: Introduce additional environment variables for other build contexts: | ||||||
- `CARGO_DOC` for documentation generation | ||||||
- `CARGO_IDE` specifically for IDE tooling |
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.
FWIW,
check
ing orbuild
ing cargo#4001.check
ing orbuild
ing cargo#4001 (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.
Thank you for the links! I searched Rust issues and RFCs but forgot to search the cargo repo☹️
It doesn't look like the discussion has moved much in years, is there a reason against forcing the issue via RFC at this point?
The Rust landscape has changed significantly since the last major activity in the discussion and rust-analyzer seems to be the de-facto LSP implementation at this point. What are the downsides of introducing an LSP specific
check
command whose only difference (for now) is setting that environment variable? Or the alternative, allowing callers to set aCARGO_NO_BUILD
environment variable themselves , one that is officially sanctioned and supported by Rust/Cargo (if not set automatically)?I understand and appreciate the hesitance to stabilize contracts without ironing out all of the generalities but the discussion about cargo modes and the
cargo check && cargo build
feels very ivory tower. Rust is increasingly being used to integrate with C++ code beyond*-sys
crates as cxx and other tools mature, and I feel like a way to notify build scripts not to run extraneous steps is very much needed regardless of the aforementioned issues. Personally I always configurerust-analyzer
to use a subdirectory so that it doesn't blockcargo build
anyway, so build caching betweencargo check
andcargo build
wouldn't apply (and I'm curious what fraction of the community does too)That said, I'm biased as I feel acute pain with cxx/cxx-qt, where sccache doesn't seem to help. Worst case scenario I can set the environment variables myself and use a custom fork.
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.
No. While RFC doesn't really force anything until accepted, it is good to open a discussion. Sometimes people hang out in https://internals.rust-lang.org/ first for pre-RFC, before preparing a more formal proposal here.