From 424ffc2f7f1077a91aa183c86d8a54f01285dd21 Mon Sep 17 00:00:00 2001 From: Alexander Kiselev Date: Fri, 20 Dec 2024 20:45:18 -0800 Subject: [PATCH 1/6] Initial commit for CARGO_CHECK env var proposal --- text/0000-cargo_check_environment_variable.md | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 text/0000-cargo_check_environment_variable.md diff --git a/text/0000-cargo_check_environment_variable.md b/text/0000-cargo_check_environment_variable.md new file mode 100644 index 00000000000..cc718a4766a --- /dev/null +++ b/text/0000-cargo_check_environment_variable.md @@ -0,0 +1,107 @@ +- Feature Name: `cargo_check_environment_variable` +- Start Date: 2024-12-20 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- 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. + +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" { + 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 +[reference-level-explanation]: #reference-level-explanation + +Cargo will set the `CARGO_CHECK` environment variable to `true` when running `cargo check` + +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 +[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 +[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 +[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 \ No newline at end of file From 80d6c3f9823ae98156d422320e07376b762c2c5b Mon Sep 17 00:00:00 2001 From: Alexander Kiselev Date: Fri, 20 Dec 2024 20:51:53 -0800 Subject: [PATCH 2/6] update RFC number and fix typo --- ...t_variable.md => 3748-cargo_check_environment_variable.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename text/{0000-cargo_check_environment_variable.md => 3748-cargo_check_environment_variable.md} (98%) diff --git a/text/0000-cargo_check_environment_variable.md b/text/3748-cargo_check_environment_variable.md similarity index 98% rename from text/0000-cargo_check_environment_variable.md rename to text/3748-cargo_check_environment_variable.md index cc718a4766a..e51d7231de9 100644 --- a/text/0000-cargo_check_environment_variable.md +++ b/text/3748-cargo_check_environment_variable.md @@ -1,12 +1,12 @@ - Feature Name: `cargo_check_environment_variable` - Start Date: 2024-12-20 -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- 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. +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 From aadc16ab0efb322ea73a59257adcbb14f1c14c91 Mon Sep 17 00:00:00 2001 From: Alexander Kiselev Date: Sat, 21 Dec 2024 10:09:28 -0800 Subject: [PATCH 3/6] Fixed sentence in motivation that erroneously mentioned all build scripts --- text/3748-cargo_check_environment_variable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3748-cargo_check_environment_variable.md b/text/3748-cargo_check_environment_variable.md index e51d7231de9..d44db4b0958 100644 --- a/text/3748-cargo_check_environment_variable.md +++ b/text/3748-cargo_check_environment_variable.md @@ -18,7 +18,7 @@ Rust development heavily relies on IDE tooling like rust-analyzer, which frequen - 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. +Currently, every time rust-analyzer runs `cargo check`, the build script in the changed crate must execute its 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. This is particularly important for projects using cxx-qt and similar frameworks where the build scripts perform extensive code generation and compilation. From a637be2e4fa13f9fd3fad296ad60f82e585d31e8 Mon Sep 17 00:00:00 2001 From: Alexander Kiselev Date: Sat, 21 Dec 2024 10:13:29 -0800 Subject: [PATCH 4/6] fixed code sample --- text/3748-cargo_check_environment_variable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3748-cargo_check_environment_variable.md b/text/3748-cargo_check_environment_variable.md index d44db4b0958..9b00f0580a2 100644 --- a/text/3748-cargo_check_environment_variable.md +++ b/text/3748-cargo_check_environment_variable.md @@ -32,7 +32,7 @@ fn main() { generate_rust_bindings(); // Only compile external code when not type checking - if std::env::var("CARGO_CHECK").unwrap() != "true" { + if std::env::var("CARGO_CHECK").is_ok() { compile_cpp_code(); } } From 19c72e32f449efea3533774c9b628fa73a5d053d Mon Sep 17 00:00:00 2001 From: Alexander Kiselev Date: Sat, 21 Dec 2024 10:14:12 -0800 Subject: [PATCH 5/6] change truthy value to '1' --- text/3748-cargo_check_environment_variable.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/text/3748-cargo_check_environment_variable.md b/text/3748-cargo_check_environment_variable.md index 9b00f0580a2..38754f0b9d0 100644 --- a/text/3748-cargo_check_environment_variable.md +++ b/text/3748-cargo_check_environment_variable.md @@ -6,7 +6,7 @@ # 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. +Add a new environment variable `CARGO_CHECK` that is set to `1` 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 @@ -45,9 +45,9 @@ This feature primarily benefits library authors who maintain build scripts, espe # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -Cargo will set the `CARGO_CHECK` environment variable to `true` when running `cargo check` +Cargo will set the `CARGO_CHECK` environment variable to `1` when running `cargo check` -The environment variable will not be set (or will be set to `false`) for commands that require full compilation: +The environment variable will not be set for commands that require full compilation: - `cargo build` - `cargo run` - `cargo test` From 7d28a8b6c0318f7fb8ac6efd316b4e829ab2dd16 Mon Sep 17 00:00:00 2001 From: Alexander Kiselev Date: Sat, 21 Dec 2024 14:06:10 -0800 Subject: [PATCH 6/6] Added information about rerun-if-changed --- text/3748-cargo_check_environment_variable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3748-cargo_check_environment_variable.md b/text/3748-cargo_check_environment_variable.md index 38754f0b9d0..a69e1856a2b 100644 --- a/text/3748-cargo_check_environment_variable.md +++ b/text/3748-cargo_check_environment_variable.md @@ -18,7 +18,7 @@ Rust development heavily relies on IDE tooling like rust-analyzer, which frequen - 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`, the build script in the changed crate must execute its 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. +Currently, every time rust-analyzer runs `cargo check`, the build script in the changed crate must execute its full build process, including steps like compiling C++ code that are only needed for linking but not for type checking. Normally the build script would only be run when a file added by `cargo::rerun-if-changed` is changed, which generally doesn't include the Rust source code. However, when using `cxx` to create bridges between C++ and Rust, the build script must be run for every change in the Rust bridges. Usually `cxx` bindings are rarely changed but in projects like `cxx-qt` that interface between Rust and Qt types, they receive signficantly more changes. This impacts IDE responsiveness, especially in projects with complex build scripts. This is particularly important for projects using cxx-qt and similar frameworks where the build scripts perform extensive code generation and compilation.