From 5448ed7d68b9d2df1958b96549ffd7fd6ba46999 Mon Sep 17 00:00:00 2001 From: CAD97 Date: Sun, 13 Mar 2022 22:35:32 -0500 Subject: [PATCH 001/525] Initial implementation --- .gitignore | 2 + Cargo.toml | 11 ++ README.md | 38 +++++++ src/input.rs | 288 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 12 +++ src/output.rs | 188 ++++++++++++++++++++++++++++++++ 6 files changed, 539 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/input.rs create mode 100644 src/lib.rs create mode 100644 src/output.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..4fffb2f89cb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000000..3845211de27 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "build-rs" +version = "0.1.0" +edition = "2021" + +description = "Convenience wrapper for cargo buildscript input/output" +repository = "https://github.com/cad97/build-rs" +license = "MIT OR Apache-2.0 OR 0BSD OR CC0-1.0 OR CDDL-1.0 OR MIT-0 OR Unlicense OR WTFPL" + +[lib] +name = "build" diff --git a/README.md b/README.md new file mode 100644 index 00000000000..7e5982a4c97 --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +A convenience wrapper for cargo buildscript input/output. + +## Why? + +The cargo buildscript API is (necessarily) stringly-typed. This crate helps you +avoid typos, at least in the name of the instruction to cargo or environment +variable that you're reading. + +Additionally, [potentially multi-valued variables][irlo] have a subtle footgun, +in that when they're a single value, you might match on `target_family="unix"`, +whereas `target_family="unix,wasm"` is also a valid variable, and would wrongly +be excluded by the naive check. Using this crate forces you to be correct about +multi-valued inputs. + +[irlo]: https://internals.rust-lang.org/t/futher-extensions-to-cfg-target-family-and-concerns-about-breakage/16313?u=cad97 + +## License + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +If this is insufficient, you may also use this software under any of the following licenses: + +- [MIT License](https://spdx.org/licenses/MIT.html) +- [Apache License 2.0](https://spdx.org/licenses/Apache-2.0.html) +- [BSD Zero Clause License](https://spdx.org/licenses/0BSD.html) +- [Creative Commons Zero v1.0 Universal](https://spdx.org/licenses/CC0-1.0.html) +- [Common Development and Distribution License 1.0](https://spdx.org/licenses/CDDL-1.0.html) +- [MIT No Attribution](https://spdx.org/licenses/MIT-0.html) +- [The Unlicense](https://spdx.org/licenses/Unlicense.html) (the above text) +- [Do What The F*ck You Want To Public License](https://spdx.org/licenses/WTFPL.html) + +and if for some reason that is insufficient, open a PR (and maybe ask your lawyer some questions about the other libraries you're using). diff --git a/src/input.rs b/src/input.rs new file mode 100644 index 00000000000..57b66938bf7 --- /dev/null +++ b/src/input.rs @@ -0,0 +1,288 @@ +use std::{env, ffi::OsString, path::PathBuf}; + +macro_rules! export { + () => {}; + ($(#[$meta:meta])* $f:ident -> String = $var:ident $(; $($rest:tt)*)? ) => { + $(#[$meta])* + pub fn $f() -> String { + env::var_os(stringify!($var)) + .expect(concat!("cargo buildscript env var $", stringify!($var), " not found")) + .into_string() + .expect(concat!("cargo buildscript env var $", stringify!($var), " contained invalid UTF-8")) + } + + $(export! { + $($rest)* + })? + }; + ($(#[$meta:meta])* $f:ident -> Option = $var:ident $(; $($rest:tt)*)? ) => { + $(#[$meta])* + pub fn $f() -> Option { + env::var_os(stringify!($var)).map(|it| it + .into_string() + .expect(concat!("cargo buildscript env var $", stringify!($var), " contained invalid UTF-8")) + ) + } + + $(export! { + $($rest)* + })? + }; + ($(#[$meta:meta])* $f:ident -> usize = $var:ident $(; $($rest:tt)*)? ) => { + $(#[$meta])* + pub fn $f() -> usize { + env::var_os(stringify!($var)) + .expect(concat!("cargo buildscript env var $", stringify!($var), " not found")) + .into_string() + .expect(concat!("cargo buildscript env var $", stringify!($var), " contained invalid UTF-8")) + .parse() + .expect(concat!("cargo buildscript env var $", stringify!($var), " did not parse as `usize`")) + } + + $(export! { + $($rest)* + })? + }; + ($(#[$meta:meta])* $f:ident -> Vec = $var:ident $(; $($rest:tt)*)? ) => { + $(#[$meta])* + pub fn $f() -> Vec { + env::var_os(stringify!($var)) + .expect(concat!("cargo buildscript env var $", stringify!($var), " not found")) + .into_string() + .expect(concat!("cargo buildscript env var $", stringify!($var), " contained invalid UTF-8")) + .split(',') + .map(Into::into) + .collect() + } + + $(export! { + $($rest)* + })? + }; + ($(#[$meta:meta])* $f:ident -> bool = $var:ident $(; $($rest:tt)*)? ) => { + $(#[$meta])* + pub fn $f() -> bool { + env::var_os(stringify!($var)) + .is_some() + } + + $(export! { + $($rest)* + })? + }; + ($(#[$meta:meta])* $f:ident -> PathBuf = $var:ident $(; $($rest:tt)*)? ) => { + $(#[$meta])* + pub fn $f() -> PathBuf { + env::var_os(stringify!($var)) + .expect(concat!("cargo buildscript env var $", stringify!($var), " not found")) + .into() + } + + $(export! { + $($rest)* + })? + }; + ($(#[$meta:meta])* $f:ident -> Option = $var:ident $(; $($rest:tt)*)? ) => { + $(#[$meta])* + pub fn $f() -> Option { + env::var_os(stringify!($var)) + .map(Into::into) + } + + $(export! { + $($rest)* + })? + }; + ($(#[$meta:meta])* $f:ident -> OsString = $var:ident $(; $($rest:tt)*)? ) => { + $(#[$meta])* + pub fn $f() -> OsString { + env::var_os(stringify!($var)) + .expect(concat!("cargo buildscript env var $", stringify!($var), " not found")) + } + + $(export! { + $($rest)* + })? + }; + ($(#[$meta:meta])* $f:ident -> $out:ty = $var:ident $(; $($rest:tt)*)? ) => { + compile_error!(concat!("Provided unknown output type ", stringify!($out), " to export!")); + + $(export! { + $($rest)* + })? + }; +} + +export! { + /// Path to the `cargo` binary performing the build. + cargo -> PathBuf = CARGO; + /// The directory containing the manifest for the package being built (the + /// package containing the build script). Also note that this is the value + /// of the current working directory of the build script when it starts. + cargo_manifest_dir -> PathBuf = CARGO_MANIFEST_DIR; + /// The manifest links value. + cargo_manifest_links -> String = CARGO_MANIFEST_LINKS; + /// Contains parameters needed for Cargo's jobserver implementation to + /// parallelize subprocesses. Rustc or cargo invocations from build.rs + /// can already read CARGO_MAKEFLAGS, but GNU Make requires the flags + /// to be specified either directly as arguments, or through the MAKEFLAGS + /// environment variable. Currently Cargo doesn't set the MAKEFLAGS + /// variable, but it's free for build scripts invoking GNU Make to set it + /// to the contents of CARGO_MAKEFLAGS. + cargo_makeflags -> OsString = CARGO_MAKEFLAGS; + /// Set on [unix-like platforms](https://doc.rust-lang.org/reference/conditional-compilation.html#unix-and-windows). + cargo_cfg_unix -> bool = CARGO_CFG_UNIX; + /// Set on [windows-like platforms](https://doc.rust-lang.org/reference/conditional-compilation.html#unix-and-windows). + cargo_cfg_windows -> bool = CARGO_CFG_WINDOWS; + /// The [target family](https://doc.rust-lang.org/reference/conditional-compilation.html#target_family). + cargo_cfg_target_family -> Vec = CARGO_CFG_TARGET_FAMILY; + /// The [target operating system](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os). + cargo_cfg_target_os -> String = CARGO_CFG_TARGET_OS; + /// The CPU [target architecture](https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch). + cargo_cfg_target_arch -> String = CARGO_CFG_TARGET_ARCH; + /// The [target vendor](https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor). + cargo_cfg_target_vendor -> String = CARGO_CFG_TARGET_VENDOR; + /// The [target environment](https://doc.rust-lang.org/reference/conditional-compilation.html#target_env) ABI. + cargo_cfg_target_env -> String = CARGO_CFG_TARGET_ENV; + /// The CPU [pointer width](https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width). + cargo_cfg_pointer_width -> usize = CARGO_CFG_TARGET_POINTER_WIDTH; + /// Teh CPU [target endianness](https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian). + cargo_cfg_target_endian -> String = CARGO_CFG_TARGET_ENDIAN; + /// List of CPU [target features](https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature) enabled. + cargo_cfg_target_feature -> Vec = CARGO_CFG_TARGET_FEATURE; + /// The folder in which all output should be placed. This folder is inside + /// the build directory for the package being built, and it is unique for + /// the package in question. + out_dir -> PathBuf = OUT_DIR; + /// The target triple that is being compiled for. Native code should be + /// compiled for this triple. See the [Target Triple] description for + /// more information. + /// + /// [Target Triple]: https://doc.rust-lang.org/cargo/appendix/glossary.html#target + target -> String = TARGET; + /// The host triple of the Rust compiler. + host -> String = HOST; + /// The parallelism specified as the top-level parallelism. This can be + /// useful to pass a `-j` parameter to a system like `make`. Note that care + /// should be taken when interpreting this environment variable. For + /// historical purposes this is still provided but recent versions of + /// Cargo, for example, do not need to run `make -j`, and instead can set + /// the `MAKEFLAGS` env var to the content of `CARGO_MAKEFLAGS` to activate + /// the use of Cargo's GNU Make compatible [jobserver] for sub-make + /// invocations. + /// + /// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html + num_jobs -> String = NUM_JOBS; + /// Value of the corresponding variable for the profile currently being built. + opt_level -> String = OPT_LEVEL; + /// Value of the corresponding variable for the profile currently being built. + debug -> String = DEBUG; + /// `release` for release builds, `debug` for other builds. This is + /// determined based on if the profile inherits from the [`dev`] or + /// [`release`] profile. Using this environment variable is not + /// recommended. Using other environment variables like `OPT_LEVEL` + /// provide a more correct view of the actual settings being used. + /// + /// [`dev`]: https://doc.rust-lang.org/cargo/reference/profiles.html#dev + /// [`release`]: https://doc.rust-lang.org/cargo/reference/profiles.html#release + profile -> String = PROFILE; + /// The compiler that Cargo has resolved to use, passed to the build script + /// so it might use it as well. + rustc -> PathBuf = RUSTC; + /// The documentation generator that Cargo has resolved to use, passed to + /// the build script so it might use it as well. + rustdoc -> PathBuf = RUSTDOC; + /// The `rustc` wrapper, if any, that Cargo is using. See + /// [`build.rustc-wrapper`](https://doc.rust-lang.org/cargo/reference/config.html#buildrustc-wrapper). + rustc_wrapper -> Option = RUSTC_WRAPPER; + /// The `rustc` wrapper, if any, that Cargo is using for workspace members. + /// See [`build.rustc-workspace-wrapper`](https://doc.rust-lang.org/cargo/reference/config.html#buildrustc-workspace-wrapper). + rustc_workspace_wrapper -> Option = RUSTC_WORKSPACE_WRAPPER; + /// The path to the linker binary that Cargo has resolved to use for the + /// current target, if specified. + rustc_linker -> Option = RUSTC_LINKER; + /// The full version of your package. + cargo_pkg_version -> String = CARGO_PKG_VERSION; + /// The major version of your package. + cargo_pkg_version_major -> usize = CARGO_PKG_VERSION_MAJOR; + /// The minor version of your package. + cargo_pkg_version_minor -> usize = CARGO_PKG_VERSION_MINOR; + /// The patch version of your package. + cargo_pkg_version_patch -> usize = CARGO_PKG_VERSION_PATCH; + /// The pre-release of your package. + cargo_pkg_version_pre -> String = CARGO_PKG_VERSION_PRE; + /// The name of your package. + cargo_pkg_name -> String = CARGO_PKG_NAME; + /// The description from the manifest of your package. + cargo_pkg_description -> String = CARGO_PKG_DESCRIPTION; + /// The home page from the manifest of your package. + cargo_pkg_homepage -> String = CARGO_PKG_HOMEPAGE; + /// The repository from the manifest of your package. + cargo_pkg_repository -> String = CARGO_PKG_REPOSITORY; + /// The license from the manifest of your package. + cargo_pkg_license -> String = CARGO_PKG_LICENSE; + /// The license file from the manifest of your package. + cargo_pkg_license_file -> String = CARGO_PKG_LICENSE_FILE; +} + +/// For each activated feature of the package being built, this will be true. +pub fn cargo_feature(name: &str) -> bool { + let key = format!("CARGO_FEATURE_{}", name.to_uppercase().replace('-', "_")); + env::var_os(key).is_some() +} + +/// For each [configuration option] of the package being built, this will +/// contain the value of the configuration. Boolean configurations are present +/// if they are set, and not present otherwise. This includes values built-in +/// to the compiler (which can be seen with `rustc --print=cfg`) and values set +/// by build scripts and extra flags passed to `rustc` (such as those defined +/// in `RUSTFLAGS`). +/// +/// [configuration option]: https://doc.rust-lang.org/reference/conditional-compilation.html +pub fn cargo_cfg(cfg: &str) -> Option> { + let key = format!("CARGO_CFG_{}", cfg.to_uppercase().replace('-', "_")); + let val = env::var_os(&key)?.into_string().unwrap_or_else(|_| { + panic!("cargo buildscript env var ${key} contained invalid UTF-8"); + }); + Some(val.split(',').map(Into::into).collect()) +} + +/// Each build script can generate an arbitrary set of metadata in the form of +/// key-value pairs. This metadata is passed to the build scripts of +/// **dependent** packages. For example, if the package `bar` depends on `foo`, +/// then if `foo` generates `key=value` as part of its build script metadata, +/// then the build script of `bar` will have the environment variables +/// `DEP_FOO_KEY=value`. +pub fn dep(name: &str, key: &str) -> Option { + let key = format!( + "DEP_{}_{}", + name.to_uppercase().replace('-', "_"), + key.to_uppercase().replace('-', "_") + ); + let val = env::var_os(&key)?.into_string().unwrap_or_else(|_| { + panic!("cargo buildscript env var ${key} contained invalid UTF-8"); + }); + Some(val) +} + +/// Extra flags that Cargo invokes rustc with. See [`build.rustflags`]. Note +/// that since Rust 1.55, `RUSTFLAGS` is removed from the environment; scripts +/// should use `CARGO_ENCODED_RUSTFLAGS` instead. +/// +/// [`build.rustflags`]: https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags +pub fn cargo_encoded_rustflags() -> Vec { + let val = env::var_os("CARGO_ENCODED_RUSTFLAGS") + .expect("cargo buildscript env var $CARGO_ENCODED_RUSTFLAGS") + .into_string() + .expect("cargo buildscript env var $CARGO_ENCODED_RUSTFLAGS contained invalid UTF-8"); + val.split('\x1f').map(Into::into).collect() +} + +/// List of authors from the manifest of your package. +pub fn cargo_pkg_authors() -> Vec { + let val = env::var_os("CARGO_PKG_AUTHORS") + .expect("cargo buildscript env var $CARGO_PKG_AUTHORS") + .into_string() + .expect("cargo buildscript env var $CARGO_PKG_AUTHORS contained invalid UTF-8"); + val.split(':').map(Into::into).collect() +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000000..1e4cbc526b9 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,12 @@ +//! Convenience wrappers for cargo buildscript input/output. + +/// Inputs to the build script, in the form of environment variables. +pub mod input; +/// Outputs from the build script, in the form of `cargo:` printed lines. +/// +/// _Does not print a leading newline._ Thus, if you ever write to stdout and +/// don't lock until a trailing newline, these instructions will likely fail. +pub mod output; + +#[doc(no_inline)] +pub use crate::{input::*, output::*}; diff --git a/src/output.rs b/src/output.rs new file mode 100644 index 00000000000..75bc450fcca --- /dev/null +++ b/src/output.rs @@ -0,0 +1,188 @@ +use std::{ffi::OsStr, path::Path}; + +/// The rustc-link-arg instruction tells Cargo to pass the +/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg) +/// to the compiler, but only when building supported targets (benchmarks, +/// binaries, cdylib crates, examples, and tests). Its usage is highly platform +/// specific. It is useful to set the shared library version or linker script. +pub fn rustc_link_arg(flag: &str) { + println!("cargo:rustc-link-arg={flag}"); +} + +/// The `rustc-link-arg-bin` instruction tells Cargo to pass the +/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg) +/// to the compiler, but only when building the binary target with name `BIN`. +/// Its usage is highly platform specific. It is useful to set a linker script +/// or other linker options. +pub fn rustc_link_arg_bin(bin: &str, flag: &str) { + println!("cargo:rustc-link-arg-bin={bin}={flag}"); +} + +/// The `rustc-link-arg-bins` instruction tells Cargo to pass the +/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg) +/// to the compiler, but only when building a binary target. Its usage is +/// highly platform specific. It is useful to set a linker script or other +/// linker options. +pub fn rustc_link_arg_bins(flag: &str) { + println!("cargo:rustc-link-arg-bins={flag}"); +} + +/// The `rustc-link-lib` instruction tells Cargo to link the given library +/// using the compiler's [`-l` flag]. This is typically used to link a native +/// library using [`FFI`]. +/// +/// The `-l` flag is only passed to the library target of the package, unless +/// there is no library target, in which case it is passed to all targets. This +/// is done because all other targets have an implicit dependency on the +/// library target, and the given library to link should only be included once. +/// This means that if a package has both a library and a binary target, the +/// _library_ has access to the symbols from the given lib, and the binary +/// should access them through the library target's public API. +/// +/// The optional `KIND` may be one of `dylib`, `static`, or `framework`. +/// See the [rustc book] for more detail. +/// +/// [`-l` flag]: https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-link-lib +/// [rustc book]: https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-link-lib +pub fn rustc_link_lib(name: &str) { + println!("cargo:rustc-link-lib={name}"); +} + +/// See [`rustc_link_lib`]. +pub fn rustc_link_lib_kind(kind: &str, name: &str) { + println!("cargo:rustc-link-lib={kind}={name}"); +} + +/// The `rustc-link-search` instruction tells Cargo to pass the [`-L flag`] to +/// the compiler to add a directory to the library search path. +/// +/// The optional `KIND` may be one of `dependency`, `crate`, `native`, +/// `framework`, or `all`. See the [rustc book] for more detail. +/// +/// These paths are also added to the +/// [dynamic library search path environment variable] if they are within the +/// `OUT_DIR`. Depending on this behavior is discouraged since this makes it +/// difficult to use the resulting binary. In general, it is best to avoid +/// creating dynamic libraries in a build script (using existing system +/// libraries is fine). +/// +/// [`-L flag`]: https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-search-path +/// [rustc book]: https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-search-path +/// [dynamic library search path environment variable]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#dynamic-library-paths +pub fn rustc_link_search(path: impl AsRef) { + let path = path + .as_ref() + .as_os_str() + .to_str() + .expect("cannot print non-UTF8 path"); + println!("cargo:rustc-link-search={path}"); +} + +/// See [`rustc_link_search`]. +pub fn rustc_link_search_kind(kind: &str, path: impl AsRef) { + let path = path + .as_ref() + .as_os_str() + .to_str() + .expect("cannot print non-UTF8 path"); + println!("cargo:rustc-link-search={kind}={path}"); +} + +/// The `rustc-flags` instruction tells Cargo to pass the given space-separated +/// flags to the compiler. This only allows the `-l` and `-L` flags, and is +/// equivalent to using `rustc-link-lib` and `rustc-link-search`. +pub fn rustc_flags(flags: &str) { + println!("cargo:rustc-flags={flags}"); +} + +/// The `rustc-cfg` instruction tells Cargo to pass the given value to the +/// [`--cfg` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-cfg) +/// to the compiler. This may be used for compile-time detection of features to +/// enable [conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html). +/// +/// Note that this does not affect Cargo's dependency resolution. This cannot +/// be used to enable an optional dependency, or enable other Cargo features. +/// +/// Be aware that Cargo features use the form `feature="foo"`. `cfg` values +/// passed with this flag are not restricted to that form, and may provide just +/// a single identifier, or any arbitrary key/value pair. For example, emitting +/// `cargo:rustc-cfg=abc` will then allow code to use `#[cfg(abc)]` (note the +/// lack of `feature=`). Or an arbitrary key/value pair may be used with an `=` +/// symbol like `cargo:rustc-cfg=my_component="foo"`. The key should be a Rust +/// identifier, the value should be a string. +pub fn rustc_cfg(key: &str) { + println!("cargo:rustc-cfg={key}") +} + +/// See [`rustc_cfg`]. +pub fn rustc_cfg_value(key: &str, value: &str) { + println!("cargo:rustc-cfg={key}={value}"); +} + +/// The `rustc-env` instruction tells Cargo to set the given environment +/// variable when compiling the package. The value can be then retrieved by the +/// [`env! macro`](env!) in the compiled crate. This is useful for embedding +/// additional metadata in crate's code, such as the hash of git HEAD or the +/// unique identifier of a continuous integration server. +pub fn rustc_env(var: &str, value: &str) { + println!("cargo:rustc-env={var}={value}"); +} + +/// The `rustc-cdylib-link-arg` instruction tells Cargo to pass the +/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg) +/// to the compiler, but only when building a cdylib library target. Its usage +/// is highly platform specific. It is useful to set the shared library version +/// or the runtime-path. +pub fn rustc_cdylib_link_arg(flag: &str) { + println!("cargo:rustc-cdylib-link-arg={flag}"); +} + +/// The `warning` instruction tells Cargo to display a warning after the build +/// script has finished running. Warnings are only shown for path dependencies +/// (that is, those you're working on locally), so for example warnings printed +/// out in crates.io crates are not emitted by default. The `-vv` "very verbose" +/// flag may be used to have Cargo display warnings for all crates. +pub fn warning(message: &str) { + println!("cargo:warning={message}"); +} + +/// The `rerun-if-changed` instruction tells Cargo to re-run the build script +/// if the file at the given path has changed. Currently, Cargo only uses the +/// filesystem last-modified "mtime" timestamp to determine if the file has +/// changed. It compares against an internal cached timestamp of when the build +/// script last ran. +/// +/// If the path points to a directory, it will scan the entire directory for +/// any modifications. +/// +/// If the build script inherently does not need to re-run under any +/// circumstance, then emitting `cargo:rerun-if-changed=build.rs` is a simple +/// way to prevent it from being re-run (otherwise, the default if no +/// `rerun-if` instructions are emitted is to scan the entire package +/// directory for changes). Cargo automatically handles whether or not the +/// script itself needs to be recompiled, and of course the script will be +/// re-run after it has been recompiled. Otherwise, specifying build.rs is +/// redundant and unnecessary. +pub fn rerun_if_changed(path: impl AsRef) { + let path = path + .as_ref() + .as_os_str() + .to_str() + .expect("cannot print non-UTF8 path"); + println!("cargo:rerun-if-changed={path}"); +} + +/// The `rerun-if-env-changed` instruction tells Cargo to re-run the build +/// script if the value of an environment variable of the given name has +/// changed. +/// +/// Note that the environment variables here are intended for global +/// environment variables like `CC` and such, it is not necessary to use this +/// for environment variables like `TARGET` that Cargo sets. +pub fn rerun_if_env_changed(name: impl AsRef) { + let name = name + .as_ref() + .to_str() + .expect("cannot print non-UTF8 env key"); + println!("cargo:rerun-if-env-changed={name}"); +} From 41ea720c3fca63ef8bf2ec2cb7e5517cb381880e Mon Sep 17 00:00:00 2001 From: CAD97 Date: Sun, 13 Mar 2022 22:38:50 -0500 Subject: [PATCH 002/525] Add simple example --- src/lib.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 1e4cbc526b9..b8e486fb94f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,13 @@ //! Convenience wrappers for cargo buildscript input/output. +//! +//! # Example +//! +//! ```rust +//! build::rerun_if_changed("build.rs"); // only rerun for buildscript changes +//! build::rustc_cfg("has_buildrs"); // set #[cfg(has_buildrs)] +//! dbg!(build::cargo()); // path to the cargo executable +//! dbg!(build::cargo_manifest_dir()); // the directory of the build manifest +//! ``` /// Inputs to the build script, in the form of environment variables. pub mod input; From 22c1e9e64ffa73f6566a76e4fe41febd135a809d Mon Sep 17 00:00:00 2001 From: CAD97 Date: Sun, 13 Mar 2022 22:47:58 -0500 Subject: [PATCH 003/525] Add autopublish CI job --- .github/workflows/publish.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000000..668fc5570a0 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,22 @@ +on: + push: + tags: + - '*' + +jobs: + publish: + name: Publish + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Install stable + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - name: Publish + run: cargo publish + env: + CARGO_REGISTRY_TOKEN: ${{secrets.CRATES_IO_API_TOKEN}} From 4a6d465166f97d175f836dbec5fd188caf802a2f Mon Sep 17 00:00:00 2001 From: CAD97 Date: Sun, 13 Mar 2022 23:02:36 -0500 Subject: [PATCH 004/525] Add extra missing metadata --- Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 3845211de27..62cfb4bb681 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" description = "Convenience wrapper for cargo buildscript input/output" repository = "https://github.com/cad97/build-rs" license = "MIT OR Apache-2.0 OR 0BSD OR CC0-1.0 OR CDDL-1.0 OR MIT-0 OR Unlicense OR WTFPL" +keywords = ["buildscript","build-script","cargo"] +categories = ["development-tools::build-utils"] [lib] name = "build" From 6655e485135d1c339864b4e4f4147cb60144ec48 Mon Sep 17 00:00:00 2001 From: CAD97 Date: Sun, 13 Mar 2022 23:39:50 -0500 Subject: [PATCH 005/525] Bump version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 62cfb4bb681..7a3621c1427 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "build-rs" -version = "0.1.0" +version = "0.1.2" edition = "2021" description = "Convenience wrapper for cargo buildscript input/output" From 7b58bd51c34907221ea38abf40652bef61b4b1ba Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 30 Oct 2024 09:50:01 -0500 Subject: [PATCH 006/525] feat(resolver): Stabilize v3 resolver --- src/cargo/util/toml/mod.rs | 14 +------------- src/doc/src/reference/config.md | 2 +- src/doc/src/reference/resolver.md | 1 + tests/testsuite/rust_version.rs | 28 ++-------------------------- 4 files changed, 5 insertions(+), 40 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index be8e7512776..c0f6df61d90 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -273,12 +273,6 @@ fn normalize_toml( warnings: &mut Vec, errors: &mut Vec, ) -> CargoResult { - if let Some(workspace) = &original_toml.workspace { - if workspace.resolver.as_deref() == Some("3") { - features.require(Feature::edition2024())?; - } - } - let mut normalized_toml = manifest::TomlManifest { cargo_features: original_toml.cargo_features.clone(), package: None, @@ -316,8 +310,7 @@ fn normalize_toml( if let Some(original_package) = original_toml.package() { let package_name = &original_package.name; - let normalized_package = - normalize_package_toml(original_package, features, package_root, &inherit)?; + let normalized_package = normalize_package_toml(original_package, package_root, &inherit)?; let edition = normalized_package .normalized_edition() .expect("previously normalized") @@ -549,7 +542,6 @@ fn normalize_patch<'a>( #[tracing::instrument(skip_all)] fn normalize_package_toml<'a>( original_package: &manifest::TomlPackage, - features: &Features, package_root: &Path, inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>, ) -> CargoResult> { @@ -682,10 +674,6 @@ fn normalize_package_toml<'a>( _invalid_cargo_features: Default::default(), }; - if normalized_package.resolver.as_deref() == Some("3") { - features.require(Feature::edition2024())?; - } - Ok(Box::new(normalized_package)) } diff --git a/src/doc/src/reference/config.md b/src/doc/src/reference/config.md index fe3941a7503..11ac2f9a5dc 100644 --- a/src/doc/src/reference/config.md +++ b/src/doc/src/reference/config.md @@ -981,7 +981,7 @@ The `[resolver]` table overrides [dependency resolution behavior](resolver.md) f #### `resolver.incompatible-rust-versions` * Type: string -* Default: `"allow"` +* Default: See [`resolver`](resolver.md#resolver-versions) docs * Environment: `CARGO_RESOLVER_INCOMPATIBLE_RUST_VERSIONS` When resolving which version of a dependency to use, select how versions with incompatible `package.rust-version`s are treated. diff --git a/src/doc/src/reference/resolver.md b/src/doc/src/reference/resolver.md index 1ca87b15c05..bc41517b21f 100644 --- a/src/doc/src/reference/resolver.md +++ b/src/doc/src/reference/resolver.md @@ -507,6 +507,7 @@ resolver = "2" - `"2"` ([`edition = "2021"`](manifest.md#the-edition-field) default): Introduces changes in [feature unification](#features). See the [features chapter][features-2] for more details. +- `"3"` (requires Rust 1.84+): Change the default for [`resolver.incompatible-rust-versions`] from `allow` to `fallback` The resolver is a global option that affects the entire workspace. The `resolver` version in dependencies is ignored, only the value in the top-level diff --git a/tests/testsuite/rust_version.rs b/tests/testsuite/rust_version.rs index 463e599e5ba..a1af79b6b60 100644 --- a/tests/testsuite/rust_version.rs +++ b/tests/testsuite/rust_version.rs @@ -612,7 +612,7 @@ foo v0.0.1 ([ROOT]/foo) .run(); } -#[cargo_test(nightly, reason = "edition2024 in rustc is unstable")] +#[cargo_test] fn resolve_v3() { Package::new("only-newer", "1.6.0") .rust_version("1.65.0") @@ -631,8 +631,6 @@ fn resolve_v3() { .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "foo" version = "0.0.1" @@ -651,7 +649,6 @@ fn resolve_v3() { // v3 should resolve for MSRV p.cargo("generate-lockfile") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest Rust 1.60.0 compatible versions @@ -661,7 +658,6 @@ fn resolve_v3() { "#]]) .run(); p.cargo("tree") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.5.0 @@ -677,10 +673,8 @@ foo v0.0.1 ([ROOT]/foo) [LOCKING] 2 packages to latest compatible versions "#]]) - .masquerade_as_nightly_cargo(&["edition2024"]) .run(); p.cargo("tree") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.6.0 @@ -697,29 +691,13 @@ foo v0.0.1 ([ROOT]/foo) [LOCKING] 2 packages to latest compatible versions "#]]) - .masquerade_as_nightly_cargo(&["edition2024"]) .run(); p.cargo("tree") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.6.0 └── only-newer v1.6.0 -"#]]) - .run(); - - // unstable - p.cargo("generate-lockfile") - .with_status(101) - .with_stderr_data(str![[r#" -[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` - -Caused by: - the cargo feature `edition2024` requires a nightly version of Cargo, but this is the `stable` channel - See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels. - See https://doc.rust-lang.org/cargo/reference/unstable.html#edition-2024 for more information about using this feature. - "#]]) .run(); } @@ -946,7 +924,7 @@ fn cargo_install_ignores_msrv_config() { .run(); } -#[cargo_test(nightly, reason = "edition2024 in rustc is unstable")] +#[cargo_test] fn cargo_install_ignores_resolver_v3_msrv_change() { Package::new("dep", "1.0.0") .rust_version("1.50") @@ -958,14 +936,12 @@ fn cargo_install_ignores_resolver_v3_msrv_change() { .publish(); Package::new("foo", "0.0.1") .rust_version("1.60") - .cargo_feature("edition2024") .resolver("3") .file("src/main.rs", "fn main() {}") .dep("dep", "1") .publish(); cargo_process("install foo") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [DOWNLOADING] crates ... From 401f77a2979f80b0df2b6b475237d5729dbbbc8b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 6 Nov 2024 15:31:35 -0600 Subject: [PATCH 007/525] Big refactor --- .github/dependabot.yml | 7 + .github/renovate.json5 | 35 ++ .github/workflows/automerge.yaml | 25 ++ .github/workflows/ci.yaml | 59 +++ .github/workflows/publish.yaml | 19 + .github/workflows/publish.yml | 22 -- .gitignore | 1 - Cargo.lock | 14 + Cargo.toml | 17 +- README.md | 38 -- src/allow_use.rs | 17 + src/input.rs | 658 ++++++++++++++++++------------- src/lib.rs | 41 +- src/output.rs | 385 ++++++++++++------ test-lib/Cargo.toml | 9 + test-lib/build.rs | 4 + test-lib/src/lib.rs | 4 + 17 files changed, 869 insertions(+), 486 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/renovate.json5 create mode 100644 .github/workflows/automerge.yaml create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/publish.yaml delete mode 100644 .github/workflows/publish.yml create mode 100644 Cargo.lock delete mode 100644 README.md create mode 100644 src/allow_use.rs create mode 100644 test-lib/Cargo.toml create mode 100644 test-lib/build.rs create mode 100644 test-lib/src/lib.rs diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..c3533d12882 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: tuesday diff --git a/.github/renovate.json5 b/.github/renovate.json5 new file mode 100644 index 00000000000..948f0823284 --- /dev/null +++ b/.github/renovate.json5 @@ -0,0 +1,35 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "schedule": "every 6 weeks on tuesday", + "customManagers": [ + { + "customType": "regex", + "fileMatch": [ + "Cargo.toml$" + ], + "matchStrings": [ + "rust-version = \"(?\\d+\\.\\d+(\\.\\d+)?)\"" + ], + "depNameTemplate": "rust-version", + "packageNameTemplate": "rust-lang/rust", + "datasourceTemplate": "github-releases" + } + ], + "packageRules": [ + { + "commitMessageTopic": "bump rust-version", + "matchManagers": [ + "custom.regex" + ], + "matchPackageNames": [ + "rust-version" + ], + "extractVersion": "^(?\\d+\\.\\d+)", // drop patch version + "schedule": [ + "* * * * *" + ], + "minimumReleaseAge": "6 months", + "internalChecksFilter": "strict" + } + ] +} \ No newline at end of file diff --git a/.github/workflows/automerge.yaml b/.github/workflows/automerge.yaml new file mode 100644 index 00000000000..2ddc1dc2932 --- /dev/null +++ b/.github/workflows/automerge.yaml @@ -0,0 +1,25 @@ +name: Auto-merge safe bot PRs +on: pull_request + +permissions: + contents: write + pull-requests: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: github.actor == 'dependabot[bot]' + steps: + - name: Enable auto-merge + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + + renovate: + runs-on: ubuntu-latest + if: github.actor == 'renovate[bot]' + steps: + - name: Enable auto-merge + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000000..3278326ec77 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,59 @@ +name: CI +on: + pull_request: + merge_group: + push: + branches: + - main + +jobs: + tests: + name: Tests + runs-on: ubuntu-latest + strategy: + matrix: + toolchain: ["stable", "stable 6 months ago"] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ matrix.toolchain }} + - name: Build tests + run: cargo test --workspace --no-run + - name: Run tests + run: cargo test --workspace + + style: + name: Style + runs-on: ubuntu-latest + env: + RUSTFLAGS: -Dwarnings + RUSTDOCFLAGS: -Dwarnings + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt, clippy + - name: Install cargo-docs-rs + uses: dtolnay/install@cargo-docs-rs + - name: Check docs + run: cargo docs-rs + - name: Check lints + run: cargo clippy + - name: Check fmt + run: cargo fmt -- --check + + semver: + name: API + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Semver checks + uses: obi1kenobi/cargo-semver-checks-action@v2 + with: + package: build-rs diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 00000000000..4535d40afa4 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,19 @@ +name: Publish +on: + push: + tags: + - 'v*' + +jobs: + publish: + name: Publish + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + - name: Publish + run: cargo publish + env: + CARGO_REGISTRY_TOKEN: ${{secrets.CARGO_REGISTRY_TOKEN}} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 668fc5570a0..00000000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,22 +0,0 @@ -on: - push: - tags: - - '*' - -jobs: - publish: - name: Publish - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Install stable - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - override: true - - name: Publish - run: cargo publish - env: - CARGO_REGISTRY_TOKEN: ${{secrets.CRATES_IO_API_TOKEN}} diff --git a/.gitignore b/.gitignore index 4fffb2f89cb..ea8c4bf7f35 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ /target -/Cargo.lock diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000000..2ff714842b0 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,14 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "build-rs" +version = "0.2.0" + +[[package]] +name = "build-rs-test-lib" +version = "0.2.0" +dependencies = [ + "build-rs", +] diff --git a/Cargo.toml b/Cargo.toml index 7a3621c1427..f8a31b551df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,14 @@ [package] name = "build-rs" -version = "0.1.2" +version = "0.2.0" edition = "2021" +rust-version = "1.70" +publish = false -description = "Convenience wrapper for cargo buildscript input/output" -repository = "https://github.com/cad97/build-rs" -license = "MIT OR Apache-2.0 OR 0BSD OR CC0-1.0 OR CDDL-1.0 OR MIT-0 OR Unlicense OR WTFPL" -keywords = ["buildscript","build-script","cargo"] -categories = ["development-tools::build-utils"] +[features] -[lib] -name = "build" +## Experimental API. This feature flag is **NOT** semver stable. +unstable = [] + +[workspace] +members = ["test-lib"] diff --git a/README.md b/README.md deleted file mode 100644 index 7e5982a4c97..00000000000 --- a/README.md +++ /dev/null @@ -1,38 +0,0 @@ -A convenience wrapper for cargo buildscript input/output. - -## Why? - -The cargo buildscript API is (necessarily) stringly-typed. This crate helps you -avoid typos, at least in the name of the instruction to cargo or environment -variable that you're reading. - -Additionally, [potentially multi-valued variables][irlo] have a subtle footgun, -in that when they're a single value, you might match on `target_family="unix"`, -whereas `target_family="unix,wasm"` is also a valid variable, and would wrongly -be excluded by the naive check. Using this crate forces you to be correct about -multi-valued inputs. - -[irlo]: https://internals.rust-lang.org/t/futher-extensions-to-cfg-target-family-and-concerns-about-breakage/16313?u=cad97 - -## License - -This is free and unencumbered software released into the public domain. - -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. - -In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -If this is insufficient, you may also use this software under any of the following licenses: - -- [MIT License](https://spdx.org/licenses/MIT.html) -- [Apache License 2.0](https://spdx.org/licenses/Apache-2.0.html) -- [BSD Zero Clause License](https://spdx.org/licenses/0BSD.html) -- [Creative Commons Zero v1.0 Universal](https://spdx.org/licenses/CC0-1.0.html) -- [Common Development and Distribution License 1.0](https://spdx.org/licenses/CDDL-1.0.html) -- [MIT No Attribution](https://spdx.org/licenses/MIT-0.html) -- [The Unlicense](https://spdx.org/licenses/Unlicense.html) (the above text) -- [Do What The F*ck You Want To Public License](https://spdx.org/licenses/WTFPL.html) - -and if for some reason that is insufficient, open a PR (and maybe ask your lawyer some questions about the other libraries you're using). diff --git a/src/allow_use.rs b/src/allow_use.rs new file mode 100644 index 00000000000..fb39e9cfd29 --- /dev/null +++ b/src/allow_use.rs @@ -0,0 +1,17 @@ +use std::sync::OnceLock; + +fn rust_version_minor() -> u32 { + static VERSION_MINOR: OnceLock = OnceLock::new(); + *VERSION_MINOR.get_or_init(|| { + crate::input::cargo_pkg_rust_version() + .split('.') + .nth(1) + .unwrap_or("70") + .parse() + .unwrap() + }) +} + +pub(crate) fn double_colon_directives() -> bool { + rust_version_minor() >= 77 +} diff --git a/src/input.rs b/src/input.rs index 57b66938bf7..10a41965ba2 100644 --- a/src/input.rs +++ b/src/input.rs @@ -1,288 +1,406 @@ -use std::{env, ffi::OsString, path::PathBuf}; - -macro_rules! export { - () => {}; - ($(#[$meta:meta])* $f:ident -> String = $var:ident $(; $($rest:tt)*)? ) => { - $(#[$meta])* - pub fn $f() -> String { - env::var_os(stringify!($var)) - .expect(concat!("cargo buildscript env var $", stringify!($var), " not found")) - .into_string() - .expect(concat!("cargo buildscript env var $", stringify!($var), " contained invalid UTF-8")) - } - - $(export! { - $($rest)* - })? - }; - ($(#[$meta:meta])* $f:ident -> Option = $var:ident $(; $($rest:tt)*)? ) => { - $(#[$meta])* - pub fn $f() -> Option { - env::var_os(stringify!($var)).map(|it| it - .into_string() - .expect(concat!("cargo buildscript env var $", stringify!($var), " contained invalid UTF-8")) - ) - } - - $(export! { - $($rest)* - })? - }; - ($(#[$meta:meta])* $f:ident -> usize = $var:ident $(; $($rest:tt)*)? ) => { - $(#[$meta])* - pub fn $f() -> usize { - env::var_os(stringify!($var)) - .expect(concat!("cargo buildscript env var $", stringify!($var), " not found")) - .into_string() - .expect(concat!("cargo buildscript env var $", stringify!($var), " contained invalid UTF-8")) - .parse() - .expect(concat!("cargo buildscript env var $", stringify!($var), " did not parse as `usize`")) - } - - $(export! { - $($rest)* - })? - }; - ($(#[$meta:meta])* $f:ident -> Vec = $var:ident $(; $($rest:tt)*)? ) => { - $(#[$meta])* - pub fn $f() -> Vec { - env::var_os(stringify!($var)) - .expect(concat!("cargo buildscript env var $", stringify!($var), " not found")) - .into_string() - .expect(concat!("cargo buildscript env var $", stringify!($var), " contained invalid UTF-8")) - .split(',') - .map(Into::into) - .collect() - } - - $(export! { - $($rest)* - })? - }; - ($(#[$meta:meta])* $f:ident -> bool = $var:ident $(; $($rest:tt)*)? ) => { - $(#[$meta])* - pub fn $f() -> bool { - env::var_os(stringify!($var)) - .is_some() - } - - $(export! { - $($rest)* - })? - }; - ($(#[$meta:meta])* $f:ident -> PathBuf = $var:ident $(; $($rest:tt)*)? ) => { - $(#[$meta])* - pub fn $f() -> PathBuf { - env::var_os(stringify!($var)) - .expect(concat!("cargo buildscript env var $", stringify!($var), " not found")) - .into() - } - - $(export! { - $($rest)* - })? - }; - ($(#[$meta:meta])* $f:ident -> Option = $var:ident $(; $($rest:tt)*)? ) => { - $(#[$meta])* - pub fn $f() -> Option { - env::var_os(stringify!($var)) - .map(Into::into) - } - - $(export! { - $($rest)* - })? +//! Inputs from the build system to the build script. +//! +//! This crate does not do any caching or interpreting of the values provided by +//! Cargo beyond the communication protocol itself. It is up to the build script +//! to interpret the string values and decide what to do with them. +//! +//! Reference: + +use std::{ + env, + fmt::Display, + path::PathBuf, + str::{self, FromStr}, +}; + +macro_rules! missing { + ($key:ident) => { + panic!("cargo environment variable `{}` is missing", $key) }; - ($(#[$meta:meta])* $f:ident -> OsString = $var:ident $(; $($rest:tt)*)? ) => { - $(#[$meta])* - pub fn $f() -> OsString { - env::var_os(stringify!($var)) - .expect(concat!("cargo buildscript env var $", stringify!($var), " not found")) - } - - $(export! { - $($rest)* - })? +} + +macro_rules! invalid { + ($key:ident, $err:expr) => { + panic!("cargo environment variable `{}` is invalid: {}", $key, $err) }; - ($(#[$meta:meta])* $f:ident -> $out:ty = $var:ident $(; $($rest:tt)*)? ) => { - compile_error!(concat!("Provided unknown output type ", stringify!($out), " to export!")); +} + +#[track_caller] +fn get_bool(key: &str) -> bool { + env::var_os(key).is_some() +} + +#[track_caller] +fn get_opt_path(key: &str) -> Option { + let var = env::var_os(key)?; + Some(PathBuf::from(var)) +} - $(export! { - $($rest)* - })? +#[track_caller] +fn get_path(key: &str) -> PathBuf { + get_opt_path(key).unwrap_or_else(|| missing!(key)) +} + +#[track_caller] +fn get_opt_str(key: &str) -> Option { + let var = env::var_os(key)?; + match str::from_utf8(var.as_encoded_bytes()) { + Ok(s) => Some(s.to_owned()), + Err(err) => invalid!(key, err), + } +} + +#[track_caller] +fn get_str(key: &str) -> String { + get_opt_str(key).unwrap_or_else(|| missing!(key)) +} + +#[track_caller] +fn get_num(key: &str) -> T +where + T::Err: Display, +{ + let val = get_str(key); + match val.parse() { + Ok(num) => num, + Err(err) => invalid!(key, err), + } +} + +#[track_caller] +fn get_opt_cfg(cfg: &str) -> (String, Option>) { + let cfg = cfg.to_uppercase().replace('-', "_"); + let key = format!("CARGO_CFG_{cfg}"); + let Some(var) = env::var_os(&key) else { + return (key, None); }; + let val = str::from_utf8(var.as_encoded_bytes()).unwrap_or_else(|err| invalid!(key, err)); + (key, Some(val.split(',').map(str::to_owned).collect())) +} + +#[track_caller] +fn get_cfg(cfg: &str) -> Vec { + let (key, val) = get_opt_cfg(cfg); + val.unwrap_or_else(|| missing!(key)) +} + +// docs last updated to match release 1.77.2 reference + +/// Path to the `cargo` binary performing the build. +pub fn cargo() -> PathBuf { + get_path("CARGO") +} + +/// The directory containing the manifest for the package being built (the package +/// containing the build script). Also note that this is the value of the current +/// working directory of the build script when it starts. +pub fn cargo_manifest_dir() -> PathBuf { + get_path("CARGO_MANIFEST_DIR") +} + +/// Contains parameters needed for Cargo’s [jobserver] implementation to parallelize +/// subprocesses. Rustc or cargo invocations from build.rs can already read +/// `CARGO_MAKEFLAGS`, but GNU Make requires the flags to be specified either +/// directly as arguments, or through the `MAKEFLAGS` environment variable. +/// Currently Cargo doesn’t set the `MAKEFLAGS` variable, but it’s free for build +/// scripts invoking GNU Make to set it to the contents of `CARGO_MAKEFLAGS`. +/// +/// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html +pub fn cargo_manifest_links() -> Option { + get_opt_str("CARGO_MANIFEST_LINKS") } -export! { - /// Path to the `cargo` binary performing the build. - cargo -> PathBuf = CARGO; - /// The directory containing the manifest for the package being built (the - /// package containing the build script). Also note that this is the value - /// of the current working directory of the build script when it starts. - cargo_manifest_dir -> PathBuf = CARGO_MANIFEST_DIR; - /// The manifest links value. - cargo_manifest_links -> String = CARGO_MANIFEST_LINKS; - /// Contains parameters needed for Cargo's jobserver implementation to - /// parallelize subprocesses. Rustc or cargo invocations from build.rs - /// can already read CARGO_MAKEFLAGS, but GNU Make requires the flags - /// to be specified either directly as arguments, or through the MAKEFLAGS - /// environment variable. Currently Cargo doesn't set the MAKEFLAGS - /// variable, but it's free for build scripts invoking GNU Make to set it - /// to the contents of CARGO_MAKEFLAGS. - cargo_makeflags -> OsString = CARGO_MAKEFLAGS; - /// Set on [unix-like platforms](https://doc.rust-lang.org/reference/conditional-compilation.html#unix-and-windows). - cargo_cfg_unix -> bool = CARGO_CFG_UNIX; - /// Set on [windows-like platforms](https://doc.rust-lang.org/reference/conditional-compilation.html#unix-and-windows). - cargo_cfg_windows -> bool = CARGO_CFG_WINDOWS; - /// The [target family](https://doc.rust-lang.org/reference/conditional-compilation.html#target_family). - cargo_cfg_target_family -> Vec = CARGO_CFG_TARGET_FAMILY; - /// The [target operating system](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os). - cargo_cfg_target_os -> String = CARGO_CFG_TARGET_OS; - /// The CPU [target architecture](https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch). - cargo_cfg_target_arch -> String = CARGO_CFG_TARGET_ARCH; - /// The [target vendor](https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor). - cargo_cfg_target_vendor -> String = CARGO_CFG_TARGET_VENDOR; - /// The [target environment](https://doc.rust-lang.org/reference/conditional-compilation.html#target_env) ABI. - cargo_cfg_target_env -> String = CARGO_CFG_TARGET_ENV; - /// The CPU [pointer width](https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width). - cargo_cfg_pointer_width -> usize = CARGO_CFG_TARGET_POINTER_WIDTH; - /// Teh CPU [target endianness](https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian). - cargo_cfg_target_endian -> String = CARGO_CFG_TARGET_ENDIAN; - /// List of CPU [target features](https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature) enabled. - cargo_cfg_target_feature -> Vec = CARGO_CFG_TARGET_FEATURE; - /// The folder in which all output should be placed. This folder is inside - /// the build directory for the package being built, and it is unique for - /// the package in question. - out_dir -> PathBuf = OUT_DIR; - /// The target triple that is being compiled for. Native code should be - /// compiled for this triple. See the [Target Triple] description for - /// more information. - /// - /// [Target Triple]: https://doc.rust-lang.org/cargo/appendix/glossary.html#target - target -> String = TARGET; - /// The host triple of the Rust compiler. - host -> String = HOST; - /// The parallelism specified as the top-level parallelism. This can be - /// useful to pass a `-j` parameter to a system like `make`. Note that care - /// should be taken when interpreting this environment variable. For - /// historical purposes this is still provided but recent versions of - /// Cargo, for example, do not need to run `make -j`, and instead can set - /// the `MAKEFLAGS` env var to the content of `CARGO_MAKEFLAGS` to activate - /// the use of Cargo's GNU Make compatible [jobserver] for sub-make - /// invocations. - /// - /// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html - num_jobs -> String = NUM_JOBS; - /// Value of the corresponding variable for the profile currently being built. - opt_level -> String = OPT_LEVEL; - /// Value of the corresponding variable for the profile currently being built. - debug -> String = DEBUG; - /// `release` for release builds, `debug` for other builds. This is - /// determined based on if the profile inherits from the [`dev`] or - /// [`release`] profile. Using this environment variable is not - /// recommended. Using other environment variables like `OPT_LEVEL` - /// provide a more correct view of the actual settings being used. - /// - /// [`dev`]: https://doc.rust-lang.org/cargo/reference/profiles.html#dev - /// [`release`]: https://doc.rust-lang.org/cargo/reference/profiles.html#release - profile -> String = PROFILE; - /// The compiler that Cargo has resolved to use, passed to the build script - /// so it might use it as well. - rustc -> PathBuf = RUSTC; - /// The documentation generator that Cargo has resolved to use, passed to - /// the build script so it might use it as well. - rustdoc -> PathBuf = RUSTDOC; - /// The `rustc` wrapper, if any, that Cargo is using. See - /// [`build.rustc-wrapper`](https://doc.rust-lang.org/cargo/reference/config.html#buildrustc-wrapper). - rustc_wrapper -> Option = RUSTC_WRAPPER; - /// The `rustc` wrapper, if any, that Cargo is using for workspace members. - /// See [`build.rustc-workspace-wrapper`](https://doc.rust-lang.org/cargo/reference/config.html#buildrustc-workspace-wrapper). - rustc_workspace_wrapper -> Option = RUSTC_WORKSPACE_WRAPPER; - /// The path to the linker binary that Cargo has resolved to use for the - /// current target, if specified. - rustc_linker -> Option = RUSTC_LINKER; - /// The full version of your package. - cargo_pkg_version -> String = CARGO_PKG_VERSION; - /// The major version of your package. - cargo_pkg_version_major -> usize = CARGO_PKG_VERSION_MAJOR; - /// The minor version of your package. - cargo_pkg_version_minor -> usize = CARGO_PKG_VERSION_MINOR; - /// The patch version of your package. - cargo_pkg_version_patch -> usize = CARGO_PKG_VERSION_PATCH; - /// The pre-release of your package. - cargo_pkg_version_pre -> String = CARGO_PKG_VERSION_PRE; - /// The name of your package. - cargo_pkg_name -> String = CARGO_PKG_NAME; - /// The description from the manifest of your package. - cargo_pkg_description -> String = CARGO_PKG_DESCRIPTION; - /// The home page from the manifest of your package. - cargo_pkg_homepage -> String = CARGO_PKG_HOMEPAGE; - /// The repository from the manifest of your package. - cargo_pkg_repository -> String = CARGO_PKG_REPOSITORY; - /// The license from the manifest of your package. - cargo_pkg_license -> String = CARGO_PKG_LICENSE; - /// The license file from the manifest of your package. - cargo_pkg_license_file -> String = CARGO_PKG_LICENSE_FILE; -} - -/// For each activated feature of the package being built, this will be true. +/// For each activated feature of the package being built, this will be `true`. pub fn cargo_feature(name: &str) -> bool { - let key = format!("CARGO_FEATURE_{}", name.to_uppercase().replace('-', "_")); - env::var_os(key).is_some() + let name = name.to_uppercase().replace('-', "_"); + let key = format!("CARGO_FEATURE_{name}"); + get_bool(&key) } -/// For each [configuration option] of the package being built, this will -/// contain the value of the configuration. Boolean configurations are present -/// if they are set, and not present otherwise. This includes values built-in -/// to the compiler (which can be seen with `rustc --print=cfg`) and values set -/// by build scripts and extra flags passed to `rustc` (such as those defined -/// in `RUSTFLAGS`). +/// For each [configuration option] of the package being built, this will contain +/// the value of the configuration. This includes values built-in to the compiler +/// (which can be seen with `rustc --print=cfg`) and values set by build scripts +/// and extra flags passed to rustc (such as those defined in `RUSTFLAGS`). /// -/// [configuration option]: https://doc.rust-lang.org/reference/conditional-compilation.html +/// [configuration option]: https://doc.rust-lang.org/stable/reference/conditional-compilation.html pub fn cargo_cfg(cfg: &str) -> Option> { - let key = format!("CARGO_CFG_{}", cfg.to_uppercase().replace('-', "_")); - let val = env::var_os(&key)?.into_string().unwrap_or_else(|_| { - panic!("cargo buildscript env var ${key} contained invalid UTF-8"); - }); - Some(val.split(',').map(Into::into).collect()) -} - -/// Each build script can generate an arbitrary set of metadata in the form of -/// key-value pairs. This metadata is passed to the build scripts of -/// **dependent** packages. For example, if the package `bar` depends on `foo`, -/// then if `foo` generates `key=value` as part of its build script metadata, -/// then the build script of `bar` will have the environment variables -/// `DEP_FOO_KEY=value`. + let (_, val) = get_opt_cfg(cfg); + val +} + +/// Set on [unix-like platforms](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#unix-and-windows). +pub fn cargo_cfg_unix() -> bool { + get_bool("CARGO_CFG_UNIX") +} + +/// Set on [windows-like platforms](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#unix-and-windows). +pub fn cargo_cfg_windows() -> bool { + get_bool("CARGO_CFG_WINDOWS") +} + +/// The [target family](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_family). +pub fn cargo_target_family() -> Vec { + get_cfg("target_family") +} + +/// The [target operating system](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_os). +/// This value is similar to the second and third element of the platform's target triple. +pub fn cargo_cfg_target_os() -> String { + get_str("CARGO_CFG_TARGET_OS") +} + +/// The CPU [target architecture](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_arch). +/// This is similar to the first element of the platform's target triple, but not identical. +pub fn cargo_cfg_target_arch() -> String { + get_str("CARGO_CFG_TARGET_ARCH") +} + +/// The [target vendor](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_vendor). +pub fn cargo_cfg_target_vendor() -> String { + get_str("CARGO_CFG_TARGET_VENDOR") +} + +/// The [target environment](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_env) ABI. +/// This value is similar to the fourth element of the platform's target triple. +/// +/// For historical reasons, this value is only defined as not the empty-string when +/// actually needed for disambiguation. Thus, for example, on many GNU platforms, +/// this value will be empty. +pub fn cargo_cfg_target_env() -> String { + get_str("CARGO_CFG_TARGET_ENV") +} + +/// The CPU [pointer width](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_pointer_width). +pub fn cargo_cfg_target_pointer_width() -> u32 { + get_num("CARGO_CFG_TARGET_POINTER_WIDTH") +} + +/// The CPU [target endianness](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_endian). +pub fn cargo_cfg_target_endian() -> String { + get_str("CARGO_CFG_TARGET_ENDIAN") +} + +/// List of CPU [target features](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_feature) enabled. +pub fn cargo_cfg_target_feature() -> Vec { + get_cfg("target_feature") +} + +/// List of CPU [supported atomic widths](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_has_atomic). +pub fn cargo_cfg_target_has_atomic() -> Vec { + get_cfg("target_has_atomic") +} + +/// List of atomic widths that have equal alignment requirements. +/// +#[doc = unstable!(cfg_target_has_atomic_equal_alignment, 93822)] +#[cfg(feature = "unstable")] +pub fn cargo_cfg_target_has_atomic_equal_alignment() -> Vec { + get_cfg("target_has_atomic_equal_alignment") +} + +/// List of atomic widths that have atomic load and store operations. +/// +#[doc = unstable!(cfg_target_has_atomic_load_store, 94039)] +#[cfg(feature = "unstable")] +pub fn cargo_cfg_target_has_atomic_load_store() -> Vec { + get_cfg("target_has_atomic_load_store") +} + +/// If the target supports thread-local storage. +/// +#[doc = unstable!(cfg_target_thread_local, 29594)] +#[cfg(feature = "unstable")] +pub fn cargo_cfg_target_thread_local() -> bool { + get_bool("CARGO_CFG_TARGET_THREAD_LOCAL") +} + +/// The [panic strategy](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#panic). +pub fn cargo_cfg_panic() -> String { + get_str("CARGO_CFG_PANIC") +} + +/// If we are compiling with debug assertions enabled. +pub fn cargo_cfg_debug_assertions() -> bool { + get_bool("CARGO_CFG_DEBUG_ASSERTIONS") +} + +/// If we are compiling with overflow checks enabled. +/// +#[doc = unstable!(cfg_overflow_checks, 111466)] +#[cfg(feature = "unstable")] +pub fn cargo_cfg_overflow_checks() -> bool { + get_bool("CARGO_CFG_OVERFLOW_CHECKS") +} + +/// If we are compiling with UB checks enabled. +/// +#[doc = unstable!(cfg_ub_checks, 123499)] +#[cfg(feature = "unstable")] +pub fn cargo_cfg_ub_checks() -> bool { + get_bool("CARGO_CFG_UB_CHECKS") +} + +/// The target relocation model. +/// +#[doc = unstable!(cfg_relocation_model, 114929)] +#[cfg(feature = "unstable")] +pub fn cargo_cfg_relocation_model() -> String { + get_str("CARGO_CFG_RELOCATION_MODEL") +} + +/// The folder in which all output and intermediate artifacts should be placed. +/// This folder is inside the build directory for the package being built, and +/// it is unique for the package in question. +pub fn out_dir() -> PathBuf { + get_path("OUT_DIR") +} + +/// The [target triple] that is being compiled for. Native code should be compiled +/// for this triple. +/// +/// [target triple]: https://doc.rust-lang.org/stable/cargo/appendix/glossary.html#target +pub fn target() -> String { + get_str("TARGET") +} + +/// The host triple of the Rust compiler. +pub fn host() -> String { + get_str("HOST") +} + +/// The parallelism specified as the top-level parallelism. This can be useful to +/// pass a `-j` parameter to a system like `make`. Note that care should be taken +/// when interpreting this value. For historical purposes this is still provided +/// but Cargo, for example, does not need to run `make -j`, and instead can set the +/// `MAKEFLAGS` env var to the content of `CARGO_MAKEFLAGS` to activate the use of +/// Cargo’s GNU Make compatible [jobserver] for sub-make invocations. +/// +/// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html +pub fn num_jobs() -> u32 { + get_num("NUM_JOBS") +} + +/// The [level of optimization](https://doc.rust-lang.org/stable/cargo/reference/profiles.html#opt-level). +pub fn opt_level() -> String { + get_str("OPT_LEVEL") +} + +/// The amount of [debug information](https://doc.rust-lang.org/stable/cargo/reference/profiles.html#debug) included. +pub fn debug() -> String { + get_str("DEBUG") +} + +/// `release` for release builds, `debug` for other builds. This is determined based +/// on if the [profile] inherits from the [`dev`] or [`release`] profile. Using this +/// function is not recommended. Using other functions like [`opt_level`] provides +/// a more correct view of the actual settings being used. +/// +/// [profile]: https://doc.rust-lang.org/stable/cargo/reference/profiles.html +/// [`dev`]: https://doc.rust-lang.org/stable/cargo/reference/profiles.html#dev +/// [`release`]: https://doc.rust-lang.org/stable/cargo/reference/profiles.html#release +pub fn profile() -> String { + get_str("PROFILE") +} + +/// [Metadata] set by dependencies. For more information, see build script +/// documentation about [the `links` manifest key][links]. +/// +/// [metadata]: crate::output::metadata +/// [links]: https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#the-links-manifest-key pub fn dep(name: &str, key: &str) -> Option { - let key = format!( - "DEP_{}_{}", - name.to_uppercase().replace('-', "_"), - key.to_uppercase().replace('-', "_") - ); - let val = env::var_os(&key)?.into_string().unwrap_or_else(|_| { - panic!("cargo buildscript env var ${key} contained invalid UTF-8"); - }); - Some(val) -} - -/// Extra flags that Cargo invokes rustc with. See [`build.rustflags`]. Note -/// that since Rust 1.55, `RUSTFLAGS` is removed from the environment; scripts -/// should use `CARGO_ENCODED_RUSTFLAGS` instead. + let name = name.to_uppercase().replace('-', "_"); + let key = key.to_uppercase().replace('-', "_"); + let key = format!("DEP_{name}_{key}"); + get_opt_str(&key) +} + +/// The compiler that Cargo has resolved to use. +pub fn rustc() -> PathBuf { + get_path("RUSTC") +} + +/// The documentation generator that Cargo has resolved to use. +pub fn rustdoc() -> PathBuf { + get_path("RUSTDOC") +} + +/// The rustc wrapper, if any, that Cargo is using. See [`build.rustc-wrapper`]. +/// +/// [`build.rustc-wrapper`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustc-wrapper +pub fn rustc_wrapper() -> Option { + get_opt_path("RUSTC_WRAPPER") +} + +/// The rustc wrapper, if any, that Cargo is using for workspace members. See +/// [`build.rustc-workspace-wrapper`]. +/// +/// [`build.rustc-workspace-wrapper`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustc-workspace-wrapper +pub fn rustc_workspace_wrapper() -> Option { + get_opt_path("RUSTC_WORKSPACE_WRAPPER") +} + +/// The linker that Cargo has resolved to use for the current target, if specified. +/// +/// [`target.*.linker`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#targettriplelinker +pub fn rustc_linker() -> Option { + get_opt_path("RUSTC_LINKER") +} + +/// Extra flags that Cargo invokes rustc with. See [`build.rustflags`]. /// -/// [`build.rustflags`]: https://doc.rust-lang.org/cargo/reference/config.html#buildrustflags +/// [`build.rustflags`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustflags pub fn cargo_encoded_rustflags() -> Vec { - let val = env::var_os("CARGO_ENCODED_RUSTFLAGS") - .expect("cargo buildscript env var $CARGO_ENCODED_RUSTFLAGS") - .into_string() - .expect("cargo buildscript env var $CARGO_ENCODED_RUSTFLAGS contained invalid UTF-8"); - val.split('\x1f').map(Into::into).collect() + get_str("CARGO_ENCODED_RUSTFLAGS") + .split('\x1f') + .map(str::to_owned) + .collect() } -/// List of authors from the manifest of your package. +/// The full version of your package. +pub fn cargo_pkg_version() -> String { + get_str("CARGO_PKG_VERSION") +} + +/// The major version of your package. +pub fn cargo_pkg_version_major() -> u64 { + get_num("CARGO_PKG_VERSION_MAJOR") +} + +/// The minor version of your package. +pub fn cargo_pkg_version_minor() -> u64 { + get_num("CARGO_PKG_VERSION_MINOR") +} + +/// The patch version of your package. +pub fn cargo_pkg_version_patch() -> u64 { + get_num("CARGO_PKG_VERSION_PATCH") +} + +/// The pre-release version of your package. +pub fn cargo_pkg_version_pre() -> String { + get_str("CARGO_PKG_VERSION_PRE") +} + +/// Colon separated list of authors from the manifest of your package. pub fn cargo_pkg_authors() -> Vec { - let val = env::var_os("CARGO_PKG_AUTHORS") - .expect("cargo buildscript env var $CARGO_PKG_AUTHORS") - .into_string() - .expect("cargo buildscript env var $CARGO_PKG_AUTHORS contained invalid UTF-8"); - val.split(':').map(Into::into).collect() + get_str("CARGO_PKG_AUTHORS") + .split(':') + .map(str::to_owned) + .collect() +} + +/// The name of your package. +pub fn cargo_pkg_name() -> String { + get_str("CARGO_PKG_NAME") +} + +/// The description from the manifest of your package. +pub fn cargo_pkg_description() -> String { + get_str("CARGO_PKG_DESCRIPTION") +} + +/// The Rust version from the manifest of your package. Note that this is the +/// minimum Rust version supported by the package, not the current Rust version. +pub fn cargo_pkg_rust_version() -> String { + get_str("CARGO_PKG_RUST_VERSION") } diff --git a/src/lib.rs b/src/lib.rs index b8e486fb94f..0637fe2565a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,21 +1,26 @@ -//! Convenience wrappers for cargo buildscript input/output. -//! -//! # Example -//! -//! ```rust -//! build::rerun_if_changed("build.rs"); // only rerun for buildscript changes -//! build::rustc_cfg("has_buildrs"); // set #[cfg(has_buildrs)] -//! dbg!(build::cargo()); // path to the cargo executable -//! dbg!(build::cargo_manifest_dir()); // the directory of the build manifest -//! ``` +//! build-rs provides a strongly typed interface around the Cargo build script +//! protocol. Cargo provides inputs to the build script by environment variable +//! and accepts commands by printing to stdout. +#![cfg_attr(all(doc, feature = "unstable"), feature(doc_auto_cfg, doc_cfg))] -/// Inputs to the build script, in the form of environment variables. +#[cfg(feature = "unstable")] +macro_rules! unstable { + ($feature:ident, $issue:literal) => { + concat!( + r#"
"#, + r#"🔬"#, + r#"This is a nightly-only experimental API. ("#, + stringify!($feature), + r#" #"#, + $issue, + r#")"#, + r#"
"# + ) + }; +} + +mod allow_use; pub mod input; -/// Outputs from the build script, in the form of `cargo:` printed lines. -/// -/// _Does not print a leading newline._ Thus, if you ever write to stdout and -/// don't lock until a trailing newline, these instructions will likely fail. pub mod output; - -#[doc(no_inline)] -pub use crate::{input::*, output::*}; diff --git a/src/output.rs b/src/output.rs index 75bc450fcca..f02734e8271 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1,188 +1,315 @@ -use std::{ffi::OsStr, path::Path}; +//! Outputs from the build script to the build system. +//! +//! This crate assumes that stdout is at a new line whenever an output directive +//! is called. Printing to stdout without a terminating newline (i.e. not using +//! [`println!`]) may lead to surprising behavior. +//! +//! Reference: -/// The rustc-link-arg instruction tells Cargo to pass the -/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg) -/// to the compiler, but only when building supported targets (benchmarks, -/// binaries, cdylib crates, examples, and tests). Its usage is highly platform -/// specific. It is useful to set the shared library version or linker script. +use crate::allow_use; +use std::{ffi::OsStr, fmt::Display, path::Path, str}; + +fn emit(directive: &str, value: impl Display) { + if allow_use::double_colon_directives() { + println!("cargo::{}={}", directive, value); + } else { + println!("cargo:{}={}", directive, value); + } +} + +/// The `rerun-if-changed` instruction tells Cargo to re-run the build script if the +/// file at the given path has changed. Currently, Cargo only uses the filesystem +/// last-modified “mtime” timestamp to determine if the file has changed. It +/// compares against an internal cached timestamp of when the build script last ran. +/// +/// If the path points to a directory, it will scan the entire directory for any +/// modifications. +/// +/// If the build script inherently does not need to re-run under any circumstance, +/// then calling `rerun_if_changed("build.rs")` is a simple way to prevent it from +/// being re-run (otherwise, the default if no `rerun-if` instructions are emitted +/// is to scan the entire package directory for changes). Cargo automatically +/// handles whether or not the script itself needs to be recompiled, and of course +/// the script will be re-run after it has been recompiled. Otherwise, specifying +/// `build.rs` is redundant and unnecessary. +pub fn rerun_if_changed(path: impl AsRef) { + let Some(path) = path.as_ref().to_str() else { + panic!("cannot emit rerun-if-changed: path is not UTF-8"); + }; + if path.contains('\n') { + panic!("cannot emit rerun-if-changed: path contains newline"); + } + emit("rerun-if-changed", path); +} + +/// The `rerun-if-env-changed` instruction tells Cargo to re-run the build script +/// if the value of an environment variable of the given name has changed. +/// +/// Note that the environment variables here are intended for global environment +/// variables like `CC` and such, it is not possible to use this for environment +/// variables like `TARGET` that [Cargo sets for build scripts][build-env]. The +/// environment variables in use are those received by cargo invocations, not +/// those received by the executable of the build script. +/// +/// [build-env]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts +pub fn rerun_if_env_changed(key: impl AsRef) { + let Some(key) = key.as_ref().to_str() else { + panic!("cannot emit rerun-if-env-changed: key is not UTF-8"); + }; + if key.contains('\n') { + panic!("cannot emit rerun-if-env-changed: key contains newline"); + } + emit("rerun-if-env-changed", key); +} + +/// The `rustc-link-arg` instruction tells Cargo to pass the +/// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building +/// supported targets (benchmarks, binaries, cdylib crates, examples, and tests). +/// Its usage is highly platform specific. It is useful to set the shared library +/// version or linker script. +/// +/// [link-arg]: https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg pub fn rustc_link_arg(flag: &str) { - println!("cargo:rustc-link-arg={flag}"); + if flag.contains('\n') { + panic!("cannot emit rustc-link-arg: invalid flag"); + } + emit("rustc-link-arg", flag); } /// The `rustc-link-arg-bin` instruction tells Cargo to pass the -/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg) -/// to the compiler, but only when building the binary target with name `BIN`. -/// Its usage is highly platform specific. It is useful to set a linker script -/// or other linker options. +/// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building +/// the binary target with name `BIN`. Its usage is highly platform specific. It +/// is useful to set a linker script or other linker options. +/// +/// [link-arg]: https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg pub fn rustc_link_arg_bin(bin: &str, flag: &str) { - println!("cargo:rustc-link-arg-bin={bin}={flag}"); + if bin.contains(['=', '\n']) { + panic!("cannot emit rustc-link-arg-bin: invalid bin name"); + } + emit("rustc-link-arg-bin", format_args!("{}={}", bin, flag)); } /// The `rustc-link-arg-bins` instruction tells Cargo to pass the -/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg) -/// to the compiler, but only when building a binary target. Its usage is -/// highly platform specific. It is useful to set a linker script or other -/// linker options. +/// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building +/// the binary target. Its usage is highly platform specific. It is useful to set +/// a linker script or other linker options. +/// +/// [link-arg]: https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg pub fn rustc_link_arg_bins(flag: &str) { - println!("cargo:rustc-link-arg-bins={flag}"); + if flag.contains('\n') { + panic!("cannot emit rustc-link-arg-bins: invalid flag"); + } + emit("rustc-link-arg-bins", flag); } -/// The `rustc-link-lib` instruction tells Cargo to link the given library -/// using the compiler's [`-l` flag]. This is typically used to link a native -/// library using [`FFI`]. +/// The `rustc-link-arg-tests` instruction tells Cargo to pass the +/// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building +/// a tests target. +pub fn rustc_link_arg_tests(flag: &str) { + if flag.contains('\n') { + panic!("cannot emit rustc-link-arg-tests: invalid flag"); + } + emit("rustc-link-arg-tests", flag); +} + +/// The `rustc-link-arg-examples` instruction tells Cargo to pass the +/// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building +/// an examples target. +pub fn rustc_link_arg_examples(flag: &str) { + if flag.contains('\n') { + panic!("cannot emit rustc-link-arg-examples: invalid flag"); + } + emit("rustc-link-arg-examples", flag); +} + +/// The `rustc-link-arg-benches` instruction tells Cargo to pass the +/// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building +/// a benchmark target. +pub fn rustc_link_arg_benches(flag: &str) { + if flag.contains('\n') { + panic!("cannot emit rustc-link-arg-benches: invalid flag"); + } + emit("rustc-link-arg-benches", flag); +} + +/// The `rustc-link-lib` instruction tells Cargo to link the given library using +/// the compiler’s [`-l` flag][-l]. This is typically used to link a native library +/// using [FFI]. /// -/// The `-l` flag is only passed to the library target of the package, unless -/// there is no library target, in which case it is passed to all targets. This -/// is done because all other targets have an implicit dependency on the -/// library target, and the given library to link should only be included once. -/// This means that if a package has both a library and a binary target, the -/// _library_ has access to the symbols from the given lib, and the binary -/// should access them through the library target's public API. +/// The `LIB` string is passed directly to rustc, so it supports any syntax that +/// `-l` does. Currently the full supported syntax for `LIB` is +/// `[KIND[:MODIFIERS]=]NAME[:RENAME]`. /// -/// The optional `KIND` may be one of `dylib`, `static`, or `framework`. -/// See the [rustc book] for more detail. +/// The `-l` flag is only passed to the library target of the package, unless there +/// is no library target, in which case it is passed to all targets. This is done +/// because all other targets have an implicit dependency on the library target, +/// and the given library to link should only be included once. This means that +/// if a package has both a library and a binary target, the library has access +/// to the symbols from the given lib, and the binary should access them through +/// the library target’s public API. /// -/// [`-l` flag]: https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-link-lib -/// [rustc book]: https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-link-lib -pub fn rustc_link_lib(name: &str) { - println!("cargo:rustc-link-lib={name}"); +/// The optional `KIND` may be one of dylib, static, or framework. See the +/// [rustc book][-l] for more detail. +/// +/// [-l]: https://doc.rust-lang.org/stable/rustc/command-line-arguments.html#option-l-link-lib +/// [FFI]: https://doc.rust-lang.org/stable/nomicon/ffi.html +pub fn rustc_link_lib(lib: &str) { + if lib.contains('\n') { + panic!("cannot emit rustc-link-lib: invalid lib"); + } + emit("rustc-link-lib", lib); } -/// See [`rustc_link_lib`]. -pub fn rustc_link_lib_kind(kind: &str, name: &str) { - println!("cargo:rustc-link-lib={kind}={name}"); +/// Like [`rustc_link_lib`], but with KIND specified separately. +pub fn rustc_link_lib_kind(kind: &str, lib: &str) { + if kind.contains(['=', '\n']) { + panic!("cannot emit rustc-link-lib: invalid kind"); + } + if lib.contains('\n') { + panic!("cannot emit rustc-link-lib: invalid lib"); + } + emit("rustc-link-lib", format_args!("{kind}={lib}")); } -/// The `rustc-link-search` instruction tells Cargo to pass the [`-L flag`] to -/// the compiler to add a directory to the library search path. +/// The `rustc-link-search` instruction tells Cargo to pass the [`-L` flag] to the +/// compiler to add a directory to the library search path. /// -/// The optional `KIND` may be one of `dependency`, `crate`, `native`, -/// `framework`, or `all`. See the [rustc book] for more detail. +/// The optional `KIND` may be one of `dependency`, `crate`, `native`, `framework`, +/// or `all`. See the [rustc book][-L] for more detail. /// /// These paths are also added to the -/// [dynamic library search path environment variable] if they are within the -/// `OUT_DIR`. Depending on this behavior is discouraged since this makes it -/// difficult to use the resulting binary. In general, it is best to avoid -/// creating dynamic libraries in a build script (using existing system +/// [dynamic library search path environment variable][search-path] if they are +/// within the `OUT_DIR`. Depending on this behavior is discouraged since this +/// makes it difficult to use the resulting binary. In general, it is best to +/// avoid creating dynamic libraries in a build script (using existing system /// libraries is fine). /// -/// [`-L flag`]: https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-search-path -/// [rustc book]: https://doc.rust-lang.org/rustc/command-line-arguments.html#option-l-search-path -/// [dynamic library search path environment variable]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#dynamic-library-paths +/// [-L]: https://doc.rust-lang.org/stable/rustc/command-line-arguments.html#option-l-search-path +/// [search-path]: https://doc.rust-lang.org/stable/cargo/reference/environment-variables.html#dynamic-library-paths pub fn rustc_link_search(path: impl AsRef) { - let path = path - .as_ref() - .as_os_str() - .to_str() - .expect("cannot print non-UTF8 path"); - println!("cargo:rustc-link-search={path}"); + let Some(path) = path.as_ref().to_str() else { + panic!("cannot emit rustc-link-search: path is not UTF-8"); + }; + if path.contains('\n') { + panic!("cannot emit rustc-link-search: path contains newline"); + } + emit("rustc-link-search", path); } -/// See [`rustc_link_search`]. +/// Like [`rustc_link_search`], but with KIND specified separately. pub fn rustc_link_search_kind(kind: &str, path: impl AsRef) { - let path = path - .as_ref() - .as_os_str() - .to_str() - .expect("cannot print non-UTF8 path"); - println!("cargo:rustc-link-search={kind}={path}"); + if kind.contains(['=', '\n']) { + panic!("cannot emit rustc-link-search: invalid kind"); + } + let Some(path) = path.as_ref().to_str() else { + panic!("cannot emit rustc-link-search: path is not UTF-8"); + }; + if path.contains('\n') { + panic!("cannot emit rustc-link-search: path contains newline"); + } + emit("rustc-link-search", format_args!("{kind}={path}")); } /// The `rustc-flags` instruction tells Cargo to pass the given space-separated /// flags to the compiler. This only allows the `-l` and `-L` flags, and is -/// equivalent to using `rustc-link-lib` and `rustc-link-search`. +/// equivalent to using [`rustc_link_lib`] and [`rustc_link_search`]. pub fn rustc_flags(flags: &str) { - println!("cargo:rustc-flags={flags}"); + if flags.contains('\n') { + panic!("cannot emit rustc-flags: invalid flags"); + } + emit("rustc-flags", flags); } /// The `rustc-cfg` instruction tells Cargo to pass the given value to the -/// [`--cfg` flag](https://doc.rust-lang.org/rustc/command-line-arguments.html#option-cfg) -/// to the compiler. This may be used for compile-time detection of features to -/// enable [conditional compilation](https://doc.rust-lang.org/reference/conditional-compilation.html). +/// [`--cfg` flag][--cfg] to the compiler. This may be used for compile-time +/// detection of features to enable conditional compilation. /// -/// Note that this does not affect Cargo's dependency resolution. This cannot +/// Note that this does not affect Cargo’s dependency resolution. This cannot /// be used to enable an optional dependency, or enable other Cargo features. /// -/// Be aware that Cargo features use the form `feature="foo"`. `cfg` values +/// Be aware that [Cargo features] use the form `feature="foo"`. `cfg` values /// passed with this flag are not restricted to that form, and may provide just /// a single identifier, or any arbitrary key/value pair. For example, emitting -/// `cargo:rustc-cfg=abc` will then allow code to use `#[cfg(abc)]` (note the -/// lack of `feature=`). Or an arbitrary key/value pair may be used with an `=` -/// symbol like `cargo:rustc-cfg=my_component="foo"`. The key should be a Rust -/// identifier, the value should be a string. +/// `rustc_cfg("abc")` will then allow code to use `#[cfg(abc)]` (note the lack +/// of `feature=`). Or an arbitrary key/value pair may be used with an `=` symbol +/// like `rustc_cfg(r#"my_component="foo""#)`. The key should be a Rust identifier, +/// the value should be a string. +/// +/// [--cfg]: https://doc.rust-lang.org/rustc/command-line-arguments.html#option-cfg +/// [Cargo features]: https://doc.rust-lang.org/cargo/reference/features.html pub fn rustc_cfg(key: &str) { - println!("cargo:rustc-cfg={key}") + if key.contains('\n') { + panic!("cannot emit rustc-cfg: invalid key"); + } + emit("rustc-cfg", key); } -/// See [`rustc_cfg`]. +/// Like [`rustc_cfg`], but with the value specified separately. pub fn rustc_cfg_value(key: &str, value: &str) { - println!("cargo:rustc-cfg={key}={value}"); + let value = value.escape_default(); + if key.contains(['=', '\n']) { + panic!("cannot emit rustc-cfg-value: invalid key"); + } + emit("rustc-cfg", format_args!("{key}=\"{value}\"")); } -/// The `rustc-env` instruction tells Cargo to set the given environment -/// variable when compiling the package. The value can be then retrieved by the -/// [`env! macro`](env!) in the compiled crate. This is useful for embedding -/// additional metadata in crate's code, such as the hash of git HEAD or the +/// The `rustc-env` instruction tells Cargo to set the given environment variable +/// when compiling the package. The value can be then retrieved by the +/// [`env!` macro][env!] in the compiled crate. This is useful for embedding +/// additional metadata in crate’s code, such as the hash of git HEAD or the /// unique identifier of a continuous integration server. -pub fn rustc_env(var: &str, value: &str) { - println!("cargo:rustc-env={var}={value}"); +/// +/// See also the [environment variables automatically included by Cargo][cargo-env]. +/// +/// [cargo-env]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates +pub fn rustc_env(key: &str, value: &str) { + if key.contains(['=', '\n']) { + panic!("cannot emit rustc-env: invalid key"); + } + if value.contains('\n') { + panic!("cannot emit rustc-env: invalid value"); + } + emit("rustc-env", format_args!("{key}={value}")); } /// The `rustc-cdylib-link-arg` instruction tells Cargo to pass the -/// [`-C link-arg=FLAG` option](https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg) -/// to the compiler, but only when building a cdylib library target. Its usage -/// is highly platform specific. It is useful to set the shared library version -/// or the runtime-path. +/// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building +/// a `cdylib` library target. Its usage is highly platform specific. It is useful +/// to set the shared library version or the runtime-path. +/// +/// [link-arg]: https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg pub fn rustc_cdylib_link_arg(flag: &str) { - println!("cargo:rustc-cdylib-link-arg={flag}"); + if flag.contains('\n') { + panic!("cannot emit rustc-cdylib-link-arg: invalid flag"); + } + emit("rustc-cdylib-link-arg", flag); } /// The `warning` instruction tells Cargo to display a warning after the build /// script has finished running. Warnings are only shown for path dependencies -/// (that is, those you're working on locally), so for example warnings printed -/// out in crates.io crates are not emitted by default. The `-vv` "very verbose" +/// (that is, those you’re working on locally), so for example warnings printed +/// out in [crates.io] crates are not emitted by default. The `-vv` “very verbose” /// flag may be used to have Cargo display warnings for all crates. +/// +/// [crates.io]: https://crates.io/ pub fn warning(message: &str) { - println!("cargo:warning={message}"); -} - -/// The `rerun-if-changed` instruction tells Cargo to re-run the build script -/// if the file at the given path has changed. Currently, Cargo only uses the -/// filesystem last-modified "mtime" timestamp to determine if the file has -/// changed. It compares against an internal cached timestamp of when the build -/// script last ran. -/// -/// If the path points to a directory, it will scan the entire directory for -/// any modifications. -/// -/// If the build script inherently does not need to re-run under any -/// circumstance, then emitting `cargo:rerun-if-changed=build.rs` is a simple -/// way to prevent it from being re-run (otherwise, the default if no -/// `rerun-if` instructions are emitted is to scan the entire package -/// directory for changes). Cargo automatically handles whether or not the -/// script itself needs to be recompiled, and of course the script will be -/// re-run after it has been recompiled. Otherwise, specifying build.rs is -/// redundant and unnecessary. -pub fn rerun_if_changed(path: impl AsRef) { - let path = path - .as_ref() - .as_os_str() - .to_str() - .expect("cannot print non-UTF8 path"); - println!("cargo:rerun-if-changed={path}"); -} - -/// The `rerun-if-env-changed` instruction tells Cargo to re-run the build -/// script if the value of an environment variable of the given name has -/// changed. -/// -/// Note that the environment variables here are intended for global -/// environment variables like `CC` and such, it is not necessary to use this -/// for environment variables like `TARGET` that Cargo sets. -pub fn rerun_if_env_changed(name: impl AsRef) { - let name = name - .as_ref() - .to_str() - .expect("cannot print non-UTF8 env key"); - println!("cargo:rerun-if-env-changed={name}"); + if message.contains('\n') { + panic!("cannot emit warning: message contains newline"); + } + emit("warning", message); +} + +/// Metadata, used by `links` scripts. +pub fn metadata(key: &str, val: &str) { + if key.contains(['=', '\n']) { + panic!("cannot emit metadata: invalid key"); + } + if val.contains('\n') { + panic!("cannot emit metadata: invalid value"); + } + if allow_use::double_colon_directives() { + emit("metadata", format_args!("{}={}", key, val)); + } else { + emit(key, val); + } } diff --git a/test-lib/Cargo.toml b/test-lib/Cargo.toml new file mode 100644 index 00000000000..f2285ee3eab --- /dev/null +++ b/test-lib/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "build-rs-test-lib" +version = "0.2.0" +edition = "2021" +rust-version = "1.70" +publish = false + +[build-dependencies] +build-rs = { path = ".." } diff --git a/test-lib/build.rs b/test-lib/build.rs new file mode 100644 index 00000000000..b59bb77643b --- /dev/null +++ b/test-lib/build.rs @@ -0,0 +1,4 @@ +fn main() { + build_rs::output::rerun_if_changed("build.rs"); + build_rs::output::rustc_cfg("did_run_build_script"); +} diff --git a/test-lib/src/lib.rs b/test-lib/src/lib.rs new file mode 100644 index 00000000000..871cc4ee586 --- /dev/null +++ b/test-lib/src/lib.rs @@ -0,0 +1,4 @@ +#[test] +fn test() { + assert!(cfg!(did_run_build_script)); +} From e5516ae50cdc9f1a35bc09087b5f79662d2a455e Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Tue, 2 Jul 2024 15:03:52 -0400 Subject: [PATCH 008/525] configure renovate --- .github/renovate.json5 | 38 ++++++++++++++++++-------------- .github/workflows/automerge.yaml | 17 +++++++------- 2 files changed, 31 insertions(+), 24 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 948f0823284..05003daade9 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,35 +1,41 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "schedule": "every 6 weeks on tuesday", + "extends": [ + "config:recommended", + "schedule:weekly", + ":automergePatch", + ":automergeStableNonMajor", + ":combinePatchMinorReleases", + ":disableDependencyDashboard", + ":maintainLockFilesWeekly", + ":separateMajorReleases" + ], + "platformCommit": true, "customManagers": [ { "customType": "regex", + "description": "Update the minimal supported Rust version in Cargo.toml", "fileMatch": [ - "Cargo.toml$" + "(^|/)Cargo\\.toml$" ], "matchStrings": [ - "rust-version = \"(?\\d+\\.\\d+(\\.\\d+)?)\"" + "(^|\\[package\\])(.|\n)*?($|\n\\s*\\[)", // select package table + "rust-version = \"(?\\d+\\.\\d+(\\.\\d+)?)\"" // select rust-version ], + "matchStringsStrategy": "recursive", "depNameTemplate": "rust-version", "packageNameTemplate": "rust-lang/rust", - "datasourceTemplate": "github-releases" + "datasourceTemplate": "github-releases", + "versioningTemplate": "cargo", + "extractVersionTemplate": "^(?\\d+\\.\\d+)" // drop patch version } ], "packageRules": [ { - "commitMessageTopic": "bump rust-version", - "matchManagers": [ - "custom.regex" - ], - "matchPackageNames": [ - "rust-version" - ], - "extractVersion": "^(?\\d+\\.\\d+)", // drop patch version - "schedule": [ - "* * * * *" - ], "minimumReleaseAge": "6 months", - "internalChecksFilter": "strict" + "matchDepNames": [ + "rust-version" + ] } ] } \ No newline at end of file diff --git a/.github/workflows/automerge.yaml b/.github/workflows/automerge.yaml index 2ddc1dc2932..00e8eebe48c 100644 --- a/.github/workflows/automerge.yaml +++ b/.github/workflows/automerge.yaml @@ -15,11 +15,12 @@ jobs: env: PR_URL: ${{github.event.pull_request.html_url}} - renovate: - runs-on: ubuntu-latest - if: github.actor == 'renovate[bot]' - steps: - - name: Enable auto-merge - run: gh pr merge --auto --merge "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} + # # testing: renovate triggered automerge + # renovate: + # runs-on: ubuntu-latest + # if: github.actor == 'renovate[bot]' + # steps: + # - name: Enable auto-merge + # run: gh pr merge --auto --merge "$PR_URL" + # env: + # PR_URL: ${{github.event.pull_request.html_url}} From 7a5005345c229a870e217c21667444e4eef3aa0e Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Tue, 2 Jul 2024 15:39:10 -0400 Subject: [PATCH 009/525] configure renovate --- .github/renovate.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 05003daade9..86dcbd22c91 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -25,7 +25,7 @@ "matchStringsStrategy": "recursive", "depNameTemplate": "rust-version", "packageNameTemplate": "rust-lang/rust", - "datasourceTemplate": "github-releases", + "datasourceTemplate": "git-tags", "versioningTemplate": "cargo", "extractVersionTemplate": "^(?\\d+\\.\\d+)" // drop patch version } From 66a254404fce6dac1806caae93cbf325b2b11b8c Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Tue, 2 Jul 2024 15:44:18 -0400 Subject: [PATCH 010/525] configure renovate --- .github/renovate.json5 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 86dcbd22c91..a03512168b0 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -25,8 +25,8 @@ "matchStringsStrategy": "recursive", "depNameTemplate": "rust-version", "packageNameTemplate": "rust-lang/rust", - "datasourceTemplate": "git-tags", - "versioningTemplate": "cargo", + "datasourceTemplate": "github-releases", + "versioningTemplate": "semver-coerced", "extractVersionTemplate": "^(?\\d+\\.\\d+)" // drop patch version } ], From 657b2aa10c0d9fd853a2219858f428a1a7a4ddf2 Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Tue, 2 Jul 2024 16:12:09 -0400 Subject: [PATCH 011/525] configure renovate --- .github/dependabot.yml | 7 ------- .github/renovate.json5 | 6 +----- .github/workflows/automerge.yaml | 26 -------------------------- 3 files changed, 1 insertion(+), 38 deletions(-) delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/automerge.yaml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index c3533d12882..00000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: 2 -updates: - - package-ecosystem: github-actions - directory: / - schedule: - interval: weekly - day: tuesday diff --git a/.github/renovate.json5 b/.github/renovate.json5 index a03512168b0..5539ca2525e 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -3,12 +3,8 @@ "extends": [ "config:recommended", "schedule:weekly", - ":automergePatch", ":automergeStableNonMajor", - ":combinePatchMinorReleases", - ":disableDependencyDashboard", - ":maintainLockFilesWeekly", - ":separateMajorReleases" + ":disableDependencyDashboard" ], "platformCommit": true, "customManagers": [ diff --git a/.github/workflows/automerge.yaml b/.github/workflows/automerge.yaml deleted file mode 100644 index 00e8eebe48c..00000000000 --- a/.github/workflows/automerge.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: Auto-merge safe bot PRs -on: pull_request - -permissions: - contents: write - pull-requests: write - -jobs: - dependabot: - runs-on: ubuntu-latest - if: github.actor == 'dependabot[bot]' - steps: - - name: Enable auto-merge - run: gh pr merge --auto --merge "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - - # # testing: renovate triggered automerge - # renovate: - # runs-on: ubuntu-latest - # if: github.actor == 'renovate[bot]' - # steps: - # - name: Enable auto-merge - # run: gh pr merge --auto --merge "$PR_URL" - # env: - # PR_URL: ${{github.event.pull_request.html_url}} From 85896ca15d0605c24ea980cee20e445d4e2e8cab Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Tue, 2 Jul 2024 16:33:41 -0400 Subject: [PATCH 012/525] update renovate --- .github/renovate.json5 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 5539ca2525e..ad88060f39e 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -3,7 +3,6 @@ "extends": [ "config:recommended", "schedule:weekly", - ":automergeStableNonMajor", ":disableDependencyDashboard" ], "platformCommit": true, @@ -28,6 +27,7 @@ ], "packageRules": [ { + "automerge": true, "minimumReleaseAge": "6 months", "matchDepNames": [ "rust-version" From 9b627d66b5518510965f328c9e6774d31cba39b7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 20:34:35 +0000 Subject: [PATCH 013/525] Update dependency rust-version to v1.75 --- Cargo.toml | 2 +- test-lib/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f8a31b551df..286f45dadc2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "build-rs" version = "0.2.0" edition = "2021" -rust-version = "1.70" +rust-version = "1.75" publish = false [features] diff --git a/test-lib/Cargo.toml b/test-lib/Cargo.toml index f2285ee3eab..a244acd974b 100644 --- a/test-lib/Cargo.toml +++ b/test-lib/Cargo.toml @@ -2,7 +2,7 @@ name = "build-rs-test-lib" version = "0.2.0" edition = "2021" -rust-version = "1.70" +rust-version = "1.75" publish = false [build-dependencies] From 25f8bd7a5222f288d9b7c04820a57c46ef8312a3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 01:28:57 +0000 Subject: [PATCH 014/525] Update dependency rust-version to v1.76 --- Cargo.toml | 2 +- test-lib/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 286f45dadc2..94d5841eb75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "build-rs" version = "0.2.0" edition = "2021" -rust-version = "1.75" +rust-version = "1.76" publish = false [features] diff --git a/test-lib/Cargo.toml b/test-lib/Cargo.toml index a244acd974b..e354bb96416 100644 --- a/test-lib/Cargo.toml +++ b/test-lib/Cargo.toml @@ -2,7 +2,7 @@ name = "build-rs-test-lib" version = "0.2.0" edition = "2021" -rust-version = "1.75" +rust-version = "1.76" publish = false [build-dependencies] From e243d715c51bea4d7eb374982d292fd015a2a532 Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Tue, 5 Nov 2024 13:38:50 -0500 Subject: [PATCH 015/525] Update renovate config The "6 months" it uses differs from dtolnay/rust-toolchain. Hopefully this means it will make update PRs at the right time. --- .github/renovate.json5 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index ad88060f39e..c719c650629 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -5,7 +5,7 @@ "schedule:weekly", ":disableDependencyDashboard" ], - "platformCommit": true, + "platformCommit": "enabled", "customManagers": [ { "customType": "regex", @@ -28,7 +28,7 @@ "packageRules": [ { "automerge": true, - "minimumReleaseAge": "6 months", + "minimumReleaseAge": "27 weeks", // 6 months + a little slack "matchDepNames": [ "rust-version" ] From 4f60fe0e58294e7513ad6e32d4bca31c1dff7cd8 Mon Sep 17 00:00:00 2001 From: Christopher Durham Date: Wed, 6 Nov 2024 16:23:07 -0500 Subject: [PATCH 016/525] Cleanup before upstream submission --- Cargo.lock | 9 + Cargo.toml | 3 + src/allow_use.rs | 33 +++- src/ident.rs | 27 +++ src/input.rs | 393 ++++++++++++++++++++++++++++++++------------ src/lib.rs | 8 + src/output.rs | 165 +++++++++++++++---- test-lib/Cargo.toml | 3 + test-lib/build.rs | 74 +++++++++ 9 files changed, 573 insertions(+), 142 deletions(-) create mode 100644 src/ident.rs diff --git a/Cargo.lock b/Cargo.lock index 2ff714842b0..3280b6e4352 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,6 +5,9 @@ version = 3 [[package]] name = "build-rs" version = "0.2.0" +dependencies = [ + "unicode-ident", +] [[package]] name = "build-rs-test-lib" @@ -12,3 +15,9 @@ version = "0.2.0" dependencies = [ "build-rs", ] + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" diff --git a/Cargo.toml b/Cargo.toml index 94d5841eb75..e81f8345519 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,6 +5,9 @@ edition = "2021" rust-version = "1.76" publish = false +[dependencies] +unicode-ident = "1.0.13" + [features] ## Experimental API. This feature flag is **NOT** semver stable. diff --git a/src/allow_use.rs b/src/allow_use.rs index fb39e9cfd29..15e9787aa4b 100644 --- a/src/allow_use.rs +++ b/src/allow_use.rs @@ -1,4 +1,4 @@ -use std::sync::OnceLock; +use std::{process::Command, sync::OnceLock}; fn rust_version_minor() -> u32 { static VERSION_MINOR: OnceLock = OnceLock::new(); @@ -6,12 +6,41 @@ fn rust_version_minor() -> u32 { crate::input::cargo_pkg_rust_version() .split('.') .nth(1) - .unwrap_or("70") + // assume build-rs's MSRV if none specified for the current package + .unwrap_or(env!("CARGO_PKG_RUST_VERSION").split('.').nth(1).unwrap()) + .parse() + .unwrap() + }) +} + +fn cargo_version_minor() -> u32 { + static VERSION_MINOR: OnceLock = OnceLock::new(); + *VERSION_MINOR.get_or_init(|| { + let out = Command::new(crate::input::cargo()) + .arg("-V") + .output() + .expect("running `cargo -V` should succeed"); + assert!(out.status.success(), "running `cargo -V` should succeed"); + + // > cargo -V # example output + // cargo 1.82.0 (8f40fc59f 2024-08-21) + + String::from_utf8(out.stdout).expect("`cargo -V` should output valid UTF-8") + ["cargo 1.".len()..] + .split('.') + .next() + .expect("`cargo -V` format should be stable") .parse() .unwrap() }) } pub(crate) fn double_colon_directives() -> bool { + // cargo errors on `cargo::` directives with insufficient package.rust-version rust_version_minor() >= 77 } + +pub(crate) fn check_cfg() -> bool { + // emit check-cfg if the toolchain being used supports it + cargo_version_minor() >= 80 +} diff --git a/src/ident.rs b/src/ident.rs new file mode 100644 index 00000000000..c84e1fee3f8 --- /dev/null +++ b/src/ident.rs @@ -0,0 +1,27 @@ +use unicode_ident::{is_xid_continue, is_xid_start}; + +pub(crate) fn is_feature_name(s: &str) -> bool { + s.chars() + .all(|ch| is_xid_continue(ch) || matches!(ch, '-' | '+' | '.')) +} + +pub(crate) fn is_ident(s: &str) -> bool { + let mut cs = s.chars(); + cs.next() + .is_some_and(|ch| is_xid_start(ch) || matches!(ch, '_')) + && cs.all(is_xid_continue) +} + +pub(crate) fn is_ascii_ident(s: &str) -> bool { + let mut cs = s.chars(); + cs.next() + .is_some_and(|ch| ch.is_ascii_alphabetic() || matches!(ch, '_')) + && cs.all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '_')) +} + +pub(crate) fn is_crate_name(s: &str) -> bool { + let mut cs = s.chars(); + cs.next() + .is_some_and(|ch| is_xid_start(ch) || matches!(ch, '-' | '_')) + && cs.all(|ch| is_xid_continue(ch) || matches!(ch, '-')) +} diff --git a/src/input.rs b/src/input.rs index 10a41965ba2..1b7adc36cd4 100644 --- a/src/input.rs +++ b/src/input.rs @@ -6,6 +6,7 @@ //! //! Reference: +use crate::ident::{is_ascii_ident, is_crate_name, is_feature_name}; use std::{ env, fmt::Display, @@ -14,13 +15,13 @@ use std::{ }; macro_rules! missing { - ($key:ident) => { + ($key:expr) => { panic!("cargo environment variable `{}` is missing", $key) }; } macro_rules! invalid { - ($key:ident, $err:expr) => { + ($key:expr, $err:expr) => { panic!("cargo environment variable `{}` is invalid: {}", $key, $err) }; } @@ -69,6 +70,9 @@ where #[track_caller] fn get_opt_cfg(cfg: &str) -> (String, Option>) { + if !is_ascii_ident(cfg) { + panic!("invalid configuration option {cfg:?}") + } let cfg = cfg.to_uppercase().replace('-', "_"); let key = format!("CARGO_CFG_{cfg}"); let Some(var) = env::var_os(&key) else { @@ -84,9 +88,10 @@ fn get_cfg(cfg: &str) -> Vec { val.unwrap_or_else(|| missing!(key)) } -// docs last updated to match release 1.77.2 reference +// docs last updated to match release 1.82.0 reference /// Path to the `cargo` binary performing the build. +#[track_caller] pub fn cargo() -> PathBuf { get_path("CARGO") } @@ -94,6 +99,7 @@ pub fn cargo() -> PathBuf { /// The directory containing the manifest for the package being built (the package /// containing the build script). Also note that this is the value of the current /// working directory of the build script when it starts. +#[track_caller] pub fn cargo_manifest_dir() -> PathBuf { get_path("CARGO_MANIFEST_DIR") } @@ -106,12 +112,17 @@ pub fn cargo_manifest_dir() -> PathBuf { /// scripts invoking GNU Make to set it to the contents of `CARGO_MAKEFLAGS`. /// /// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html +#[track_caller] pub fn cargo_manifest_links() -> Option { get_opt_str("CARGO_MANIFEST_LINKS") } /// For each activated feature of the package being built, this will be `true`. +#[track_caller] pub fn cargo_feature(name: &str) -> bool { + if !is_feature_name(name) { + panic!("invalid feature name {name:?}") + } let name = name.to_uppercase().replace('-', "_"); let key = format!("CARGO_FEATURE_{name}"); get_bool(&key) @@ -123,134 +134,249 @@ pub fn cargo_feature(name: &str) -> bool { /// and extra flags passed to rustc (such as those defined in `RUSTFLAGS`). /// /// [configuration option]: https://doc.rust-lang.org/stable/reference/conditional-compilation.html +#[track_caller] pub fn cargo_cfg(cfg: &str) -> Option> { let (_, val) = get_opt_cfg(cfg); val } -/// Set on [unix-like platforms](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#unix-and-windows). -pub fn cargo_cfg_unix() -> bool { - get_bool("CARGO_CFG_UNIX") -} +pub use self::cfg::*; +mod cfg { + use super::*; -/// Set on [windows-like platforms](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#unix-and-windows). -pub fn cargo_cfg_windows() -> bool { - get_bool("CARGO_CFG_WINDOWS") -} + // those disabled with #[cfg(any())] don't seem meaningfully useful + // but we list all cfg that are default known to check-cfg -/// The [target family](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_family). -pub fn cargo_target_family() -> Vec { - get_cfg("target_family") -} + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_clippy() -> bool { + get_bool("CARGO_CFG_CLIPPY") + } -/// The [target operating system](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_os). -/// This value is similar to the second and third element of the platform's target triple. -pub fn cargo_cfg_target_os() -> String { - get_str("CARGO_CFG_TARGET_OS") -} + /// If we are compiling with debug assertions enabled. + #[track_caller] + pub fn cargo_cfg_debug_assertions() -> bool { + get_bool("CARGO_CFG_DEBUG_ASSERTIONS") + } -/// The CPU [target architecture](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_arch). -/// This is similar to the first element of the platform's target triple, but not identical. -pub fn cargo_cfg_target_arch() -> String { - get_str("CARGO_CFG_TARGET_ARCH") -} + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_doc() -> bool { + get_bool("CARGO_CFG_DOC") + } -/// The [target vendor](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_vendor). -pub fn cargo_cfg_target_vendor() -> String { - get_str("CARGO_CFG_TARGET_VENDOR") -} + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_docsrs() -> bool { + get_bool("CARGO_CFG_DOCSRS") + } -/// The [target environment](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_env) ABI. -/// This value is similar to the fourth element of the platform's target triple. -/// -/// For historical reasons, this value is only defined as not the empty-string when -/// actually needed for disambiguation. Thus, for example, on many GNU platforms, -/// this value will be empty. -pub fn cargo_cfg_target_env() -> String { - get_str("CARGO_CFG_TARGET_ENV") -} + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_doctest() -> bool { + get_bool("CARGO_CFG_DOCTEST") + } -/// The CPU [pointer width](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_pointer_width). -pub fn cargo_cfg_target_pointer_width() -> u32 { - get_num("CARGO_CFG_TARGET_POINTER_WIDTH") -} + /// The level of detail provided by derived [`Debug`] implementations. + #[doc = unstable!(fmt_dbg, 129709)] + #[cfg(feature = "unstable")] + #[track_caller] + pub fn cargo_cfg_fmt_debug() -> String { + get_str("CARGO_CFG_FMT_DEBUG") + } -/// The CPU [target endianness](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_endian). -pub fn cargo_cfg_target_endian() -> String { - get_str("CARGO_CFG_TARGET_ENDIAN") -} + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_miri() -> bool { + get_bool("CARGO_CFG_MIRI") + } -/// List of CPU [target features](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_feature) enabled. -pub fn cargo_cfg_target_feature() -> Vec { - get_cfg("target_feature") -} + /// If we are compiling with overflow checks enabled. + #[doc = unstable!(cfg_overflow_checks, 111466)] + #[cfg(feature = "unstable")] + #[track_caller] + pub fn cargo_cfg_overflow_checks() -> bool { + get_bool("CARGO_CFG_OVERFLOW_CHECKS") + } -/// List of CPU [supported atomic widths](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_has_atomic). -pub fn cargo_cfg_target_has_atomic() -> Vec { - get_cfg("target_has_atomic") -} + /// The [panic strategy](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#panic). + #[track_caller] + pub fn cargo_cfg_panic() -> String { + get_str("CARGO_CFG_PANIC") + } -/// List of atomic widths that have equal alignment requirements. -/// -#[doc = unstable!(cfg_target_has_atomic_equal_alignment, 93822)] -#[cfg(feature = "unstable")] -pub fn cargo_cfg_target_has_atomic_equal_alignment() -> Vec { - get_cfg("target_has_atomic_equal_alignment") -} + /// If the crate is being compiled as a procedural macro. + #[track_caller] + pub fn cargo_cfg_proc_macro() -> bool { + get_bool("CARGO_CFG_PROC_MACRO") + } -/// List of atomic widths that have atomic load and store operations. -/// -#[doc = unstable!(cfg_target_has_atomic_load_store, 94039)] -#[cfg(feature = "unstable")] -pub fn cargo_cfg_target_has_atomic_load_store() -> Vec { - get_cfg("target_has_atomic_load_store") -} + /// The target relocation model. + #[doc = unstable!(cfg_relocation_model, 114929)] + #[cfg(feature = "unstable")] + #[track_caller] + pub fn cargo_cfg_relocation_model() -> String { + get_str("CARGO_CFG_RELOCATION_MODEL") + } -/// If the target supports thread-local storage. -/// -#[doc = unstable!(cfg_target_thread_local, 29594)] -#[cfg(feature = "unstable")] -pub fn cargo_cfg_target_thread_local() -> bool { - get_bool("CARGO_CFG_TARGET_THREAD_LOCAL") -} + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_rustfmt() -> bool { + get_bool("CARGO_CFG_RUSTFMT") + } -/// The [panic strategy](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#panic). -pub fn cargo_cfg_panic() -> String { - get_str("CARGO_CFG_PANIC") -} + /// Sanitizers enabled for the crate being compiled. + #[doc = unstable!(cfg_sanitize, 39699)] + #[cfg(feature = "unstable")] + #[track_caller] + pub fn cargo_cfg_sanitize() -> Option> { + let (_, val) = get_opt_cfg("CARGO_CFG_SANITIZE"); + val + } -/// If we are compiling with debug assertions enabled. -pub fn cargo_cfg_debug_assertions() -> bool { - get_bool("CARGO_CFG_DEBUG_ASSERTIONS") -} + /// If CFI sanitization is generalizing pointers. + #[doc = unstable!(cfg_sanitizer_cfi, 89653)] + #[cfg(feature = "unstable")] + #[track_caller] + pub fn cargo_cfg_sanitizer_cfi_generalize_pointers() -> bool { + get_bool("CARGO_CFG_SANITIZER_CFI_GENERALIZE_POINTERS") + } -/// If we are compiling with overflow checks enabled. -/// -#[doc = unstable!(cfg_overflow_checks, 111466)] -#[cfg(feature = "unstable")] -pub fn cargo_cfg_overflow_checks() -> bool { - get_bool("CARGO_CFG_OVERFLOW_CHECKS") -} + /// If CFI sanitization is normalizing integers. + #[doc = unstable!(cfg_sanitizer_cfi, 89653)] + #[cfg(feature = "unstable")] + #[track_caller] + pub fn cargo_cfg_sanitizer_cfi_normalize_integers() -> bool { + get_bool("CARGO_CFG_SANITIZER_CFI_NORMALIZE_INTEGERS") + } -/// If we are compiling with UB checks enabled. -/// -#[doc = unstable!(cfg_ub_checks, 123499)] -#[cfg(feature = "unstable")] -pub fn cargo_cfg_ub_checks() -> bool { - get_bool("CARGO_CFG_UB_CHECKS") -} + /// Disambiguation of the [target ABI](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_abi) + /// when the [target env](cargo_cfg_target_env) isn't sufficient. + /// + /// For historical reasons, this value is only defined as not the empty-string when + /// actually needed for disambiguation. Thus, for example, on many GNU platforms, + /// this value will be empty. + #[track_caller] + pub fn cargo_cfg_target_abi() -> String { + get_str("CARGO_CFG_TARGET_ABI") + } -/// The target relocation model. -/// -#[doc = unstable!(cfg_relocation_model, 114929)] -#[cfg(feature = "unstable")] -pub fn cargo_cfg_relocation_model() -> String { - get_str("CARGO_CFG_RELOCATION_MODEL") + /// The CPU [target architecture](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_arch). + /// This is similar to the first element of the platform's target triple, but not identical. + #[track_caller] + pub fn cargo_cfg_target_arch() -> String { + get_str("CARGO_CFG_TARGET_ARCH") + } + + /// The CPU [target endianness](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_endian). + #[track_caller] + pub fn cargo_cfg_target_endian() -> String { + get_str("CARGO_CFG_TARGET_ENDIAN") + } + + /// The [target environment](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_env) ABI. + /// This value is similar to the fourth element of the platform's target triple. + /// + /// For historical reasons, this value is only defined as not the empty-string when + /// actually needed for disambiguation. Thus, for example, on many GNU platforms, + /// this value will be empty. + #[track_caller] + pub fn cargo_cfg_target_env() -> String { + get_str("CARGO_CFG_TARGET_ENV") + } + + /// The [target family](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_family). + #[track_caller] + pub fn cargo_target_family() -> Vec { + get_cfg("target_family") + } + + /// List of CPU [target features](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_feature) enabled. + #[track_caller] + pub fn cargo_cfg_target_feature() -> Vec { + get_cfg("target_feature") + } + + /// List of CPU [supported atomic widths](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_has_atomic). + #[track_caller] + pub fn cargo_cfg_target_has_atomic() -> Vec { + get_cfg("target_has_atomic") + } + + /// List of atomic widths that have equal alignment requirements. + #[doc = unstable!(cfg_target_has_atomic_equal_alignment, 93822)] + #[cfg(feature = "unstable")] + #[track_caller] + pub fn cargo_cfg_target_has_atomic_equal_alignment() -> Vec { + get_cfg("target_has_atomic_equal_alignment") + } + + /// List of atomic widths that have atomic load and store operations. + #[doc = unstable!(cfg_target_has_atomic_load_store, 94039)] + #[cfg(feature = "unstable")] + #[track_caller] + pub fn cargo_cfg_target_has_atomic_load_store() -> Vec { + get_cfg("target_has_atomic_load_store") + } + + /// The [target operating system](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_os). + /// This value is similar to the second and third element of the platform's target triple. + #[track_caller] + pub fn cargo_cfg_target_os() -> String { + get_str("CARGO_CFG_TARGET_OS") + } + + /// The CPU [pointer width](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_pointer_width). + #[track_caller] + pub fn cargo_cfg_target_pointer_width() -> u32 { + get_num("CARGO_CFG_TARGET_POINTER_WIDTH") + } + + /// If the target supports thread-local storage. + #[doc = unstable!(cfg_target_thread_local, 29594)] + #[cfg(feature = "unstable")] + #[track_caller] + pub fn cargo_cfg_target_thread_local() -> bool { + get_bool("CARGO_CFG_TARGET_THREAD_LOCAL") + } + + /// The [target vendor](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_vendor). + #[track_caller] + pub fn cargo_cfg_target_vendor() -> String { + get_str("CARGO_CFG_TARGET_VENDOR") + } + + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_test() -> bool { + get_bool("CARGO_CFG_TEST") + } + + /// If we are compiling with UB checks enabled. + #[doc = unstable!(cfg_ub_checks, 123499)] + #[cfg(feature = "unstable")] + #[track_caller] + pub fn cargo_cfg_ub_checks() -> bool { + get_bool("CARGO_CFG_UB_CHECKS") + } + + /// Set on [unix-like platforms](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#unix-and-windows). + #[track_caller] + pub fn cargo_cfg_unix() -> bool { + get_bool("CARGO_CFG_UNIX") + } + + /// Set on [windows-like platforms](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#unix-and-windows). + #[track_caller] + pub fn cargo_cfg_windows() -> bool { + get_bool("CARGO_CFG_WINDOWS") + } } /// The folder in which all output and intermediate artifacts should be placed. /// This folder is inside the build directory for the package being built, and /// it is unique for the package in question. +#[track_caller] pub fn out_dir() -> PathBuf { get_path("OUT_DIR") } @@ -259,11 +385,13 @@ pub fn out_dir() -> PathBuf { /// for this triple. /// /// [target triple]: https://doc.rust-lang.org/stable/cargo/appendix/glossary.html#target +#[track_caller] pub fn target() -> String { get_str("TARGET") } /// The host triple of the Rust compiler. +#[track_caller] pub fn host() -> String { get_str("HOST") } @@ -276,16 +404,19 @@ pub fn host() -> String { /// Cargo’s GNU Make compatible [jobserver] for sub-make invocations. /// /// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html +#[track_caller] pub fn num_jobs() -> u32 { get_num("NUM_JOBS") } /// The [level of optimization](https://doc.rust-lang.org/stable/cargo/reference/profiles.html#opt-level). +#[track_caller] pub fn opt_level() -> String { get_str("OPT_LEVEL") } /// The amount of [debug information](https://doc.rust-lang.org/stable/cargo/reference/profiles.html#debug) included. +#[track_caller] pub fn debug() -> String { get_str("DEBUG") } @@ -298,6 +429,7 @@ pub fn debug() -> String { /// [profile]: https://doc.rust-lang.org/stable/cargo/reference/profiles.html /// [`dev`]: https://doc.rust-lang.org/stable/cargo/reference/profiles.html#dev /// [`release`]: https://doc.rust-lang.org/stable/cargo/reference/profiles.html#release +#[track_caller] pub fn profile() -> String { get_str("PROFILE") } @@ -307,7 +439,15 @@ pub fn profile() -> String { /// /// [metadata]: crate::output::metadata /// [links]: https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#the-links-manifest-key -pub fn dep(name: &str, key: &str) -> Option { +#[track_caller] +pub fn dep_metadata(name: &str, key: &str) -> Option { + if !is_crate_name(name) { + panic!("invalid dependency name {name:?}") + } + if !is_ascii_ident(key) { + panic!("invalid metadata key {key:?}") + } + let name = name.to_uppercase().replace('-', "_"); let key = key.to_uppercase().replace('-', "_"); let key = format!("DEP_{name}_{key}"); @@ -315,11 +455,13 @@ pub fn dep(name: &str, key: &str) -> Option { } /// The compiler that Cargo has resolved to use. +#[track_caller] pub fn rustc() -> PathBuf { get_path("RUSTC") } /// The documentation generator that Cargo has resolved to use. +#[track_caller] pub fn rustdoc() -> PathBuf { get_path("RUSTDOC") } @@ -327,6 +469,7 @@ pub fn rustdoc() -> PathBuf { /// The rustc wrapper, if any, that Cargo is using. See [`build.rustc-wrapper`]. /// /// [`build.rustc-wrapper`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustc-wrapper +#[track_caller] pub fn rustc_wrapper() -> Option { get_opt_path("RUSTC_WRAPPER") } @@ -335,6 +478,7 @@ pub fn rustc_wrapper() -> Option { /// [`build.rustc-workspace-wrapper`]. /// /// [`build.rustc-workspace-wrapper`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustc-workspace-wrapper +#[track_caller] pub fn rustc_workspace_wrapper() -> Option { get_opt_path("RUSTC_WORKSPACE_WRAPPER") } @@ -342,6 +486,7 @@ pub fn rustc_workspace_wrapper() -> Option { /// The linker that Cargo has resolved to use for the current target, if specified. /// /// [`target.*.linker`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#targettriplelinker +#[track_caller] pub fn rustc_linker() -> Option { get_opt_path("RUSTC_LINKER") } @@ -349,6 +494,7 @@ pub fn rustc_linker() -> Option { /// Extra flags that Cargo invokes rustc with. See [`build.rustflags`]. /// /// [`build.rustflags`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustflags +#[track_caller] pub fn cargo_encoded_rustflags() -> Vec { get_str("CARGO_ENCODED_RUSTFLAGS") .split('\x1f') @@ -357,31 +503,37 @@ pub fn cargo_encoded_rustflags() -> Vec { } /// The full version of your package. +#[track_caller] pub fn cargo_pkg_version() -> String { get_str("CARGO_PKG_VERSION") } /// The major version of your package. +#[track_caller] pub fn cargo_pkg_version_major() -> u64 { get_num("CARGO_PKG_VERSION_MAJOR") } /// The minor version of your package. +#[track_caller] pub fn cargo_pkg_version_minor() -> u64 { get_num("CARGO_PKG_VERSION_MINOR") } /// The patch version of your package. +#[track_caller] pub fn cargo_pkg_version_patch() -> u64 { get_num("CARGO_PKG_VERSION_PATCH") } /// The pre-release version of your package. +#[track_caller] pub fn cargo_pkg_version_pre() -> String { get_str("CARGO_PKG_VERSION_PRE") } /// Colon separated list of authors from the manifest of your package. +#[track_caller] pub fn cargo_pkg_authors() -> Vec { get_str("CARGO_PKG_AUTHORS") .split(':') @@ -390,17 +542,50 @@ pub fn cargo_pkg_authors() -> Vec { } /// The name of your package. +#[track_caller] pub fn cargo_pkg_name() -> String { get_str("CARGO_PKG_NAME") } /// The description from the manifest of your package. +#[track_caller] pub fn cargo_pkg_description() -> String { get_str("CARGO_PKG_DESCRIPTION") } +/// The home page from the manifest of your package. +#[track_caller] +pub fn cargo_pkg_homepage() -> String { + get_str("CARGO_PKG_HOMEPAGE") +} + +/// The repository from the manifest of your package. +#[track_caller] +pub fn cargo_pkg_repository() -> String { + get_str("CARGO_PKG_REPOSITORY") +} + +/// The license from the manifest of your package. +#[track_caller] +pub fn cargo_pkg_license() -> String { + get_str("CARGO_PKG_LICENSE") +} + +/// The license file from the manifest of your package. +#[track_caller] +pub fn cargo_pkg_license_file() -> PathBuf { + get_path("CARGO_PKG_LICENSE_FILE") +} + /// The Rust version from the manifest of your package. Note that this is the /// minimum Rust version supported by the package, not the current Rust version. +#[track_caller] pub fn cargo_pkg_rust_version() -> String { get_str("CARGO_PKG_RUST_VERSION") } + +/// Path to the README file of your package. +#[track_caller] +pub fn cargo_pkg_readme() -> PathBuf { + get_path("CARGO_PKG_README") +} diff --git a/src/lib.rs b/src/lib.rs index 0637fe2565a..ccc22dd07a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,14 @@ macro_rules! unstable { }; } +macro_rules! msrv { + ($ver:literal) => { + concat!("> MSRV: Respected as of ", $ver, ".") + }; +} + mod allow_use; +mod ident; + pub mod input; pub mod output; diff --git a/src/output.rs b/src/output.rs index f02734e8271..318185a040e 100644 --- a/src/output.rs +++ b/src/output.rs @@ -6,8 +6,11 @@ //! //! Reference: -use crate::allow_use; -use std::{ffi::OsStr, fmt::Display, path::Path, str}; +use crate::{ + allow_use, + ident::{is_ascii_ident, is_ident}, +}; +use std::{ffi::OsStr, fmt::Display, fmt::Write, path::Path, str}; fn emit(directive: &str, value: impl Display) { if allow_use::double_colon_directives() { @@ -32,6 +35,7 @@ fn emit(directive: &str, value: impl Display) { /// handles whether or not the script itself needs to be recompiled, and of course /// the script will be re-run after it has been recompiled. Otherwise, specifying /// `build.rs` is redundant and unnecessary. +#[track_caller] pub fn rerun_if_changed(path: impl AsRef) { let Some(path) = path.as_ref().to_str() else { panic!("cannot emit rerun-if-changed: path is not UTF-8"); @@ -52,6 +56,7 @@ pub fn rerun_if_changed(path: impl AsRef) { /// those received by the executable of the build script. /// /// [build-env]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts +#[track_caller] pub fn rerun_if_env_changed(key: impl AsRef) { let Some(key) = key.as_ref().to_str() else { panic!("cannot emit rerun-if-env-changed: key is not UTF-8"); @@ -69,9 +74,10 @@ pub fn rerun_if_env_changed(key: impl AsRef) { /// version or linker script. /// /// [link-arg]: https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg +#[track_caller] pub fn rustc_link_arg(flag: &str) { - if flag.contains('\n') { - panic!("cannot emit rustc-link-arg: invalid flag"); + if flag.contains([' ', '\n']) { + panic!("cannot emit rustc-link-arg: invalid flag {flag:?}"); } emit("rustc-link-arg", flag); } @@ -82,9 +88,13 @@ pub fn rustc_link_arg(flag: &str) { /// is useful to set a linker script or other linker options. /// /// [link-arg]: https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg +#[track_caller] pub fn rustc_link_arg_bin(bin: &str, flag: &str) { - if bin.contains(['=', '\n']) { - panic!("cannot emit rustc-link-arg-bin: invalid bin name"); + if !is_ident(bin) { + panic!("cannot emit rustc-link-arg-bin: invalid bin name {bin:?}"); + } + if flag.contains([' ', '\n']) { + panic!("cannot emit rustc-link-arg-bin: invalid flag {flag:?}"); } emit("rustc-link-arg-bin", format_args!("{}={}", bin, flag)); } @@ -95,9 +105,10 @@ pub fn rustc_link_arg_bin(bin: &str, flag: &str) { /// a linker script or other linker options. /// /// [link-arg]: https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg +#[track_caller] pub fn rustc_link_arg_bins(flag: &str) { - if flag.contains('\n') { - panic!("cannot emit rustc-link-arg-bins: invalid flag"); + if flag.contains([' ', '\n']) { + panic!("cannot emit rustc-link-arg-bins: invalid flag {flag:?}"); } emit("rustc-link-arg-bins", flag); } @@ -105,9 +116,10 @@ pub fn rustc_link_arg_bins(flag: &str) { /// The `rustc-link-arg-tests` instruction tells Cargo to pass the /// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building /// a tests target. +#[track_caller] pub fn rustc_link_arg_tests(flag: &str) { - if flag.contains('\n') { - panic!("cannot emit rustc-link-arg-tests: invalid flag"); + if flag.contains([' ', '\n']) { + panic!("cannot emit rustc-link-arg-tests: invalid flag {flag:?}"); } emit("rustc-link-arg-tests", flag); } @@ -115,9 +127,10 @@ pub fn rustc_link_arg_tests(flag: &str) { /// The `rustc-link-arg-examples` instruction tells Cargo to pass the /// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building /// an examples target. +#[track_caller] pub fn rustc_link_arg_examples(flag: &str) { - if flag.contains('\n') { - panic!("cannot emit rustc-link-arg-examples: invalid flag"); + if flag.contains([' ', '\n']) { + panic!("cannot emit rustc-link-arg-examples: invalid flag {flag:?}"); } emit("rustc-link-arg-examples", flag); } @@ -125,9 +138,10 @@ pub fn rustc_link_arg_examples(flag: &str) { /// The `rustc-link-arg-benches` instruction tells Cargo to pass the /// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building /// a benchmark target. +#[track_caller] pub fn rustc_link_arg_benches(flag: &str) { - if flag.contains('\n') { - panic!("cannot emit rustc-link-arg-benches: invalid flag"); + if flag.contains([' ', '\n']) { + panic!("cannot emit rustc-link-arg-benches: invalid flag {flag:?}"); } emit("rustc-link-arg-benches", flag); } @@ -153,20 +167,22 @@ pub fn rustc_link_arg_benches(flag: &str) { /// /// [-l]: https://doc.rust-lang.org/stable/rustc/command-line-arguments.html#option-l-link-lib /// [FFI]: https://doc.rust-lang.org/stable/nomicon/ffi.html +#[track_caller] pub fn rustc_link_lib(lib: &str) { - if lib.contains('\n') { - panic!("cannot emit rustc-link-lib: invalid lib"); + if lib.contains([' ', '\n']) { + panic!("cannot emit rustc-link-lib: invalid lib {lib:?}"); } emit("rustc-link-lib", lib); } -/// Like [`rustc_link_lib`], but with KIND specified separately. +/// Like [`rustc_link_lib`], but with `KIND[:MODIFIERS]` specified separately. +#[track_caller] pub fn rustc_link_lib_kind(kind: &str, lib: &str) { - if kind.contains(['=', '\n']) { - panic!("cannot emit rustc-link-lib: invalid kind"); + if kind.contains(['=', ' ', '\n']) { + panic!("cannot emit rustc-link-lib: invalid kind {kind:?}"); } - if lib.contains('\n') { - panic!("cannot emit rustc-link-lib: invalid lib"); + if lib.contains([' ', '\n']) { + panic!("cannot emit rustc-link-lib: invalid lib {lib:?}"); } emit("rustc-link-lib", format_args!("{kind}={lib}")); } @@ -186,6 +202,7 @@ pub fn rustc_link_lib_kind(kind: &str, lib: &str) { /// /// [-L]: https://doc.rust-lang.org/stable/rustc/command-line-arguments.html#option-l-search-path /// [search-path]: https://doc.rust-lang.org/stable/cargo/reference/environment-variables.html#dynamic-library-paths +#[track_caller] pub fn rustc_link_search(path: impl AsRef) { let Some(path) = path.as_ref().to_str() else { panic!("cannot emit rustc-link-search: path is not UTF-8"); @@ -197,9 +214,10 @@ pub fn rustc_link_search(path: impl AsRef) { } /// Like [`rustc_link_search`], but with KIND specified separately. +#[track_caller] pub fn rustc_link_search_kind(kind: &str, path: impl AsRef) { if kind.contains(['=', '\n']) { - panic!("cannot emit rustc-link-search: invalid kind"); + panic!("cannot emit rustc-link-search: invalid kind {kind:?}"); } let Some(path) = path.as_ref().to_str() else { panic!("cannot emit rustc-link-search: path is not UTF-8"); @@ -213,6 +231,7 @@ pub fn rustc_link_search_kind(kind: &str, path: impl AsRef) { /// The `rustc-flags` instruction tells Cargo to pass the given space-separated /// flags to the compiler. This only allows the `-l` and `-L` flags, and is /// equivalent to using [`rustc_link_lib`] and [`rustc_link_search`]. +#[track_caller] pub fn rustc_flags(flags: &str) { if flags.contains('\n') { panic!("cannot emit rustc-flags: invalid flags"); @@ -221,7 +240,7 @@ pub fn rustc_flags(flags: &str) { } /// The `rustc-cfg` instruction tells Cargo to pass the given value to the -/// [`--cfg` flag][--cfg] to the compiler. This may be used for compile-time +/// [`--cfg` flag][cfg] to the compiler. This may be used for compile-time /// detection of features to enable conditional compilation. /// /// Note that this does not affect Cargo’s dependency resolution. This cannot @@ -235,26 +254,95 @@ pub fn rustc_flags(flags: &str) { /// like `rustc_cfg(r#"my_component="foo""#)`. The key should be a Rust identifier, /// the value should be a string. /// -/// [--cfg]: https://doc.rust-lang.org/rustc/command-line-arguments.html#option-cfg +/// [cfg]: https://doc.rust-lang.org/rustc/command-line-arguments.html#option-cfg /// [Cargo features]: https://doc.rust-lang.org/cargo/reference/features.html +#[track_caller] pub fn rustc_cfg(key: &str) { - if key.contains('\n') { - panic!("cannot emit rustc-cfg: invalid key"); + if !is_ident(key) { + panic!("cannot emit rustc-cfg: invalid key {key:?}"); } emit("rustc-cfg", key); } -/// Like [`rustc_cfg`], but with the value specified separately. +/// Like [`rustc_cfg`], but with the value specified separately. To replace the +/// less convenient `rustc_cfg(r#"my_component="foo""#)`, you can instead use +/// `rustc_cfg_value("my_component", "foo")`. +#[track_caller] pub fn rustc_cfg_value(key: &str, value: &str) { - let value = value.escape_default(); - if key.contains(['=', '\n']) { + if !is_ident(key) { panic!("cannot emit rustc-cfg-value: invalid key"); } + let value = value.escape_default(); emit("rustc-cfg", format_args!("{key}=\"{value}\"")); } +/// Add to the list of expected config names that is used when checking the +/// *reachable* cfg expressions with the [`unexpected_cfgs`] lint. +/// +/// This form is for keys without an expected value, such as `cfg(name)`. +/// +/// It is recommended to group the `rustc_check_cfg` and `rustc_cfg` calls as +/// closely as possible in order to avoid typos, missing check_cfg, stale cfgs, +/// and other mistakes. +/// +/// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs +#[doc = msrv!("1.80")] +#[track_caller] +pub fn rustc_check_cfgs(keys: &[&str]) { + if keys.is_empty() { + return; + } + for key in keys { + if !is_ident(key) { + panic!("cannot emit rustc-check-cfg: invalid key {key:?}"); + } + } + + if allow_use::check_cfg() { + let mut directive = keys[0].to_string(); + for key in &keys[1..] { + write!(directive, ", {key}").expect("writing to string should be infallible"); + } + emit("rustc-check-cfg", format_args!("cfg({directive})")); + } +} + +/// Add to the list of expected config names that is used when checking the +/// *reachable* cfg expressions with the [`unexpected_cfgs`] lint. +/// +/// This form is for keys with expected values, such as `cfg(name = "value")`. +/// +/// It is recommended to group the `rustc_check_cfg` and `rustc_cfg` calls as +/// closely as possible in order to avoid typos, missing check_cfg, stale cfgs, +/// and other mistakes. +/// +/// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs +#[doc = msrv!("1.80")] +#[track_caller] +pub fn rustc_check_cfg_values(key: &str, values: &[&str]) { + if !is_ident(key) { + panic!("cannot emit rustc-check-cfg: invalid key {key:?}"); + } + if values.is_empty() { + rustc_check_cfgs(&[key]); + return; + } + + if allow_use::check_cfg() { + let mut directive = format!("\"{}\"", values[0].escape_default()); + for value in &values[1..] { + write!(directive, ", \"{}\"", value.escape_default()) + .expect("writing to string should be infallible"); + } + emit( + "rustc-check-cfg", + format_args!("cfg({key}, values({directive}))"), + ); + } +} + /// The `rustc-env` instruction tells Cargo to set the given environment variable -/// when compiling the package. The value can be then retrieved by the +/// when compiling the package. The value can be then retrieved by the /// [`env!` macro][env!] in the compiled crate. This is useful for embedding /// additional metadata in crate’s code, such as the hash of git HEAD or the /// unique identifier of a continuous integration server. @@ -262,12 +350,13 @@ pub fn rustc_cfg_value(key: &str, value: &str) { /// See also the [environment variables automatically included by Cargo][cargo-env]. /// /// [cargo-env]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates +#[track_caller] pub fn rustc_env(key: &str, value: &str) { if key.contains(['=', '\n']) { - panic!("cannot emit rustc-env: invalid key"); + panic!("cannot emit rustc-env: invalid key {key:?}"); } if value.contains('\n') { - panic!("cannot emit rustc-env: invalid value"); + panic!("cannot emit rustc-env: invalid value {value:?}"); } emit("rustc-env", format_args!("{key}={value}")); } @@ -278,9 +367,10 @@ pub fn rustc_env(key: &str, value: &str) { /// to set the shared library version or the runtime-path. /// /// [link-arg]: https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg +#[track_caller] pub fn rustc_cdylib_link_arg(flag: &str) { if flag.contains('\n') { - panic!("cannot emit rustc-cdylib-link-arg: invalid flag"); + panic!("cannot emit rustc-cdylib-link-arg: invalid flag {flag:?}"); } emit("rustc-cdylib-link-arg", flag); } @@ -292,6 +382,7 @@ pub fn rustc_cdylib_link_arg(flag: &str) { /// flag may be used to have Cargo display warnings for all crates. /// /// [crates.io]: https://crates.io/ +#[track_caller] pub fn warning(message: &str) { if message.contains('\n') { panic!("cannot emit warning: message contains newline"); @@ -300,13 +391,15 @@ pub fn warning(message: &str) { } /// Metadata, used by `links` scripts. +#[track_caller] pub fn metadata(key: &str, val: &str) { - if key.contains(['=', '\n']) { - panic!("cannot emit metadata: invalid key"); + if !is_ascii_ident(key) { + panic!("cannot emit metadata: invalid key {key:?}"); } if val.contains('\n') { - panic!("cannot emit metadata: invalid value"); + panic!("cannot emit metadata: invalid value {val:?}"); } + if allow_use::double_colon_directives() { emit("metadata", format_args!("{}={}", key, val)); } else { diff --git a/test-lib/Cargo.toml b/test-lib/Cargo.toml index e354bb96416..551c482bcd9 100644 --- a/test-lib/Cargo.toml +++ b/test-lib/Cargo.toml @@ -5,5 +5,8 @@ edition = "2021" rust-version = "1.76" publish = false +[features] +unstable = ["build-rs/unstable"] + [build-dependencies] build-rs = { path = ".." } diff --git a/test-lib/build.rs b/test-lib/build.rs index b59bb77643b..a6065ef0385 100644 --- a/test-lib/build.rs +++ b/test-lib/build.rs @@ -1,4 +1,78 @@ fn main() { + smoke_test_inputs(); + build_rs::output::rerun_if_changed("build.rs"); + build_rs::output::rustc_check_cfgs(&["did_run_build_script"]); build_rs::output::rustc_cfg("did_run_build_script"); } + +fn smoke_test_inputs() { + use build_rs::input::*; + dbg!(cargo()); + dbg!(cargo_cfg("careful")); + dbg!(cargo_cfg_debug_assertions()); + #[cfg(feature = "unstable")] + dbg!(cargo_cfg_fmt_debug()); + #[cfg(feature = "unstable")] + dbg!(cargo_cfg_overflow_checks()); + dbg!(cargo_cfg_panic()); + dbg!(cargo_cfg_proc_macro()); + #[cfg(feature = "unstable")] + dbg!(cargo_cfg_relocation_model()); + #[cfg(feature = "unstable")] + dbg!(cargo_cfg_sanitize()); + #[cfg(feature = "unstable")] + dbg!(cargo_cfg_sanitizer_cfi_generalize_pointers()); + #[cfg(feature = "unstable")] + dbg!(cargo_cfg_sanitizer_cfi_normalize_integers()); + dbg!(cargo_cfg_target_abi()); + dbg!(cargo_cfg_target_arch()); + dbg!(cargo_cfg_target_endian()); + dbg!(cargo_cfg_target_env()); + dbg!(cargo_cfg_target_feature()); + dbg!(cargo_cfg_target_has_atomic()); + #[cfg(feature = "unstable")] + dbg!(cargo_cfg_target_has_atomic_equal_alignment()); + #[cfg(feature = "unstable")] + dbg!(cargo_cfg_target_has_atomic_load_store()); + dbg!(cargo_cfg_target_os()); + dbg!(cargo_cfg_target_pointer_width()); + #[cfg(feature = "unstable")] + dbg!(cargo_cfg_target_thread_local()); + dbg!(cargo_cfg_target_vendor()); + #[cfg(feature = "unstable")] + dbg!(cargo_cfg_ub_checks()); + dbg!(cargo_cfg_unix()); + dbg!(cargo_cfg_windows()); + dbg!(cargo_encoded_rustflags()); + dbg!(cargo_feature("unstable")); + dbg!(cargo_manifest_dir()); + dbg!(cargo_manifest_links()); + dbg!(cargo_pkg_authors()); + dbg!(cargo_pkg_description()); + dbg!(cargo_pkg_homepage()); + dbg!(cargo_pkg_license()); + dbg!(cargo_pkg_license_file()); + dbg!(cargo_pkg_name()); + dbg!(cargo_pkg_readme()); + dbg!(cargo_pkg_repository()); + dbg!(cargo_pkg_rust_version()); + dbg!(cargo_pkg_version()); + dbg!(cargo_pkg_version_major()); + dbg!(cargo_pkg_version_minor()); + dbg!(cargo_pkg_version_patch()); + dbg!(cargo_pkg_version_pre()); + dbg!(debug()); + dbg!(dep_metadata("z", "include")); + dbg!(host()); + dbg!(num_jobs()); + dbg!(opt_level()); + dbg!(out_dir()); + dbg!(profile()); + dbg!(rustc()); + dbg!(rustc_linker()); + dbg!(rustc_workspace_wrapper()); + dbg!(rustc_wrapper()); + dbg!(rustdoc()); + dbg!(target()); +} From b62c59890a406eb604d34a1f3c6bcca31689900d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Nov 2024 08:36:32 -0600 Subject: [PATCH 017/525] chore: Remove build-rs CI --- .github/renovate.json5 | 37 --------------------- .github/workflows/ci.yaml | 59 ---------------------------------- .github/workflows/publish.yaml | 19 ----------- 3 files changed, 115 deletions(-) delete mode 100644 .github/renovate.json5 delete mode 100644 .github/workflows/ci.yaml delete mode 100644 .github/workflows/publish.yaml diff --git a/.github/renovate.json5 b/.github/renovate.json5 deleted file mode 100644 index c719c650629..00000000000 --- a/.github/renovate.json5 +++ /dev/null @@ -1,37 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:recommended", - "schedule:weekly", - ":disableDependencyDashboard" - ], - "platformCommit": "enabled", - "customManagers": [ - { - "customType": "regex", - "description": "Update the minimal supported Rust version in Cargo.toml", - "fileMatch": [ - "(^|/)Cargo\\.toml$" - ], - "matchStrings": [ - "(^|\\[package\\])(.|\n)*?($|\n\\s*\\[)", // select package table - "rust-version = \"(?\\d+\\.\\d+(\\.\\d+)?)\"" // select rust-version - ], - "matchStringsStrategy": "recursive", - "depNameTemplate": "rust-version", - "packageNameTemplate": "rust-lang/rust", - "datasourceTemplate": "github-releases", - "versioningTemplate": "semver-coerced", - "extractVersionTemplate": "^(?\\d+\\.\\d+)" // drop patch version - } - ], - "packageRules": [ - { - "automerge": true, - "minimumReleaseAge": "27 weeks", // 6 months + a little slack - "matchDepNames": [ - "rust-version" - ] - } - ] -} \ No newline at end of file diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml deleted file mode 100644 index 3278326ec77..00000000000 --- a/.github/workflows/ci.yaml +++ /dev/null @@ -1,59 +0,0 @@ -name: CI -on: - pull_request: - merge_group: - push: - branches: - - main - -jobs: - tests: - name: Tests - runs-on: ubuntu-latest - strategy: - matrix: - toolchain: ["stable", "stable 6 months ago"] - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - with: - toolchain: ${{ matrix.toolchain }} - - name: Build tests - run: cargo test --workspace --no-run - - name: Run tests - run: cargo test --workspace - - style: - name: Style - runs-on: ubuntu-latest - env: - RUSTFLAGS: -Dwarnings - RUSTDOCFLAGS: -Dwarnings - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - with: - components: rustfmt, clippy - - name: Install cargo-docs-rs - uses: dtolnay/install@cargo-docs-rs - - name: Check docs - run: cargo docs-rs - - name: Check lints - run: cargo clippy - - name: Check fmt - run: cargo fmt -- --check - - semver: - name: API - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Semver checks - uses: obi1kenobi/cargo-semver-checks-action@v2 - with: - package: build-rs diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml deleted file mode 100644 index 4535d40afa4..00000000000 --- a/.github/workflows/publish.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: Publish -on: - push: - tags: - - 'v*' - -jobs: - publish: - name: Publish - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@stable - - name: Publish - run: cargo publish - env: - CARGO_REGISTRY_TOKEN: ${{secrets.CARGO_REGISTRY_TOKEN}} From 0c7577d48ae27744b7b8b65dc6af0da37be38607 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Nov 2024 08:39:59 -0600 Subject: [PATCH 018/525] chore: Stage build-rs for merging --- Cargo.toml | 18 ++---------------- .../build-rs-test-lib}/Cargo.toml | 2 +- .../build-rs-test-lib}/build.rs | 0 .../build-rs-test-lib}/src/lib.rs | 0 crates/build-rs/Cargo.toml | 14 ++++++++++++++ {src => crates/build-rs/src}/allow_use.rs | 0 {src => crates/build-rs/src}/ident.rs | 0 {src => crates/build-rs/src}/input.rs | 0 {src => crates/build-rs/src}/lib.rs | 0 {src => crates/build-rs/src}/output.rs | 0 10 files changed, 17 insertions(+), 17 deletions(-) rename {test-lib => crates/build-rs-test-lib}/Cargo.toml (83%) rename {test-lib => crates/build-rs-test-lib}/build.rs (100%) rename {test-lib => crates/build-rs-test-lib}/src/lib.rs (100%) create mode 100644 crates/build-rs/Cargo.toml rename {src => crates/build-rs/src}/allow_use.rs (100%) rename {src => crates/build-rs/src}/ident.rs (100%) rename {src => crates/build-rs/src}/input.rs (100%) rename {src => crates/build-rs/src}/lib.rs (100%) rename {src => crates/build-rs/src}/output.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index e81f8345519..176a6d372f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,3 @@ -[package] -name = "build-rs" -version = "0.2.0" -edition = "2021" -rust-version = "1.76" -publish = false - -[dependencies] -unicode-ident = "1.0.13" - -[features] - -## Experimental API. This feature flag is **NOT** semver stable. -unstable = [] - [workspace] -members = ["test-lib"] +members = ["crates/*"] +reslver = "2" diff --git a/test-lib/Cargo.toml b/crates/build-rs-test-lib/Cargo.toml similarity index 83% rename from test-lib/Cargo.toml rename to crates/build-rs-test-lib/Cargo.toml index 551c482bcd9..2edc9c4a8a0 100644 --- a/test-lib/Cargo.toml +++ b/crates/build-rs-test-lib/Cargo.toml @@ -9,4 +9,4 @@ publish = false unstable = ["build-rs/unstable"] [build-dependencies] -build-rs = { path = ".." } +build-rs = { path = "../build-rs" } diff --git a/test-lib/build.rs b/crates/build-rs-test-lib/build.rs similarity index 100% rename from test-lib/build.rs rename to crates/build-rs-test-lib/build.rs diff --git a/test-lib/src/lib.rs b/crates/build-rs-test-lib/src/lib.rs similarity index 100% rename from test-lib/src/lib.rs rename to crates/build-rs-test-lib/src/lib.rs diff --git a/crates/build-rs/Cargo.toml b/crates/build-rs/Cargo.toml new file mode 100644 index 00000000000..e012e1f6a17 --- /dev/null +++ b/crates/build-rs/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "build-rs" +version = "0.2.0" +edition = "2021" +rust-version = "1.76" +publish = false + +[dependencies] +unicode-ident = "1.0.13" + +[features] + +## Experimental API. This feature flag is **NOT** semver stable. +unstable = [] diff --git a/src/allow_use.rs b/crates/build-rs/src/allow_use.rs similarity index 100% rename from src/allow_use.rs rename to crates/build-rs/src/allow_use.rs diff --git a/src/ident.rs b/crates/build-rs/src/ident.rs similarity index 100% rename from src/ident.rs rename to crates/build-rs/src/ident.rs diff --git a/src/input.rs b/crates/build-rs/src/input.rs similarity index 100% rename from src/input.rs rename to crates/build-rs/src/input.rs diff --git a/src/lib.rs b/crates/build-rs/src/lib.rs similarity index 100% rename from src/lib.rs rename to crates/build-rs/src/lib.rs diff --git a/src/output.rs b/crates/build-rs/src/output.rs similarity index 100% rename from src/output.rs rename to crates/build-rs/src/output.rs From 54660c665e837f78af21ed4528216698c4ed2c17 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Nov 2024 08:50:45 -0600 Subject: [PATCH 019/525] chore: Integrate build-rs into the workspace --- Cargo.lock | 2 +- Cargo.toml | 2 ++ crates/build-rs-test-lib/Cargo.toml | 7 +++---- crates/build-rs/Cargo.toml | 16 +++++++++------- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9bfed3ac55f..7d2abf48a8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -261,7 +261,7 @@ dependencies = [ [[package]] name = "build-rs-test-lib" -version = "0.2.0" +version = "0.0.0" dependencies = [ "build-rs", ] diff --git a/Cargo.toml b/Cargo.toml index 3245a05ca0b..0cebe268f12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ anstyle = "1.0.8" anyhow = "1.0.86" base64 = "0.22.1" blake3 = "1.5.2" +build-rs = { version = "0.2.0", path = "crates/build-rs" } bytesize = "1.3" cargo = { path = "" } cargo-credential = { version = "0.4.2", path = "credential/cargo-credential" } @@ -107,6 +108,7 @@ tracing = { version = "0.1.40", default-features = false, features = ["std"] } # tracing-chrome = "0.7.2" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } unicase = "2.7.0" +unicode-ident = "1.0.13" unicode-width = "0.2.0" unicode-xid = "0.2.4" url = "2.5.2" diff --git a/crates/build-rs-test-lib/Cargo.toml b/crates/build-rs-test-lib/Cargo.toml index 2edc9c4a8a0..f93b4e69428 100644 --- a/crates/build-rs-test-lib/Cargo.toml +++ b/crates/build-rs-test-lib/Cargo.toml @@ -1,12 +1,11 @@ [package] name = "build-rs-test-lib" -version = "0.2.0" -edition = "2021" -rust-version = "1.76" +version = "0.0.0" +edition.workspace = true publish = false [features] unstable = ["build-rs/unstable"] [build-dependencies] -build-rs = { path = "../build-rs" } +build-rs.workspace = true diff --git a/crates/build-rs/Cargo.toml b/crates/build-rs/Cargo.toml index e012e1f6a17..e2e8eb9fccf 100644 --- a/crates/build-rs/Cargo.toml +++ b/crates/build-rs/Cargo.toml @@ -1,14 +1,16 @@ [package] name = "build-rs" version = "0.2.0" -edition = "2021" -rust-version = "1.76" -publish = false - -[dependencies] -unicode-ident = "1.0.13" +rust-version.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +description = "API for writing Cargo `build.rs` files" [features] - ## Experimental API. This feature flag is **NOT** semver stable. unstable = [] + +[dependencies] +unicode-ident.workspace = true From 82a912a2ee93f9e087b6a3b4c2933a3ca3e6dd73 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 5 Nov 2024 13:49:49 -0600 Subject: [PATCH 020/525] chore: Silence clippy for intregration --- crates/build-rs/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/build-rs/src/lib.rs b/crates/build-rs/src/lib.rs index ccc22dd07a1..4f74da99356 100644 --- a/crates/build-rs/src/lib.rs +++ b/crates/build-rs/src/lib.rs @@ -2,6 +2,8 @@ //! protocol. Cargo provides inputs to the build script by environment variable //! and accepts commands by printing to stdout. #![cfg_attr(all(doc, feature = "unstable"), feature(doc_auto_cfg, doc_cfg))] +#![allow(clippy::disallowed_methods)] // HACK: deferred resoling this +#![allow(clippy::print_stdout)] // HACK: deferred resoling this #[cfg(feature = "unstable")] macro_rules! unstable { From 80c117a99b81bbcbe4e2c89558c21cb67b161648 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 5 Nov 2024 13:52:25 -0600 Subject: [PATCH 021/525] chore(ci): Don't check build-rs against past commits --- crates/xtask-bump-check/src/xtask.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/xtask-bump-check/src/xtask.rs b/crates/xtask-bump-check/src/xtask.rs index 1b131383dad..60dbc753856 100644 --- a/crates/xtask-bump-check/src/xtask.rs +++ b/crates/xtask-bump-check/src/xtask.rs @@ -184,6 +184,7 @@ fn bump_check(args: &clap::ArgMatches, gctx: &cargo::util::GlobalContext) -> Car let mut cmd = ProcessBuilder::new("cargo"); cmd.arg("semver-checks") .arg("--workspace") + .args(&["--exclude", "build-rs"]) // FIXME: Remove once 1.84 is stable. .arg("--baseline-rev") .arg(referenced_commit.id().to_string()); for krate in crates_not_check_against_channels { From 9814045a034dd361bf590fbcaba7b54b44c8ff14 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 6 Nov 2024 20:40:37 -0600 Subject: [PATCH 022/525] docs: Declare build-rs as intentional --- crates/build-rs/README.md | 2 ++ crates/build-rs/src/lib.rs | 3 +++ src/doc/contrib/src/team.md | 1 + 3 files changed, 6 insertions(+) create mode 100644 crates/build-rs/README.md diff --git a/crates/build-rs/README.md b/crates/build-rs/README.md new file mode 100644 index 00000000000..844bc081f70 --- /dev/null +++ b/crates/build-rs/README.md @@ -0,0 +1,2 @@ +> This crate is maintained by the Cargo team for use by the wider +> ecosystem. This crate follows semver compatibility for its APIs. diff --git a/crates/build-rs/src/lib.rs b/crates/build-rs/src/lib.rs index 4f74da99356..56a0df3e820 100644 --- a/crates/build-rs/src/lib.rs +++ b/crates/build-rs/src/lib.rs @@ -1,6 +1,9 @@ //! build-rs provides a strongly typed interface around the Cargo build script //! protocol. Cargo provides inputs to the build script by environment variable //! and accepts commands by printing to stdout. +//! +//! > This crate is maintained by the Cargo team for use by the wider +//! > ecosystem. This crate follows semver compatibility for its APIs. #![cfg_attr(all(doc, feature = "unstable"), feature(doc_auto_cfg, doc_cfg))] #![allow(clippy::disallowed_methods)] // HACK: deferred resoling this #![allow(clippy::print_stdout)] // HACK: deferred resoling this diff --git a/src/doc/contrib/src/team.md b/src/doc/contrib/src/team.md index 677138560f0..ae4cd453e09 100644 --- a/src/doc/contrib/src/team.md +++ b/src/doc/contrib/src/team.md @@ -170,6 +170,7 @@ The degree of process is correlated with the degree of change being proposed: Per the [Rust crate ownership policy](https://forge.rust-lang.org/policies/crate-ownership.html), the Cargo team's "Intentional Artifacts" include: +- [build-rs](https://crates.io/crates/build-rs) - [cargo-credential](https://crates.io/crates/cargo-credential) - [cargo-platform](https://crates.io/crates/cargo-platform) - [cargo-util-schemas](https://crates.io/crates/cargo-util-schemas) From 13255d0fc1079d3d98b1763c399e7d8956cd3821 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 8 Nov 2024 10:21:29 -0600 Subject: [PATCH 023/525] fix(schemas): Support deserializing str's as well as Strings Not needed for `toml` but could be useful for other formats. --- crates/cargo-util-schemas/src/manifest/mod.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/cargo-util-schemas/src/manifest/mod.rs b/crates/cargo-util-schemas/src/manifest/mod.rs index 8442e62cc52..0db29ad492b 100644 --- a/crates/cargo-util-schemas/src/manifest/mod.rs +++ b/crates/cargo-util-schemas/src/manifest/mod.rs @@ -418,6 +418,13 @@ impl<'de> de::Deserialize<'de> for InheritableString { Ok(InheritableString::Value(value)) } + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + self.visit_string(value.to_owned()) + } + fn visit_map(self, map: V) -> Result where V: de::MapAccess<'de>, @@ -454,6 +461,13 @@ impl<'de> de::Deserialize<'de> for InheritableRustVersion { Ok(InheritableRustVersion::Value(value)) } + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + self.visit_string(value.to_owned()) + } + fn visit_map(self, map: V) -> Result where V: de::MapAccess<'de>, @@ -533,6 +547,13 @@ impl<'de> de::Deserialize<'de> for InheritableStringOrBool { StringOrBool::deserialize(string).map(InheritableField::Value) } + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + self.visit_string(value.to_owned()) + } + fn visit_map(self, map: V) -> Result where V: de::MapAccess<'de>, From 650fb9d8f64d57d73417206d58b8208fb4af1b04 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 8 Nov 2024 10:19:04 -0600 Subject: [PATCH 024/525] style: Clarify code intention --- src/cargo/core/resolver/dep_cache.rs | 1 - src/cargo/util/toml/mod.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cargo/core/resolver/dep_cache.rs b/src/cargo/core/resolver/dep_cache.rs index e0964fcc07d..7d584d2c8bd 100644 --- a/src/cargo/core/resolver/dep_cache.rs +++ b/src/cargo/core/resolver/dep_cache.rs @@ -207,7 +207,6 @@ impl<'a> RegistryQueryer<'a> { } } - let first_version = first_version; self.version_prefs.sort_summaries(&mut ret, first_version); let out = Poll::Ready(Rc::new(ret)); diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index edbfe996292..be8e7512776 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -507,7 +507,7 @@ fn normalize_toml( normalized_toml.badges = original_toml.badges.clone(); } else { - for field in original_toml.requires_package() { + if let Some(field) = original_toml.requires_package().next() { bail!("this virtual manifest specifies a `{field}` section, which is not allowed"); } } From 49d869682369909fc80aeed14bff040540c97b2a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 8 Nov 2024 13:58:45 -0600 Subject: [PATCH 025/525] style: Workaround clippy --- crates/rustfix/src/replace.rs | 1 + src/cargo/util/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/rustfix/src/replace.rs b/crates/rustfix/src/replace.rs index 3eb130203d0..f1a2e7eca2c 100644 --- a/crates/rustfix/src/replace.rs +++ b/crates/rustfix/src/replace.rs @@ -255,6 +255,7 @@ mod tests { } #[test] + #[allow(clippy::reversed_empty_ranges)] fn replace_invalid_range() { let mut d = Data::new(b"foo!"); diff --git a/src/cargo/util/mod.rs b/src/cargo/util/mod.rs index 3236920da61..e2f32048d6e 100644 --- a/src/cargo/util/mod.rs +++ b/src/cargo/util/mod.rs @@ -176,8 +176,8 @@ mod test { ); assert_eq!(human_readable_bytes(1024 * 1024 * 1024), (1., "GiB")); assert_eq!( - human_readable_bytes((1024. * 1024. * 1024. * 3.1415) as u64), - (3.1415, "GiB") + human_readable_bytes((1024. * 1024. * 1024. * 1.2345) as u64), + (1.2345, "GiB") ); assert_eq!(human_readable_bytes(1024 * 1024 * 1024 * 1024), (1., "TiB")); assert_eq!( From fd544063ca9fec325d5c4ca64ad9f2c75fa02f73 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 8 Nov 2024 08:37:20 -0600 Subject: [PATCH 026/525] chore(ci): Check for clippy `correctness` The fact that we don't check for `derive_ord_xor_partial_ord ` almost bit us in #14663. Instead of just enabling this one lint, I figured it'd be good to audit all of the `deny` by default lints. That is long enough that I was concerned about maintaining it (or bikeshedding which to enable or disable). All `deny` by default lints are `correctness` lints and I figure we could just enable the group. We normally opt-in to individual clippy lints. From what I remember of that conversation, it mostly stems from how liberal clippy is with making a lint `warn` by default. It also makes an unpinned CI more brittle. I figured clippy is more conservative about `deny` by default lints and slower to add them that this is unlikely to be a problem. --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 3245a05ca0b..ee8eecdfeb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,7 +121,8 @@ rust_2018_idioms = "warn" # TODO: could this be removed? private_intra_doc_links = "allow" [workspace.lints.clippy] -all = { level = "allow", priority = -1 } +all = { level = "allow", priority = -2 } +correctness = { level = "warn", priority = -1 } dbg_macro = "warn" disallowed_methods = "warn" print_stderr = "warn" From f01862409303883564fad437e2d612edb4964fae Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 8 Nov 2024 15:21:33 -0600 Subject: [PATCH 027/525] test(toml): Import common assertion --- src/cargo/util/toml/embedded.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index ae072c10e96..4b490ba84b2 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -267,6 +267,7 @@ fn split_source(input: &str) -> CargoResult> { #[cfg(test)] mod test_expand { + use snapbox::assert_data_eq; use snapbox::str; use super::*; @@ -284,7 +285,7 @@ mod test_expand { #[test] fn test_default() { - snapbox::assert_data_eq!( + assert_data_eq!( si!(r#"fn main() {}"#), str![[r#" [[bin]] @@ -312,7 +313,7 @@ strip = true #[test] fn test_dependencies() { - snapbox::assert_data_eq!( + assert_data_eq!( si!(r#"---cargo [dependencies] time="0.1.25" @@ -348,7 +349,7 @@ strip = true #[test] fn test_no_infostring() { - snapbox::assert_data_eq!( + assert_data_eq!( si!(r#"--- [dependencies] time="0.1.25" From d769cb5f77c3e4d5c3c7598979c16ffad36b0cf7 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 8 Nov 2024 15:14:25 -0600 Subject: [PATCH 028/525] test(toml): Clean up manifest expanding helper --- src/cargo/util/toml/embedded.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index 4b490ba84b2..9e0be303387 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -272,21 +272,20 @@ mod test_expand { use super::*; - macro_rules! si { - ($i:expr) => {{ - let shell = crate::Shell::from_write(Box::new(Vec::new())); - let cwd = std::env::current_dir().unwrap(); - let home = home::cargo_home_with_cwd(&cwd).unwrap(); - let gctx = GlobalContext::new(shell, cwd, home); - expand_manifest($i, std::path::Path::new("/home/me/test.rs"), &gctx) - .unwrap_or_else(|err| panic!("{}", err)) - }}; + #[track_caller] + fn expand(source: &str) -> String { + let shell = crate::Shell::from_write(Box::new(Vec::new())); + let cwd = std::env::current_dir().unwrap(); + let home = home::cargo_home_with_cwd(&cwd).unwrap(); + let gctx = GlobalContext::new(shell, cwd, home); + expand_manifest(source, std::path::Path::new("/home/me/test.rs"), &gctx) + .unwrap_or_else(|err| panic!("{}", err)) } #[test] fn test_default() { assert_data_eq!( - si!(r#"fn main() {}"#), + expand(r#"fn main() {}"#), str![[r#" [[bin]] name = "test-" @@ -314,12 +313,14 @@ strip = true #[test] fn test_dependencies() { assert_data_eq!( - si!(r#"---cargo + expand( + r#"---cargo [dependencies] time="0.1.25" --- fn main() {} -"#), +"# + ), str![[r#" [[bin]] name = "test-" @@ -350,12 +351,14 @@ strip = true #[test] fn test_no_infostring() { assert_data_eq!( - si!(r#"--- + expand( + r#"--- [dependencies] time="0.1.25" --- fn main() {} -"#), +"# + ), str![[r#" [[bin]] name = "test-" From 2ea0d973de251b307f57edadcca3a120a28e18a2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 8 Nov 2024 15:20:04 -0600 Subject: [PATCH 029/525] test(toml): Separate manifest expansion from code fence tests --- src/cargo/util/toml/embedded.rs | 87 ++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index 9e0be303387..f818a460000 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -268,10 +268,55 @@ fn split_source(input: &str) -> CargoResult> { #[cfg(test)] mod test_expand { use snapbox::assert_data_eq; + use snapbox::prelude::*; use snapbox::str; use super::*; + #[track_caller] + fn assert_source(source: &str, expected: impl IntoData) { + use std::fmt::Write as _; + + let actual = match split_source(source) { + Ok(actual) => actual, + Err(err) => panic!("unexpected err: {err}"), + }; + + let mut rendered = String::new(); + write_optional_field(&mut rendered, "shebang", actual.shebang); + write_optional_field(&mut rendered, "info", actual.info); + write_optional_field(&mut rendered, "frontmatter", actual.frontmatter); + writeln!(&mut rendered, "content: {:?}", actual.content).unwrap(); + assert_data_eq!(rendered, expected.raw()); + } + + fn write_optional_field(writer: &mut dyn std::fmt::Write, field: &str, value: Option<&str>) { + if let Some(value) = value { + writeln!(writer, "{field}: {value:?}").unwrap(); + } else { + writeln!(writer, "{field}: None").unwrap(); + } + } + + #[test] + fn split_dependencies() { + assert_source( + r#"--- +[dependencies] +time="0.1.25" +--- +fn main() {} +"#, + str![[r#" +shebang: None +info: None +frontmatter: "[dependencies]\ntime=\"0.1.25\"\n" +content: "fn main() {}\n" + +"#]], + ); + } + #[track_caller] fn expand(source: &str) -> String { let shell = crate::Shell::from_write(Box::new(Vec::new())); @@ -283,7 +328,7 @@ mod test_expand { } #[test] - fn test_default() { + fn expand_default() { assert_data_eq!( expand(r#"fn main() {}"#), str![[r#" @@ -311,7 +356,7 @@ strip = true } #[test] - fn test_dependencies() { + fn expand_dependencies() { assert_data_eq!( expand( r#"---cargo @@ -344,44 +389,6 @@ strip = true [workspace] -"#]] - ); - } - - #[test] - fn test_no_infostring() { - assert_data_eq!( - expand( - r#"--- -[dependencies] -time="0.1.25" ---- -fn main() {} -"# - ), - str![[r#" -[[bin]] -name = "test-" -path = [..] - -[dependencies] -time = "0.1.25" - -[package] -autobenches = false -autobins = false -autoexamples = false -autolib = false -autotests = false -build = false -edition = "2021" -name = "test-" - -[profile.release] -strip = true - -[workspace] - "#]] ); } From 1328108e9687fd2056115115a2ab183f08dd2458 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Mon, 11 Nov 2024 10:56:02 -0500 Subject: [PATCH 030/525] refactor: clone-on-write when needed for InternedString --- src/cargo/util/interning.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/cargo/util/interning.rs b/src/cargo/util/interning.rs index 0363a2f42d2..595968eb654 100644 --- a/src/cargo/util/interning.rs +++ b/src/cargo/util/interning.rs @@ -1,6 +1,7 @@ use serde::{Serialize, Serializer}; use serde_untagged::UntaggedEnumVisitor; use std::borrow::Borrow; +use std::borrow::Cow; use std::cmp::Ordering; use std::collections::HashSet; use std::ffi::OsStr; @@ -46,7 +47,7 @@ impl<'a> From<&'a String> for InternedString { impl From for InternedString { fn from(item: String) -> Self { - InternedString::new(&item) + InternedString::from_cow(item.into()) } } @@ -72,9 +73,13 @@ impl Eq for InternedString {} impl InternedString { pub fn new(str: &str) -> InternedString { + InternedString::from_cow(str.into()) + } + + fn from_cow<'a>(str: Cow<'a, str>) -> InternedString { let mut cache = interned_storage(); - let s = cache.get(str).copied().unwrap_or_else(|| { - let s = str.to_string().leak(); + let s = cache.get(str.as_ref()).copied().unwrap_or_else(|| { + let s = str.into_owned().leak(); cache.insert(s); s }); From f1895819bc5ed09171b0aa5b8e211854fd103054 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 11 Nov 2024 16:24:23 -0600 Subject: [PATCH 031/525] test(git): Clarify checkout path --- tests/testsuite/git.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/testsuite/git.rs b/tests/testsuite/git.rs index 6bc72423c72..c22d3db88db 100644 --- a/tests/testsuite/git.rs +++ b/tests/testsuite/git.rs @@ -3873,17 +3873,17 @@ fn _corrupted_checkout(with_cli: bool) { p.cargo("fetch").run(); - let mut paths = t!(glob::glob( + let mut dep1_co_paths = t!(glob::glob( paths::home() .join(".cargo/git/checkouts/dep1-*/*") .to_str() .unwrap() )); - let path = paths.next().unwrap().unwrap(); - let ok = path.join(".cargo-ok"); + let dep1_co_path = dep1_co_paths.next().unwrap().unwrap(); + let dep1_ok = dep1_co_path.join(".cargo-ok"); // Deleting this file simulates an interrupted checkout. - t!(fs::remove_file(&ok)); + t!(fs::remove_file(&dep1_ok)); // This should refresh the checkout. let mut e = p.cargo("fetch"); @@ -3891,7 +3891,7 @@ fn _corrupted_checkout(with_cli: bool) { e.env("CARGO_NET_GIT_FETCH_WITH_CLI", "true"); } e.run(); - assert!(ok.exists()); + assert!(dep1_ok.exists()); } #[cargo_test] From e8ad597ed475f5d5a5360063a907fb2d320039a0 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 11 Nov 2024 16:25:22 -0600 Subject: [PATCH 032/525] test(git): Verify corrupted checkout is fixed --- tests/testsuite/git.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/testsuite/git.rs b/tests/testsuite/git.rs index c22d3db88db..010cfc6de9a 100644 --- a/tests/testsuite/git.rs +++ b/tests/testsuite/git.rs @@ -3881,9 +3881,11 @@ fn _corrupted_checkout(with_cli: bool) { )); let dep1_co_path = dep1_co_paths.next().unwrap().unwrap(); let dep1_ok = dep1_co_path.join(".cargo-ok"); + let dep1_manifest = dep1_co_path.join("Cargo.toml"); // Deleting this file simulates an interrupted checkout. t!(fs::remove_file(&dep1_ok)); + t!(fs::remove_file(&dep1_manifest)); // This should refresh the checkout. let mut e = p.cargo("fetch"); @@ -3892,6 +3894,7 @@ fn _corrupted_checkout(with_cli: bool) { } e.run(); assert!(dep1_ok.exists()); + assert!(dep1_manifest.exists()); } #[cargo_test] From 63c01d8dd0cfd646de1fdd8af4b0a05ab953f43e Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 1 Oct 2024 10:23:56 +0200 Subject: [PATCH 033/525] test(git): Add submodule to _corrupted_checkout tests --- tests/testsuite/git.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/testsuite/git.rs b/tests/testsuite/git.rs index 010cfc6de9a..a0650bc0646 100644 --- a/tests/testsuite/git.rs +++ b/tests/testsuite/git.rs @@ -9,7 +9,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread; -use cargo_test_support::git::cargo_uses_gitoxide; +use cargo_test_support::git::{add_submodule, cargo_uses_gitoxide}; use cargo_test_support::paths; use cargo_test_support::prelude::IntoData; use cargo_test_support::prelude::*; @@ -3847,11 +3847,20 @@ fn corrupted_checkout_with_cli() { } fn _corrupted_checkout(with_cli: bool) { - let git_project = git::new("dep1", |project| { + let (git_project, repository) = git::new_repo("dep1", |project| { project .file("Cargo.toml", &basic_manifest("dep1", "0.5.0")) .file("src/lib.rs", "") }); + + let project2 = git::new("dep2", |project| { + project.no_manifest().file("README.md", "") + }); + let url = project2.root().to_url().to_string(); + add_submodule(&repository, &url, Path::new("dep2")); + git::commit(&repository); + drop(repository); + let p = project() .file( "Cargo.toml", @@ -3882,10 +3891,12 @@ fn _corrupted_checkout(with_cli: bool) { let dep1_co_path = dep1_co_paths.next().unwrap().unwrap(); let dep1_ok = dep1_co_path.join(".cargo-ok"); let dep1_manifest = dep1_co_path.join("Cargo.toml"); + let dep2_readme = dep1_co_path.join("dep2/README.md"); // Deleting this file simulates an interrupted checkout. t!(fs::remove_file(&dep1_ok)); t!(fs::remove_file(&dep1_manifest)); + t!(fs::remove_file(&dep2_readme)); // This should refresh the checkout. let mut e = p.cargo("fetch"); @@ -3895,6 +3906,7 @@ fn _corrupted_checkout(with_cli: bool) { e.run(); assert!(dep1_ok.exists()); assert!(dep1_manifest.exists()); + assert!(dep2_readme.exists()); } #[cargo_test] From ecb63986881a66020fbe599b8775485e58607b7b Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 1 Oct 2024 11:03:52 +0200 Subject: [PATCH 034/525] refactor(git): Abstract `.cargo-ok` handling --- src/cargo/sources/git/utils.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index d28931f8642..39cd5616a5f 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -359,8 +359,7 @@ impl<'a> GitCheckout<'a> { /// /// [`.cargo-ok`]: CHECKOUT_READY_LOCK fn reset(&self, gctx: &GlobalContext) -> CargoResult<()> { - let ok_file = self.path.join(CHECKOUT_READY_LOCK); - let _ = paths::remove_file(&ok_file); + let guard = CheckoutGuard::guard(&self.path); info!("reset {} to {}", self.repo.path().display(), self.revision); // Ensure libgit2 won't mess with newlines when we vendor. @@ -370,7 +369,8 @@ impl<'a> GitCheckout<'a> { let object = self.repo.find_object(self.revision, None)?; reset(&self.repo, &object, gctx)?; - paths::create(ok_file)?; + + guard.mark_ok()?; Ok(()) } @@ -479,6 +479,25 @@ impl<'a> GitCheckout<'a> { } } +/// See [`GitCheckout::reset`] for rationale on this type. +#[must_use] +struct CheckoutGuard { + ok_file: PathBuf, +} + +impl CheckoutGuard { + fn guard(path: &Path) -> Self { + let ok_file = path.join(CHECKOUT_READY_LOCK); + let _ = paths::remove_file(&ok_file); + Self { ok_file } + } + + fn mark_ok(self) -> CargoResult<()> { + let _ = paths::create(self.ok_file)?; + Ok(()) + } +} + /// Constructs an absolute URL for a child submodule URL with its parent base URL. /// /// Git only assumes a submodule URL is a relative path if it starts with `./` From e82ad5fcf03fbb6049df19c5666d038e456a489c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 11 Nov 2024 16:21:15 -0600 Subject: [PATCH 035/525] perf(git): Skip submodules update on existing checkouts Fixes #14603 --- src/cargo/sources/git/utils.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index 39cd5616a5f..4f1f873bd8b 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -181,9 +181,14 @@ impl GitDatabase { .filter(|co| co.is_fresh()) { Some(co) => co, - None => GitCheckout::clone_into(dest, self, rev, gctx)?, + None => { + let (checkout, guard) = GitCheckout::clone_into(dest, self, rev, gctx)?; + checkout.update_submodules(gctx)?; + guard.mark_ok()?; + checkout + } }; - checkout.update_submodules(gctx)?; + Ok(checkout) } @@ -280,7 +285,7 @@ impl<'a> GitCheckout<'a> { database: &'a GitDatabase, revision: git2::Oid, gctx: &GlobalContext, - ) -> CargoResult> { + ) -> CargoResult<(GitCheckout<'a>, CheckoutGuard)> { let dirname = into.parent().unwrap(); paths::create_dir_all(&dirname)?; if into.exists() { @@ -329,8 +334,8 @@ impl<'a> GitCheckout<'a> { let repo = repo.unwrap(); let checkout = GitCheckout::new(database, revision, repo); - checkout.reset(gctx)?; - Ok(checkout) + let guard = checkout.reset(gctx)?; + Ok((checkout, guard)) } /// Checks if the `HEAD` of this checkout points to the expected revision. @@ -355,10 +360,11 @@ impl<'a> GitCheckout<'a> { /// To enable this we have a dummy file in our checkout, [`.cargo-ok`], /// which if present means that the repo has been successfully reset and is /// ready to go. Hence if we start to do a reset, we make sure this file - /// *doesn't* exist, and then once we're done we create the file. + /// *doesn't* exist. The caller of [`reset`] has an option to perform additional operations + /// (e.g. submodule update) before marking the check-out as ready. /// /// [`.cargo-ok`]: CHECKOUT_READY_LOCK - fn reset(&self, gctx: &GlobalContext) -> CargoResult<()> { + fn reset(&self, gctx: &GlobalContext) -> CargoResult { let guard = CheckoutGuard::guard(&self.path); info!("reset {} to {}", self.repo.path().display(), self.revision); @@ -370,8 +376,7 @@ impl<'a> GitCheckout<'a> { let object = self.repo.find_object(self.revision, None)?; reset(&self.repo, &object, gctx)?; - guard.mark_ok()?; - Ok(()) + Ok(guard) } /// Like `git submodule update --recursive` but for this git checkout. From c88c7891e0eaedf4855f315893a11984d5931695 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 6 Nov 2024 21:11:19 -0600 Subject: [PATCH 036/525] test(toml): Expand code fence tests --- src/cargo/util/toml/embedded.rs | 211 ++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index f818a460000..6bbd9083953 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -188,6 +188,7 @@ fn sanitize_name(name: &str) -> String { name } +#[derive(Debug)] struct Source<'s> { shebang: Option<&'s str>, info: Option<&'s str>, @@ -298,6 +299,32 @@ mod test_expand { } } + #[track_caller] + fn assert_err( + result: Result, + err: impl IntoData, + ) { + match result { + Ok(d) => panic!("unexpected Ok({d:#?})"), + Err(actual) => snapbox::assert_data_eq!(actual.to_string(), err.raw()), + } + } + + #[test] + fn split_default() { + assert_source( + r#"fn main() {} +"#, + str![[r#" +shebang: None +info: None +frontmatter: None +content: "fn main() {}\n" + +"#]], + ); + } + #[test] fn split_dependencies() { assert_source( @@ -317,6 +344,190 @@ content: "fn main() {}\n" ); } + #[test] + fn split_infostring() { + assert_source( + r#"---cargo +[dependencies] +time="0.1.25" +--- +fn main() {} +"#, + str![[r#" +shebang: None +info: "cargo" +frontmatter: "[dependencies]\ntime=\"0.1.25\"\n" +content: "fn main() {}\n" + +"#]], + ); + } + + #[test] + fn split_infostring_whitespace() { + assert_source( + r#"--- cargo +[dependencies] +time="0.1.25" +--- +fn main() {} +"#, + str![[r#" +shebang: None +info: " cargo" +frontmatter: "[dependencies]\ntime=\"0.1.25\"\n" +content: "fn main() {}\n" + +"#]], + ); + } + + #[test] + fn split_shebang() { + assert_source( + r#"#!/usr/bin/env cargo +--- +[dependencies] +time="0.1.25" +--- +fn main() {} +"#, + str![[r##" +shebang: "#!/usr/bin/env cargo" +info: None +frontmatter: "[dependencies]\ntime=\"0.1.25\"\n" +content: "fn main() {}\n" + +"##]], + ); + } + + #[test] + fn split_crlf() { + assert_source( + "#!/usr/bin/env cargo\r\n---\r\n[dependencies]\r\ntime=\"0.1.25\"\r\n---\r\nfn main() {}", + str![[r##" +shebang: "#!/usr/bin/env cargo\r" +info: "" +frontmatter: "[dependencies]\r\ntime=\"0.1.25\"\r\n" +content: "fn main() {}" + +"##]] + ); + } + + #[test] + fn split_leading_newlines() { + assert_source( + r#"#!/usr/bin/env cargo + + + +--- +[dependencies] +time="0.1.25" +--- + + +fn main() {} +"#, + str![[r##" +shebang: "#!/usr/bin/env cargo" +info: None +frontmatter: None +content: " \n\n\n---\n[dependencies]\ntime=\"0.1.25\"\n---\n\n\nfn main() {}\n" + +"##]], + ); + } + + #[test] + fn split_attribute() { + assert_source( + r#"#[allow(dead_code)] +--- +[dependencies] +time="0.1.25" +--- +fn main() {} +"#, + str![[r##" +shebang: None +info: None +frontmatter: None +content: "#[allow(dead_code)]\n---\n[dependencies]\ntime=\"0.1.25\"\n---\nfn main() {}\n" + +"##]], + ); + } + + #[test] + fn split_extra_dash() { + assert_source( + r#"#!/usr/bin/env cargo +---------- +[dependencies] +time="0.1.25" +---------- + +fn main() {}"#, + str![[r##" +shebang: "#!/usr/bin/env cargo" +info: None +frontmatter: "[dependencies]\ntime=\"0.1.25\"\n" +content: "\nfn main() {}" + +"##]], + ); + } + + #[test] + fn split_too_few_dashes() { + assert_err( + split_source( + r#"#!/usr/bin/env cargo +-- +[dependencies] +time="0.1.25" +-- +fn main() {} +"#, + ), + str!["found 2 `-` in rust frontmatter, expected at least 3"], + ); + } + + #[test] + fn split_mismatched_dashes() { + assert_err( + split_source( + r#"#!/usr/bin/env cargo +--- +[dependencies] +time="0.1.25" +---- +fn main() {} +"#, + ), + str!["unexpected trailing content on closing fence: `-`"], + ); + } + + #[test] + fn split_missing_close() { + assert_err( + split_source( + r#"#!/usr/bin/env cargo +--- +[dependencies] +time="0.1.25" +fn main() {} +"#, + ), + str!["no closing `---` found for frontmatter"], + ); + } + #[track_caller] fn expand(source: &str) -> String { let shell = crate::Shell::from_write(Box::new(Vec::new())); From 8c88f236fa8d5c80308b45455267738790242407 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Nov 2024 09:01:43 -0600 Subject: [PATCH 037/525] refactor(toml): Remove 'tick' language We've switched to dashes --- src/cargo/util/toml/embedded.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index 6bbd9083953..10d7e6521ac 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -225,21 +225,23 @@ fn split_source(input: &str) -> CargoResult> { } // Experiment: let us try which char works better - let tick_char = '-'; + let fence_char = '-'; - let tick_end = source + let fence_end = source .content .char_indices() - .find_map(|(i, c)| (c != tick_char).then_some(i)) + .find_map(|(i, c)| (c != fence_char).then_some(i)) .unwrap_or(source.content.len()); - let (fence_pattern, rest) = match tick_end { + let (fence_pattern, rest) = match fence_end { 0 => { return Ok(source); } 1 | 2 => { - anyhow::bail!("found {tick_end} `{tick_char}` in rust frontmatter, expected at least 3") + anyhow::bail!( + "found {fence_end} `{fence_char}` in rust frontmatter, expected at least 3" + ) } - _ => source.content.split_at(tick_end), + _ => source.content.split_at(fence_end), }; let (info, content) = rest.split_once("\n").unwrap_or((rest, "")); if !info.is_empty() { From 45c2ff80fc5869a7cda0cf4e3a6ea35693eca0e3 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Nov 2024 09:02:50 -0600 Subject: [PATCH 038/525] refactor(toml): Remove references to multiple fence chars --- src/cargo/util/toml/embedded.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index 10d7e6521ac..9620d35052f 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -224,13 +224,12 @@ fn split_source(input: &str) -> CargoResult> { source.content = content; } - // Experiment: let us try which char works better - let fence_char = '-'; + const FENCE_CHAR: char = '-'; let fence_end = source .content .char_indices() - .find_map(|(i, c)| (c != fence_char).then_some(i)) + .find_map(|(i, c)| (c != FENCE_CHAR).then_some(i)) .unwrap_or(source.content.len()); let (fence_pattern, rest) = match fence_end { 0 => { @@ -238,7 +237,7 @@ fn split_source(input: &str) -> CargoResult> { } 1 | 2 => { anyhow::bail!( - "found {fence_end} `{fence_char}` in rust frontmatter, expected at least 3" + "found {fence_end} `{FENCE_CHAR}` in rust frontmatter, expected at least 3" ) } _ => source.content.split_at(fence_end), From 51db4414d865a7a82a30d0a3764fb9a8134e7b72 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Nov 2024 09:40:40 -0600 Subject: [PATCH 039/525] refactor(toml): Preserve the full newline for shebang The shebang is thrown away so this has no end-user impact --- src/cargo/util/toml/embedded.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index 9620d35052f..e9cccd0e59c 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -216,10 +216,12 @@ fn split_source(input: &str) -> CargoResult> { } // No other choice than to consider this a shebang. - let (shebang, content) = source + let newline_end = source .content - .split_once('\n') - .unwrap_or((source.content, "")); + .find('\n') + .map(|pos| pos + 1) + .unwrap_or(source.content.len()); + let (shebang, content) = source.content.split_at(newline_end); source.shebang = Some(shebang); source.content = content; } @@ -394,7 +396,7 @@ time="0.1.25" fn main() {} "#, str![[r##" -shebang: "#!/usr/bin/env cargo" +shebang: "#!/usr/bin/env cargo\n" info: None frontmatter: "[dependencies]\ntime=\"0.1.25\"\n" content: "fn main() {}\n" @@ -408,7 +410,7 @@ content: "fn main() {}\n" assert_source( "#!/usr/bin/env cargo\r\n---\r\n[dependencies]\r\ntime=\"0.1.25\"\r\n---\r\nfn main() {}", str![[r##" -shebang: "#!/usr/bin/env cargo\r" +shebang: "#!/usr/bin/env cargo\r\n" info: "" frontmatter: "[dependencies]\r\ntime=\"0.1.25\"\r\n" content: "fn main() {}" @@ -433,7 +435,7 @@ time="0.1.25" fn main() {} "#, str![[r##" -shebang: "#!/usr/bin/env cargo" +shebang: "#!/usr/bin/env cargo\n" info: None frontmatter: None content: " \n\n\n---\n[dependencies]\ntime=\"0.1.25\"\n---\n\n\nfn main() {}\n" @@ -473,7 +475,7 @@ time="0.1.25" fn main() {}"#, str![[r##" -shebang: "#!/usr/bin/env cargo" +shebang: "#!/usr/bin/env cargo\n" info: None frontmatter: "[dependencies]\ntime=\"0.1.25\"\n" content: "\nfn main() {}" From 89186345c395c9a36354e7d467ee0a36c7eff2e8 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Nov 2024 14:05:41 -0600 Subject: [PATCH 040/525] fix(toml): Don't error on infostrings with trailing whitespace --- src/cargo/util/toml/embedded.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index e9cccd0e59c..3ce56b7a92b 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -245,8 +245,9 @@ fn split_source(input: &str) -> CargoResult> { _ => source.content.split_at(fence_end), }; let (info, content) = rest.split_once("\n").unwrap_or((rest, "")); + let info = info.trim_end(); if !info.is_empty() { - source.info = Some(info.trim_end()); + source.info = Some(info); } source.content = content; @@ -411,7 +412,7 @@ content: "fn main() {}\n" "#!/usr/bin/env cargo\r\n---\r\n[dependencies]\r\ntime=\"0.1.25\"\r\n---\r\nfn main() {}", str![[r##" shebang: "#!/usr/bin/env cargo\r\n" -info: "" +info: None frontmatter: "[dependencies]\r\ntime=\"0.1.25\"\r\n" content: "fn main() {}" From c1248adbd4aa08e9fa6bc95f02659e884fa1a28a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Nov 2024 14:09:22 -0600 Subject: [PATCH 041/525] fix(toml): Don't error on leading whitespace in infostring According to the [RFC](https://rust-lang.github.io/rfcs/3503-frontmatter.html): > Opens with 3+ dashes followed by 0+ whitespace, > an optional term (one or more characters excluding whitespace and commas), > 0+ whitespace, and a newline --- src/cargo/util/toml/embedded.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index 3ce56b7a92b..d25dcbfe52a 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -245,7 +245,7 @@ fn split_source(input: &str) -> CargoResult> { _ => source.content.split_at(fence_end), }; let (info, content) = rest.split_once("\n").unwrap_or((rest, "")); - let info = info.trim_end(); + let info = info.trim(); if !info.is_empty() { source.info = Some(info); } @@ -378,7 +378,7 @@ fn main() {} "#, str![[r#" shebang: None -info: " cargo" +info: "cargo" frontmatter: "[dependencies]\ntime=\"0.1.25\"\n" content: "fn main() {}\n" From 82836b0da5e00aa69e11a1ebf64d7224b0bcd225 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 7 Nov 2024 14:34:26 -0600 Subject: [PATCH 042/525] fix(toml): Strip blank linkes before frontmatter From the [RFC](https://rust-lang.github.io/rfcs/3503-frontmatter.html) > When parsing Rust source, after stripping the shebang (#!), rustc will strip the frontmatter: > > - May include 0+ blank lines (whitespace + newline) The question is what is a non-newline whitespace and what is a newline. Looking at the [Reference](https://doc.rust-lang.org/reference/whitespace.html), the answer is "unsure", particularly because the Rust language doesn't generally try to distinguish them. I kept things basic for now and we can revisit later. --- src/cargo/util/toml/embedded.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index d25dcbfe52a..6f569b94cf0 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -228,8 +228,17 @@ fn split_source(input: &str) -> CargoResult> { const FENCE_CHAR: char = '-'; - let fence_end = source - .content + let mut trimmed_content = source.content; + while !trimmed_content.is_empty() { + let c = trimmed_content; + let c = c.trim_start_matches([' ', '\t']); + let c = c.trim_start_matches(['\r', '\n']); + if c == trimmed_content { + break; + } + trimmed_content = c; + } + let fence_end = trimmed_content .char_indices() .find_map(|(i, c)| (c != FENCE_CHAR).then_some(i)) .unwrap_or(source.content.len()); @@ -242,7 +251,7 @@ fn split_source(input: &str) -> CargoResult> { "found {fence_end} `{FENCE_CHAR}` in rust frontmatter, expected at least 3" ) } - _ => source.content.split_at(fence_end), + _ => trimmed_content.split_at(fence_end), }; let (info, content) = rest.split_once("\n").unwrap_or((rest, "")); let info = info.trim(); @@ -438,8 +447,8 @@ fn main() {} str![[r##" shebang: "#!/usr/bin/env cargo\n" info: None -frontmatter: None -content: " \n\n\n---\n[dependencies]\ntime=\"0.1.25\"\n---\n\n\nfn main() {}\n" +frontmatter: "[dependencies]\ntime=\"0.1.25\"\n" +content: "\n\nfn main() {}\n" "##]], ); From 40ee338a9110ce2666cb4048dbfcc873dd8bbb0d Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 12 Nov 2024 16:22:46 -0500 Subject: [PATCH 043/525] refactor: simplify logic of env var diplay with `-vv` --- src/cargo/core/compiler/compilation.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index 4830677b6d2..e4cccf2609a 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -129,19 +129,9 @@ pub struct Compilation<'gctx> { impl<'gctx> Compilation<'gctx> { pub fn new<'a>(bcx: &BuildContext<'a, 'gctx>) -> CargoResult> { - let mut rustc = bcx.rustc().process(); - let mut primary_rustc_process = bcx.build_config.primary_unit_rustc.clone(); - let mut rustc_workspace_wrapper_process = bcx.rustc().workspace_process(); - - if bcx.gctx.extra_verbose() { - rustc.display_env_vars(); - rustc_workspace_wrapper_process.display_env_vars(); - - if let Some(rustc) = primary_rustc_process.as_mut() { - rustc.display_env_vars(); - } - } - + let rustc_process = bcx.rustc().process(); + let primary_rustc_process = bcx.build_config.primary_unit_rustc.clone(); + let rustc_workspace_wrapper_process = bcx.rustc().workspace_process(); Ok(Compilation { native_dirs: BTreeSet::new(), root_output: HashMap::new(), @@ -155,7 +145,7 @@ impl<'gctx> Compilation<'gctx> { to_doc_test: Vec::new(), gctx: bcx.gctx, host: bcx.host_triple().to_string(), - rustc_process: rustc, + rustc_process, rustc_workspace_wrapper_process, primary_rustc_process, target_runners: bcx @@ -189,14 +179,16 @@ impl<'gctx> Compilation<'gctx> { is_primary: bool, is_workspace: bool, ) -> CargoResult { - let rustc = if is_primary && self.primary_rustc_process.is_some() { + let mut rustc = if is_primary && self.primary_rustc_process.is_some() { self.primary_rustc_process.clone().unwrap() } else if is_workspace { self.rustc_workspace_wrapper_process.clone() } else { self.rustc_process.clone() }; - + if self.gctx.extra_verbose() { + rustc.display_env_vars(); + } let cmd = fill_rustc_tool_env(rustc, unit); self.fill_env(cmd, &unit.pkg, None, unit.kind, ToolKind::Rustc) } From 9a5fd425e13b5b769d70c7f100afd0dd5f6247be Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 12 Nov 2024 16:41:59 -0500 Subject: [PATCH 044/525] feat(rustdoc): diplay env vars in extra verbose mode This seems to be an overlook --- src/cargo/core/compiler/compilation.rs | 5 ++++- tests/testsuite/profile_targets.rs | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index e4cccf2609a..39f0f9f7241 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -199,7 +199,10 @@ impl<'gctx> Compilation<'gctx> { unit: &Unit, script_meta: Option, ) -> CargoResult { - let rustdoc = ProcessBuilder::new(&*self.gctx.rustdoc()?); + let mut rustdoc = ProcessBuilder::new(&*self.gctx.rustdoc()?); + if self.gctx.extra_verbose() { + rustdoc.display_env_vars(); + } let cmd = fill_rustc_tool_env(rustdoc, unit); let mut cmd = self.fill_env(cmd, &unit.pkg, script_meta, unit.kind, ToolKind::Rustdoc)?; cmd.retry_with_argfile(true); diff --git a/tests/testsuite/profile_targets.rs b/tests/testsuite/profile_targets.rs index 8aa2c372db6..c95369966ef 100644 --- a/tests/testsuite/profile_targets.rs +++ b/tests/testsuite/profile_targets.rs @@ -715,7 +715,7 @@ fn profile_selection_doc() { [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [DOCUMENTING] bar v0.0.1 ([ROOT]/foo/bar) [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]` -[RUNNING] `rustdoc [..]--crate-name bar bar/src/lib.rs [..] +[RUNNING] `[..] rustdoc [..]--crate-name bar bar/src/lib.rs [..] [RUNNING] `[..] rustc --crate-name bar --edition=2015 bar/src/lib.rs [..]--crate-type lib --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` [COMPILING] bdep v0.0.1 ([ROOT]/foo/bdep) [RUNNING] `[..] rustc --crate-name bdep --edition=2015 bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..]` @@ -724,7 +724,7 @@ fn profile_selection_doc() { [RUNNING] `[..][ROOT]/foo/target/debug/build/foo-[HASH]/build-script-build` [foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) -[RUNNING] `rustdoc [..]--crate-name foo src/lib.rs [..] +[RUNNING] `[..] rustdoc [..]--crate-name foo src/lib.rs [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html From eef4b30d3112c7533ca9682be4b3e90702fafb51 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 12:42:44 -0600 Subject: [PATCH 045/525] docs(build-rs): Split out short descriptions --- crates/build-rs/src/input.rs | 21 +++++++++++++----- crates/build-rs/src/output.rs | 41 ++++++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs index 1b7adc36cd4..01a4a3946ae 100644 --- a/crates/build-rs/src/input.rs +++ b/crates/build-rs/src/input.rs @@ -97,7 +97,9 @@ pub fn cargo() -> PathBuf { } /// The directory containing the manifest for the package being built (the package -/// containing the build script). Also note that this is the value of the current +/// containing the build script). +/// +/// Also note that this is the value of the current /// working directory of the build script when it starts. #[track_caller] pub fn cargo_manifest_dir() -> PathBuf { @@ -105,7 +107,9 @@ pub fn cargo_manifest_dir() -> PathBuf { } /// Contains parameters needed for Cargo’s [jobserver] implementation to parallelize -/// subprocesses. Rustc or cargo invocations from build.rs can already read +/// subprocesses. +/// +/// Rustc or cargo invocations from build.rs can already read /// `CARGO_MAKEFLAGS`, but GNU Make requires the flags to be specified either /// directly as arguments, or through the `MAKEFLAGS` environment variable. /// Currently Cargo doesn’t set the `MAKEFLAGS` variable, but it’s free for build @@ -129,7 +133,9 @@ pub fn cargo_feature(name: &str) -> bool { } /// For each [configuration option] of the package being built, this will contain -/// the value of the configuration. This includes values built-in to the compiler +/// the value of the configuration. +/// +/// This includes values built-in to the compiler /// (which can be seen with `rustc --print=cfg`) and values set by build scripts /// and extra flags passed to rustc (such as those defined in `RUSTFLAGS`). /// @@ -374,6 +380,7 @@ mod cfg { } /// The folder in which all output and intermediate artifacts should be placed. +/// /// This folder is inside the build directory for the package being built, and /// it is unique for the package in question. #[track_caller] @@ -396,7 +403,9 @@ pub fn host() -> String { get_str("HOST") } -/// The parallelism specified as the top-level parallelism. This can be useful to +/// The parallelism specified as the top-level parallelism. +/// +/// This can be useful to /// pass a `-j` parameter to a system like `make`. Note that care should be taken /// when interpreting this value. For historical purposes this is still provided /// but Cargo, for example, does not need to run `make -j`, and instead can set the @@ -421,7 +430,9 @@ pub fn debug() -> String { get_str("DEBUG") } -/// `release` for release builds, `debug` for other builds. This is determined based +/// `release` for release builds, `debug` for other builds. +/// +/// This is determined based /// on if the [profile] inherits from the [`dev`] or [`release`] profile. Using this /// function is not recommended. Using other functions like [`opt_level`] provides /// a more correct view of the actual settings being used. diff --git a/crates/build-rs/src/output.rs b/crates/build-rs/src/output.rs index 318185a040e..c275338c611 100644 --- a/crates/build-rs/src/output.rs +++ b/crates/build-rs/src/output.rs @@ -21,7 +21,9 @@ fn emit(directive: &str, value: impl Display) { } /// The `rerun-if-changed` instruction tells Cargo to re-run the build script if the -/// file at the given path has changed. Currently, Cargo only uses the filesystem +/// file at the given path has changed. +/// +/// Currently, Cargo only uses the filesystem /// last-modified “mtime” timestamp to determine if the file has changed. It /// compares against an internal cached timestamp of when the build script last ran. /// @@ -70,6 +72,7 @@ pub fn rerun_if_env_changed(key: impl AsRef) { /// The `rustc-link-arg` instruction tells Cargo to pass the /// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building /// supported targets (benchmarks, binaries, cdylib crates, examples, and tests). +/// /// Its usage is highly platform specific. It is useful to set the shared library /// version or linker script. /// @@ -84,7 +87,9 @@ pub fn rustc_link_arg(flag: &str) { /// The `rustc-link-arg-bin` instruction tells Cargo to pass the /// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building -/// the binary target with name `BIN`. Its usage is highly platform specific. It +/// the binary target with name `BIN`. Its usage is highly platform specific. +/// +/// It /// is useful to set a linker script or other linker options. /// /// [link-arg]: https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg @@ -101,7 +106,9 @@ pub fn rustc_link_arg_bin(bin: &str, flag: &str) { /// The `rustc-link-arg-bins` instruction tells Cargo to pass the /// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building -/// the binary target. Its usage is highly platform specific. It is useful to set +/// the binary target. +/// +/// Its usage is highly platform specific. It is useful to set /// a linker script or other linker options. /// /// [link-arg]: https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg @@ -147,7 +154,9 @@ pub fn rustc_link_arg_benches(flag: &str) { } /// The `rustc-link-lib` instruction tells Cargo to link the given library using -/// the compiler’s [`-l` flag][-l]. This is typically used to link a native library +/// the compiler’s [`-l` flag][-l]. +/// +/// This is typically used to link a native library /// using [FFI]. /// /// The `LIB` string is passed directly to rustc, so it supports any syntax that @@ -229,7 +238,9 @@ pub fn rustc_link_search_kind(kind: &str, path: impl AsRef) { } /// The `rustc-flags` instruction tells Cargo to pass the given space-separated -/// flags to the compiler. This only allows the `-l` and `-L` flags, and is +/// flags to the compiler. +/// +/// This only allows the `-l` and `-L` flags, and is /// equivalent to using [`rustc_link_lib`] and [`rustc_link_search`]. #[track_caller] pub fn rustc_flags(flags: &str) { @@ -240,7 +251,9 @@ pub fn rustc_flags(flags: &str) { } /// The `rustc-cfg` instruction tells Cargo to pass the given value to the -/// [`--cfg` flag][cfg] to the compiler. This may be used for compile-time +/// [`--cfg` flag][cfg] to the compiler. +/// +/// This may be used for compile-time /// detection of features to enable conditional compilation. /// /// Note that this does not affect Cargo’s dependency resolution. This cannot @@ -264,7 +277,9 @@ pub fn rustc_cfg(key: &str) { emit("rustc-cfg", key); } -/// Like [`rustc_cfg`], but with the value specified separately. To replace the +/// Like [`rustc_cfg`], but with the value specified separately. +/// +/// To replace the /// less convenient `rustc_cfg(r#"my_component="foo""#)`, you can instead use /// `rustc_cfg_value("my_component", "foo")`. #[track_caller] @@ -342,7 +357,9 @@ pub fn rustc_check_cfg_values(key: &str, values: &[&str]) { } /// The `rustc-env` instruction tells Cargo to set the given environment variable -/// when compiling the package. The value can be then retrieved by the +/// when compiling the package. +/// +/// The value can be then retrieved by the /// [`env!` macro][env!] in the compiled crate. This is useful for embedding /// additional metadata in crate’s code, such as the hash of git HEAD or the /// unique identifier of a continuous integration server. @@ -363,7 +380,9 @@ pub fn rustc_env(key: &str, value: &str) { /// The `rustc-cdylib-link-arg` instruction tells Cargo to pass the /// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building -/// a `cdylib` library target. Its usage is highly platform specific. It is useful +/// a `cdylib` library target. +/// +/// Its usage is highly platform specific. It is useful /// to set the shared library version or the runtime-path. /// /// [link-arg]: https://doc.rust-lang.org/rustc/codegen-options/index.html#link-arg @@ -376,7 +395,9 @@ pub fn rustc_cdylib_link_arg(flag: &str) { } /// The `warning` instruction tells Cargo to display a warning after the build -/// script has finished running. Warnings are only shown for path dependencies +/// script has finished running. +/// +/// Warnings are only shown for path dependencies /// (that is, those you’re working on locally), so for example warnings printed /// out in [crates.io] crates are not emitted by default. The `-vv` “very verbose” /// flag may be used to have Cargo display warnings for all crates. From e4e7a36eaaa769402982a4d45d542efaacdef39c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 12:43:26 -0600 Subject: [PATCH 046/525] docs(build-rs): Correct documentation for manifest_links --- crates/build-rs/src/input.rs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs index 01a4a3946ae..0636623e991 100644 --- a/crates/build-rs/src/input.rs +++ b/crates/build-rs/src/input.rs @@ -106,16 +106,7 @@ pub fn cargo_manifest_dir() -> PathBuf { get_path("CARGO_MANIFEST_DIR") } -/// Contains parameters needed for Cargo’s [jobserver] implementation to parallelize -/// subprocesses. -/// -/// Rustc or cargo invocations from build.rs can already read -/// `CARGO_MAKEFLAGS`, but GNU Make requires the flags to be specified either -/// directly as arguments, or through the `MAKEFLAGS` environment variable. -/// Currently Cargo doesn’t set the `MAKEFLAGS` variable, but it’s free for build -/// scripts invoking GNU Make to set it to the contents of `CARGO_MAKEFLAGS`. -/// -/// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html +/// The manifest `links` value. #[track_caller] pub fn cargo_manifest_links() -> Option { get_opt_str("CARGO_MANIFEST_LINKS") From 58ac23d60a58fbf5155ae8afcdb940604d7750e1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 12:43:43 -0600 Subject: [PATCH 047/525] feat(build-rs): Add cargo_makeflags --- crates/build-rs/src/input.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs index 0636623e991..e8fb90fb5f0 100644 --- a/crates/build-rs/src/input.rs +++ b/crates/build-rs/src/input.rs @@ -112,6 +112,21 @@ pub fn cargo_manifest_links() -> Option { get_opt_str("CARGO_MANIFEST_LINKS") } +/// Contains parameters needed for Cargo’s [jobserver] implementation to parallelize +/// subprocesses. +/// +/// Rustc or cargo invocations from build.rs can already read +/// `CARGO_MAKEFLAGS`, but GNU Make requires the flags to be specified either +/// directly as arguments, or through the `MAKEFLAGS` environment variable. +/// Currently Cargo doesn’t set the `MAKEFLAGS` variable, but it’s free for build +/// scripts invoking GNU Make to set it to the contents of `CARGO_MAKEFLAGS`. +/// +/// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html +#[track_caller] +pub fn cargo_makeflags() -> Option { + get_opt_str("CARGO_MAKEFLAGS") +} + /// For each activated feature of the package being built, this will be `true`. #[track_caller] pub fn cargo_feature(name: &str) -> bool { From 5e53b21e19d7c32531c5fff93ddef0b9bf5c758b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 12:54:59 -0600 Subject: [PATCH 048/525] refactor(build-rs): Clarify bool policy --- crates/build-rs/src/input.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs index e8fb90fb5f0..7274af6c2c6 100644 --- a/crates/build-rs/src/input.rs +++ b/crates/build-rs/src/input.rs @@ -27,7 +27,7 @@ macro_rules! invalid { } #[track_caller] -fn get_bool(key: &str) -> bool { +fn is_present(key: &str) -> bool { env::var_os(key).is_some() } @@ -135,7 +135,7 @@ pub fn cargo_feature(name: &str) -> bool { } let name = name.to_uppercase().replace('-', "_"); let key = format!("CARGO_FEATURE_{name}"); - get_bool(&key) + is_present(&key) } /// For each [configuration option] of the package being built, this will contain @@ -162,31 +162,31 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_clippy() -> bool { - get_bool("CARGO_CFG_CLIPPY") + is_present("CARGO_CFG_CLIPPY") } /// If we are compiling with debug assertions enabled. #[track_caller] pub fn cargo_cfg_debug_assertions() -> bool { - get_bool("CARGO_CFG_DEBUG_ASSERTIONS") + is_present("CARGO_CFG_DEBUG_ASSERTIONS") } #[cfg(any())] #[track_caller] pub fn cargo_cfg_doc() -> bool { - get_bool("CARGO_CFG_DOC") + is_present("CARGO_CFG_DOC") } #[cfg(any())] #[track_caller] pub fn cargo_cfg_docsrs() -> bool { - get_bool("CARGO_CFG_DOCSRS") + is_present("CARGO_CFG_DOCSRS") } #[cfg(any())] #[track_caller] pub fn cargo_cfg_doctest() -> bool { - get_bool("CARGO_CFG_DOCTEST") + is_present("CARGO_CFG_DOCTEST") } /// The level of detail provided by derived [`Debug`] implementations. @@ -200,7 +200,7 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_miri() -> bool { - get_bool("CARGO_CFG_MIRI") + is_present("CARGO_CFG_MIRI") } /// If we are compiling with overflow checks enabled. @@ -208,7 +208,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_overflow_checks() -> bool { - get_bool("CARGO_CFG_OVERFLOW_CHECKS") + is_present("CARGO_CFG_OVERFLOW_CHECKS") } /// The [panic strategy](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#panic). @@ -220,7 +220,7 @@ mod cfg { /// If the crate is being compiled as a procedural macro. #[track_caller] pub fn cargo_cfg_proc_macro() -> bool { - get_bool("CARGO_CFG_PROC_MACRO") + is_present("CARGO_CFG_PROC_MACRO") } /// The target relocation model. @@ -234,7 +234,7 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_rustfmt() -> bool { - get_bool("CARGO_CFG_RUSTFMT") + is_present("CARGO_CFG_RUSTFMT") } /// Sanitizers enabled for the crate being compiled. @@ -251,7 +251,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_sanitizer_cfi_generalize_pointers() -> bool { - get_bool("CARGO_CFG_SANITIZER_CFI_GENERALIZE_POINTERS") + is_present("CARGO_CFG_SANITIZER_CFI_GENERALIZE_POINTERS") } /// If CFI sanitization is normalizing integers. @@ -259,7 +259,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_sanitizer_cfi_normalize_integers() -> bool { - get_bool("CARGO_CFG_SANITIZER_CFI_NORMALIZE_INTEGERS") + is_present("CARGO_CFG_SANITIZER_CFI_NORMALIZE_INTEGERS") } /// Disambiguation of the [target ABI](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_abi) @@ -349,7 +349,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_target_thread_local() -> bool { - get_bool("CARGO_CFG_TARGET_THREAD_LOCAL") + is_present("CARGO_CFG_TARGET_THREAD_LOCAL") } /// The [target vendor](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_vendor). @@ -361,7 +361,7 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_test() -> bool { - get_bool("CARGO_CFG_TEST") + is_present("CARGO_CFG_TEST") } /// If we are compiling with UB checks enabled. @@ -369,19 +369,19 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_ub_checks() -> bool { - get_bool("CARGO_CFG_UB_CHECKS") + is_present("CARGO_CFG_UB_CHECKS") } /// Set on [unix-like platforms](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#unix-and-windows). #[track_caller] pub fn cargo_cfg_unix() -> bool { - get_bool("CARGO_CFG_UNIX") + is_present("CARGO_CFG_UNIX") } /// Set on [windows-like platforms](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#unix-and-windows). #[track_caller] pub fn cargo_cfg_windows() -> bool { - get_bool("CARGO_CFG_WINDOWS") + is_present("CARGO_CFG_WINDOWS") } } From f4193559c21228698805d73a17a692ddd3b7958a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 13:32:25 -0600 Subject: [PATCH 049/525] refactor(build-rs): Make env vars more composable --- crates/build-rs/src/input.rs | 251 ++++++++++++++++------------------- 1 file changed, 114 insertions(+), 137 deletions(-) diff --git a/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs index 7274af6c2c6..cd457e073da 100644 --- a/crates/build-rs/src/input.rs +++ b/crates/build-rs/src/input.rs @@ -6,94 +6,17 @@ //! //! Reference: -use crate::ident::{is_ascii_ident, is_crate_name, is_feature_name}; -use std::{ - env, - fmt::Display, - path::PathBuf, - str::{self, FromStr}, -}; - -macro_rules! missing { - ($key:expr) => { - panic!("cargo environment variable `{}` is missing", $key) - }; -} - -macro_rules! invalid { - ($key:expr, $err:expr) => { - panic!("cargo environment variable `{}` is invalid: {}", $key, $err) - }; -} - -#[track_caller] -fn is_present(key: &str) -> bool { - env::var_os(key).is_some() -} - -#[track_caller] -fn get_opt_path(key: &str) -> Option { - let var = env::var_os(key)?; - Some(PathBuf::from(var)) -} - -#[track_caller] -fn get_path(key: &str) -> PathBuf { - get_opt_path(key).unwrap_or_else(|| missing!(key)) -} - -#[track_caller] -fn get_opt_str(key: &str) -> Option { - let var = env::var_os(key)?; - match str::from_utf8(var.as_encoded_bytes()) { - Ok(s) => Some(s.to_owned()), - Err(err) => invalid!(key, err), - } -} - -#[track_caller] -fn get_str(key: &str) -> String { - get_opt_str(key).unwrap_or_else(|| missing!(key)) -} - -#[track_caller] -fn get_num(key: &str) -> T -where - T::Err: Display, -{ - let val = get_str(key); - match val.parse() { - Ok(num) => num, - Err(err) => invalid!(key, err), - } -} - -#[track_caller] -fn get_opt_cfg(cfg: &str) -> (String, Option>) { - if !is_ascii_ident(cfg) { - panic!("invalid configuration option {cfg:?}") - } - let cfg = cfg.to_uppercase().replace('-', "_"); - let key = format!("CARGO_CFG_{cfg}"); - let Some(var) = env::var_os(&key) else { - return (key, None); - }; - let val = str::from_utf8(var.as_encoded_bytes()).unwrap_or_else(|err| invalid!(key, err)); - (key, Some(val.split(',').map(str::to_owned).collect())) -} +use std::env::var_os; +use std::path::PathBuf; -#[track_caller] -fn get_cfg(cfg: &str) -> Vec { - let (key, val) = get_opt_cfg(cfg); - val.unwrap_or_else(|| missing!(key)) -} +use crate::ident::{is_ascii_ident, is_crate_name, is_feature_name}; // docs last updated to match release 1.82.0 reference /// Path to the `cargo` binary performing the build. #[track_caller] pub fn cargo() -> PathBuf { - get_path("CARGO") + to_path(var_or_panic("CARGO")) } /// The directory containing the manifest for the package being built (the package @@ -103,13 +26,13 @@ pub fn cargo() -> PathBuf { /// working directory of the build script when it starts. #[track_caller] pub fn cargo_manifest_dir() -> PathBuf { - get_path("CARGO_MANIFEST_DIR") + to_path(var_or_panic("CARGO_MANIFEST_DIR")) } /// The manifest `links` value. #[track_caller] pub fn cargo_manifest_links() -> Option { - get_opt_str("CARGO_MANIFEST_LINKS") + var_os("CARGO_MANIFEST_LINKS").map(to_string) } /// Contains parameters needed for Cargo’s [jobserver] implementation to parallelize @@ -124,7 +47,7 @@ pub fn cargo_manifest_links() -> Option { /// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html #[track_caller] pub fn cargo_makeflags() -> Option { - get_opt_str("CARGO_MAKEFLAGS") + var_os("CARGO_MAKEFLAGS").map(to_string) } /// For each activated feature of the package being built, this will be `true`. @@ -148,8 +71,18 @@ pub fn cargo_feature(name: &str) -> bool { /// [configuration option]: https://doc.rust-lang.org/stable/reference/conditional-compilation.html #[track_caller] pub fn cargo_cfg(cfg: &str) -> Option> { - let (_, val) = get_opt_cfg(cfg); - val + let var = cargo_cfg_var(cfg); + var_os(&var).map(|v| to_strings(v, ',')) +} + +#[track_caller] +fn cargo_cfg_var(cfg: &str) -> String { + if !is_ascii_ident(cfg) { + panic!("invalid configuration option {cfg:?}") + } + let cfg = cfg.to_uppercase().replace('-', "_"); + let key = format!("CARGO_CFG_{cfg}"); + key } pub use self::cfg::*; @@ -194,7 +127,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_fmt_debug() -> String { - get_str("CARGO_CFG_FMT_DEBUG") + to_string(var_or_panic("CARGO_CFG_FMT_DEBUG")) } #[cfg(any())] @@ -214,7 +147,7 @@ mod cfg { /// The [panic strategy](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#panic). #[track_caller] pub fn cargo_cfg_panic() -> String { - get_str("CARGO_CFG_PANIC") + to_string(var_or_panic("CARGO_CFG_PANIC")) } /// If the crate is being compiled as a procedural macro. @@ -228,7 +161,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_relocation_model() -> String { - get_str("CARGO_CFG_RELOCATION_MODEL") + to_string(var_or_panic("CARGO_CFG_RELOCATION_MODEL")) } #[cfg(any())] @@ -242,8 +175,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_sanitize() -> Option> { - let (_, val) = get_opt_cfg("CARGO_CFG_SANITIZE"); - val + var_os("CARGO_CFG_SANITIZE").map(|v| to_strings(v, ',')) } /// If CFI sanitization is generalizing pointers. @@ -270,20 +202,20 @@ mod cfg { /// this value will be empty. #[track_caller] pub fn cargo_cfg_target_abi() -> String { - get_str("CARGO_CFG_TARGET_ABI") + to_string(var_or_panic("CARGO_CFG_TARGET_ABI")) } /// The CPU [target architecture](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_arch). /// This is similar to the first element of the platform's target triple, but not identical. #[track_caller] pub fn cargo_cfg_target_arch() -> String { - get_str("CARGO_CFG_TARGET_ARCH") + to_string(var_or_panic("CARGO_CFG_TARGET_ARCH")) } /// The CPU [target endianness](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_endian). #[track_caller] pub fn cargo_cfg_target_endian() -> String { - get_str("CARGO_CFG_TARGET_ENDIAN") + to_string(var_or_panic("CARGO_CFG_TARGET_ENDIAN")) } /// The [target environment](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_env) ABI. @@ -294,25 +226,25 @@ mod cfg { /// this value will be empty. #[track_caller] pub fn cargo_cfg_target_env() -> String { - get_str("CARGO_CFG_TARGET_ENV") + to_string(var_or_panic("CARGO_CFG_TARGET_ENV")) } /// The [target family](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_family). #[track_caller] pub fn cargo_target_family() -> Vec { - get_cfg("target_family") + to_strings(var_or_panic(&cargo_cfg_var("target_family")), ',') } /// List of CPU [target features](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_feature) enabled. #[track_caller] pub fn cargo_cfg_target_feature() -> Vec { - get_cfg("target_feature") + to_strings(var_or_panic(&cargo_cfg_var("target_feature")), ',') } /// List of CPU [supported atomic widths](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_has_atomic). #[track_caller] pub fn cargo_cfg_target_has_atomic() -> Vec { - get_cfg("target_has_atomic") + to_strings(var_or_panic(&cargo_cfg_var("target_has_atomic")), ',') } /// List of atomic widths that have equal alignment requirements. @@ -320,7 +252,10 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_target_has_atomic_equal_alignment() -> Vec { - get_cfg("target_has_atomic_equal_alignment") + to_strings( + var_or_panic(&cargo_cfg_var("target_has_atomic_equal_alignment")), + ',', + ) } /// List of atomic widths that have atomic load and store operations. @@ -328,20 +263,23 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_target_has_atomic_load_store() -> Vec { - get_cfg("target_has_atomic_load_store") + to_strings( + var_or_panic(&cargo_cfg_var("target_has_atomic_load_store")), + ',', + ) } /// The [target operating system](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_os). /// This value is similar to the second and third element of the platform's target triple. #[track_caller] pub fn cargo_cfg_target_os() -> String { - get_str("CARGO_CFG_TARGET_OS") + to_string(var_or_panic("CARGO_CFG_TARGET_OS")) } /// The CPU [pointer width](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_pointer_width). #[track_caller] pub fn cargo_cfg_target_pointer_width() -> u32 { - get_num("CARGO_CFG_TARGET_POINTER_WIDTH") + to_parsed(var_or_panic("CARGO_CFG_TARGET_POINTER_WIDTH")) } /// If the target supports thread-local storage. @@ -355,7 +293,7 @@ mod cfg { /// The [target vendor](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_vendor). #[track_caller] pub fn cargo_cfg_target_vendor() -> String { - get_str("CARGO_CFG_TARGET_VENDOR") + to_string(var_or_panic("CARGO_CFG_TARGET_VENDOR")) } #[cfg(any())] @@ -391,7 +329,7 @@ mod cfg { /// it is unique for the package in question. #[track_caller] pub fn out_dir() -> PathBuf { - get_path("OUT_DIR") + to_path(var_or_panic("OUT_DIR")) } /// The [target triple] that is being compiled for. Native code should be compiled @@ -400,13 +338,13 @@ pub fn out_dir() -> PathBuf { /// [target triple]: https://doc.rust-lang.org/stable/cargo/appendix/glossary.html#target #[track_caller] pub fn target() -> String { - get_str("TARGET") + to_string(var_or_panic("TARGET")) } /// The host triple of the Rust compiler. #[track_caller] pub fn host() -> String { - get_str("HOST") + to_string(var_or_panic("HOST")) } /// The parallelism specified as the top-level parallelism. @@ -421,19 +359,19 @@ pub fn host() -> String { /// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html #[track_caller] pub fn num_jobs() -> u32 { - get_num("NUM_JOBS") + to_parsed(var_or_panic("NUM_JOBS")) } /// The [level of optimization](https://doc.rust-lang.org/stable/cargo/reference/profiles.html#opt-level). #[track_caller] pub fn opt_level() -> String { - get_str("OPT_LEVEL") + to_string(var_or_panic("OPT_LEVEL")) } /// The amount of [debug information](https://doc.rust-lang.org/stable/cargo/reference/profiles.html#debug) included. #[track_caller] pub fn debug() -> String { - get_str("DEBUG") + to_string(var_or_panic("DEBUG")) } /// `release` for release builds, `debug` for other builds. @@ -448,7 +386,7 @@ pub fn debug() -> String { /// [`release`]: https://doc.rust-lang.org/stable/cargo/reference/profiles.html#release #[track_caller] pub fn profile() -> String { - get_str("PROFILE") + to_string(var_or_panic("PROFILE")) } /// [Metadata] set by dependencies. For more information, see build script @@ -468,19 +406,19 @@ pub fn dep_metadata(name: &str, key: &str) -> Option { let name = name.to_uppercase().replace('-', "_"); let key = key.to_uppercase().replace('-', "_"); let key = format!("DEP_{name}_{key}"); - get_opt_str(&key) + var_os(&key).map(to_string) } /// The compiler that Cargo has resolved to use. #[track_caller] pub fn rustc() -> PathBuf { - get_path("RUSTC") + to_path(var_or_panic("RUSTC")) } /// The documentation generator that Cargo has resolved to use. #[track_caller] pub fn rustdoc() -> PathBuf { - get_path("RUSTDOC") + to_path(var_or_panic("RUSTDOC")) } /// The rustc wrapper, if any, that Cargo is using. See [`build.rustc-wrapper`]. @@ -488,7 +426,7 @@ pub fn rustdoc() -> PathBuf { /// [`build.rustc-wrapper`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustc-wrapper #[track_caller] pub fn rustc_wrapper() -> Option { - get_opt_path("RUSTC_WRAPPER") + var_os("RUSTC_WRAPPER").map(to_path) } /// The rustc wrapper, if any, that Cargo is using for workspace members. See @@ -497,7 +435,7 @@ pub fn rustc_wrapper() -> Option { /// [`build.rustc-workspace-wrapper`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustc-workspace-wrapper #[track_caller] pub fn rustc_workspace_wrapper() -> Option { - get_opt_path("RUSTC_WORKSPACE_WRAPPER") + var_os("RUSTC_WORKSPACE_WRAPPER").map(to_path) } /// The linker that Cargo has resolved to use for the current target, if specified. @@ -505,7 +443,7 @@ pub fn rustc_workspace_wrapper() -> Option { /// [`target.*.linker`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#targettriplelinker #[track_caller] pub fn rustc_linker() -> Option { - get_opt_path("RUSTC_LINKER") + var_os("RUSTC_LINKER").map(to_path) } /// Extra flags that Cargo invokes rustc with. See [`build.rustflags`]. @@ -513,96 +451,135 @@ pub fn rustc_linker() -> Option { /// [`build.rustflags`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustflags #[track_caller] pub fn cargo_encoded_rustflags() -> Vec { - get_str("CARGO_ENCODED_RUSTFLAGS") - .split('\x1f') - .map(str::to_owned) - .collect() + to_strings(var_or_panic("CARGO_ENCODED_RUSTFLAGS"), '\x1f') } /// The full version of your package. #[track_caller] pub fn cargo_pkg_version() -> String { - get_str("CARGO_PKG_VERSION") + to_string(var_or_panic("CARGO_PKG_VERSION")) } /// The major version of your package. #[track_caller] pub fn cargo_pkg_version_major() -> u64 { - get_num("CARGO_PKG_VERSION_MAJOR") + to_parsed(var_or_panic("CARGO_PKG_VERSION_MAJOR")) } /// The minor version of your package. #[track_caller] pub fn cargo_pkg_version_minor() -> u64 { - get_num("CARGO_PKG_VERSION_MINOR") + to_parsed(var_or_panic("CARGO_PKG_VERSION_MINOR")) } /// The patch version of your package. #[track_caller] pub fn cargo_pkg_version_patch() -> u64 { - get_num("CARGO_PKG_VERSION_PATCH") + to_parsed(var_or_panic("CARGO_PKG_VERSION_PATCH")) } /// The pre-release version of your package. #[track_caller] pub fn cargo_pkg_version_pre() -> String { - get_str("CARGO_PKG_VERSION_PRE") + to_string(var_or_panic("CARGO_PKG_VERSION_PRE")) } /// Colon separated list of authors from the manifest of your package. #[track_caller] pub fn cargo_pkg_authors() -> Vec { - get_str("CARGO_PKG_AUTHORS") - .split(':') - .map(str::to_owned) - .collect() + to_strings(var_or_panic("CARGO_PKG_AUTHORS"), ':') } /// The name of your package. #[track_caller] pub fn cargo_pkg_name() -> String { - get_str("CARGO_PKG_NAME") + to_string(var_or_panic("CARGO_PKG_NAME")) } /// The description from the manifest of your package. #[track_caller] pub fn cargo_pkg_description() -> String { - get_str("CARGO_PKG_DESCRIPTION") + to_string(var_or_panic("CARGO_PKG_DESCRIPTION")) } /// The home page from the manifest of your package. #[track_caller] pub fn cargo_pkg_homepage() -> String { - get_str("CARGO_PKG_HOMEPAGE") + to_string(var_or_panic("CARGO_PKG_HOMEPAGE")) } /// The repository from the manifest of your package. #[track_caller] pub fn cargo_pkg_repository() -> String { - get_str("CARGO_PKG_REPOSITORY") + to_string(var_or_panic("CARGO_PKG_REPOSITORY")) } /// The license from the manifest of your package. #[track_caller] pub fn cargo_pkg_license() -> String { - get_str("CARGO_PKG_LICENSE") + to_string(var_or_panic("CARGO_PKG_LICENSE")) } /// The license file from the manifest of your package. #[track_caller] pub fn cargo_pkg_license_file() -> PathBuf { - get_path("CARGO_PKG_LICENSE_FILE") + to_path(var_or_panic("CARGO_PKG_LICENSE_FILE")) } /// The Rust version from the manifest of your package. Note that this is the /// minimum Rust version supported by the package, not the current Rust version. #[track_caller] pub fn cargo_pkg_rust_version() -> String { - get_str("CARGO_PKG_RUST_VERSION") + to_string(var_or_panic("CARGO_PKG_RUST_VERSION")) } /// Path to the README file of your package. #[track_caller] pub fn cargo_pkg_readme() -> PathBuf { - get_path("CARGO_PKG_README") + to_path(var_or_panic("CARGO_PKG_README")) +} + +fn is_present(key: &str) -> bool { + var_os(key).is_some() +} + +#[track_caller] +fn var_or_panic(key: &str) -> std::ffi::OsString { + var_os(key).unwrap_or_else(|| panic!("cargo environment variable `{key}` is missing")) +} + +fn to_path(value: std::ffi::OsString) -> PathBuf { + PathBuf::from(value) +} + +#[track_caller] +fn to_string(value: std::ffi::OsString) -> String { + match value.into_string() { + Ok(s) => s, + Err(value) => { + let err = std::str::from_utf8(value.as_encoded_bytes()).unwrap_err(); + panic!("{err}") + } + } +} + +#[track_caller] +fn to_strings(value: std::ffi::OsString, sep: char) -> Vec { + let value = to_string(value); + value.split(sep).map(str::to_owned).collect() +} + +#[track_caller] +fn to_parsed(value: std::ffi::OsString) -> T +where + T: std::str::FromStr, + T::Err: std::fmt::Display, +{ + let value = to_string(value); + match value.parse() { + Ok(s) => s, + Err(err) => { + panic!("{err}") + } + } } From 347fa09b1bab20e3d633a08276e16307c2cbf8e9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 13:42:46 -0600 Subject: [PATCH 050/525] perf(build-rs): Remove an allocation --- crates/build-rs/src/allow_use.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/build-rs/src/allow_use.rs b/crates/build-rs/src/allow_use.rs index 15e9787aa4b..c46dbbfc02f 100644 --- a/crates/build-rs/src/allow_use.rs +++ b/crates/build-rs/src/allow_use.rs @@ -25,7 +25,7 @@ fn cargo_version_minor() -> u32 { // > cargo -V # example output // cargo 1.82.0 (8f40fc59f 2024-08-21) - String::from_utf8(out.stdout).expect("`cargo -V` should output valid UTF-8") + std::str::from_utf8(&out.stdout).expect("`cargo -V` should output valid UTF-8") ["cargo 1.".len()..] .split('.') .next() From 27c577286ea59632da3ca6baff4422654fbfdc57 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 13:44:04 -0600 Subject: [PATCH 051/525] refactor(build-rs): Extract minor version extraction --- crates/build-rs/src/allow_use.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/crates/build-rs/src/allow_use.rs b/crates/build-rs/src/allow_use.rs index c46dbbfc02f..06220e388a1 100644 --- a/crates/build-rs/src/allow_use.rs +++ b/crates/build-rs/src/allow_use.rs @@ -3,13 +3,9 @@ use std::{process::Command, sync::OnceLock}; fn rust_version_minor() -> u32 { static VERSION_MINOR: OnceLock = OnceLock::new(); *VERSION_MINOR.get_or_init(|| { - crate::input::cargo_pkg_rust_version() - .split('.') - .nth(1) + version_minor(&crate::input::cargo_pkg_rust_version()) // assume build-rs's MSRV if none specified for the current package - .unwrap_or(env!("CARGO_PKG_RUST_VERSION").split('.').nth(1).unwrap()) - .parse() - .unwrap() + .unwrap_or_else(|| version_minor(env!("CARGO_PKG_RUST_VERSION")).unwrap()) }) } @@ -25,16 +21,18 @@ fn cargo_version_minor() -> u32 { // > cargo -V # example output // cargo 1.82.0 (8f40fc59f 2024-08-21) - std::str::from_utf8(&out.stdout).expect("`cargo -V` should output valid UTF-8") - ["cargo 1.".len()..] - .split('.') - .next() - .expect("`cargo -V` format should be stable") - .parse() - .unwrap() + let out = std::str::from_utf8(&out.stdout).expect("`cargo -V` should output valid UTF-8"); + let version = out.split(' ').nth(1).unwrap(); + version_minor(version).unwrap() }) } +fn version_minor(version: &str) -> Option { + let minor = version.split('.').nth(1)?; + let minor = minor.parse().unwrap(); + Some(minor) +} + pub(crate) fn double_colon_directives() -> bool { // cargo errors on `cargo::` directives with insufficient package.rust-version rust_version_minor() >= 77 From 4ff0809afde4588f580d80b3d12bd9896b658cb2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 13:49:23 -0600 Subject: [PATCH 052/525] fix(build-rs)!: Prefer None/empty-Vec to empty-String --- crates/build-rs/src/allow_use.rs | 2 +- crates/build-rs/src/input.rs | 47 ++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/crates/build-rs/src/allow_use.rs b/crates/build-rs/src/allow_use.rs index 06220e388a1..7818e722edb 100644 --- a/crates/build-rs/src/allow_use.rs +++ b/crates/build-rs/src/allow_use.rs @@ -3,7 +3,7 @@ use std::{process::Command, sync::OnceLock}; fn rust_version_minor() -> u32 { static VERSION_MINOR: OnceLock = OnceLock::new(); *VERSION_MINOR.get_or_init(|| { - version_minor(&crate::input::cargo_pkg_rust_version()) + version_minor(&crate::input::cargo_pkg_rust_version().unwrap_or_default()) // assume build-rs's MSRV if none specified for the current package .unwrap_or_else(|| version_minor(env!("CARGO_PKG_RUST_VERSION")).unwrap()) }) diff --git a/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs index cd457e073da..94d19302a90 100644 --- a/crates/build-rs/src/input.rs +++ b/crates/build-rs/src/input.rs @@ -197,12 +197,12 @@ mod cfg { /// Disambiguation of the [target ABI](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_abi) /// when the [target env](cargo_cfg_target_env) isn't sufficient. /// - /// For historical reasons, this value is only defined as not the empty-string when + /// For historical reasons, this value is only defined as `Some` when /// actually needed for disambiguation. Thus, for example, on many GNU platforms, - /// this value will be empty. + /// this value will be `None`. #[track_caller] - pub fn cargo_cfg_target_abi() -> String { - to_string(var_or_panic("CARGO_CFG_TARGET_ABI")) + pub fn cargo_cfg_target_abi() -> Option { + to_opt(var_or_panic("CARGO_CFG_TARGET_ABI")).map(to_string) } /// The CPU [target architecture](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_arch). @@ -480,8 +480,8 @@ pub fn cargo_pkg_version_patch() -> u64 { /// The pre-release version of your package. #[track_caller] -pub fn cargo_pkg_version_pre() -> String { - to_string(var_or_panic("CARGO_PKG_VERSION_PRE")) +pub fn cargo_pkg_version_pre() -> Option { + to_opt(var_or_panic("CARGO_PKG_VERSION_PRE")).map(to_string) } /// Colon separated list of authors from the manifest of your package. @@ -498,45 +498,45 @@ pub fn cargo_pkg_name() -> String { /// The description from the manifest of your package. #[track_caller] -pub fn cargo_pkg_description() -> String { - to_string(var_or_panic("CARGO_PKG_DESCRIPTION")) +pub fn cargo_pkg_description() -> Option { + to_opt(var_or_panic("CARGO_PKG_DESCRIPTION")).map(to_string) } /// The home page from the manifest of your package. #[track_caller] -pub fn cargo_pkg_homepage() -> String { - to_string(var_or_panic("CARGO_PKG_HOMEPAGE")) +pub fn cargo_pkg_homepage() -> Option { + to_opt(var_or_panic("CARGO_PKG_HOMEPAGE")).map(to_string) } /// The repository from the manifest of your package. #[track_caller] -pub fn cargo_pkg_repository() -> String { - to_string(var_or_panic("CARGO_PKG_REPOSITORY")) +pub fn cargo_pkg_repository() -> Option { + to_opt(var_or_panic("CARGO_PKG_REPOSITORY")).map(to_string) } /// The license from the manifest of your package. #[track_caller] -pub fn cargo_pkg_license() -> String { - to_string(var_or_panic("CARGO_PKG_LICENSE")) +pub fn cargo_pkg_license() -> Option { + to_opt(var_or_panic("CARGO_PKG_LICENSE")).map(to_string) } /// The license file from the manifest of your package. #[track_caller] -pub fn cargo_pkg_license_file() -> PathBuf { - to_path(var_or_panic("CARGO_PKG_LICENSE_FILE")) +pub fn cargo_pkg_license_file() -> Option { + to_opt(var_or_panic("CARGO_PKG_LICENSE_FILE")).map(to_path) } /// The Rust version from the manifest of your package. Note that this is the /// minimum Rust version supported by the package, not the current Rust version. #[track_caller] -pub fn cargo_pkg_rust_version() -> String { - to_string(var_or_panic("CARGO_PKG_RUST_VERSION")) +pub fn cargo_pkg_rust_version() -> Option { + to_opt(var_or_panic("CARGO_PKG_RUST_VERSION")).map(to_string) } /// Path to the README file of your package. #[track_caller] -pub fn cargo_pkg_readme() -> PathBuf { - to_path(var_or_panic("CARGO_PKG_README")) +pub fn cargo_pkg_readme() -> Option { + to_opt(var_or_panic("CARGO_PKG_README")).map(to_path) } fn is_present(key: &str) -> bool { @@ -563,8 +563,15 @@ fn to_string(value: std::ffi::OsString) -> String { } } +fn to_opt(value: std::ffi::OsString) -> Option { + (!value.is_empty()).then_some(value) +} + #[track_caller] fn to_strings(value: std::ffi::OsString, sep: char) -> Vec { + if value.is_empty() { + return Vec::new(); + } let value = to_string(value); value.split(sep).map(str::to_owned).collect() } From 611ec991fbf4fe5af4714563d0906c1c2aee1981 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 13:54:48 -0600 Subject: [PATCH 053/525] feat(build-rs): Add cargo_manifest_path --- crates/build-rs-test-lib/build.rs | 1 + crates/build-rs/src/input.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/crates/build-rs-test-lib/build.rs b/crates/build-rs-test-lib/build.rs index a6065ef0385..1167a34907e 100644 --- a/crates/build-rs-test-lib/build.rs +++ b/crates/build-rs-test-lib/build.rs @@ -47,6 +47,7 @@ fn smoke_test_inputs() { dbg!(cargo_encoded_rustflags()); dbg!(cargo_feature("unstable")); dbg!(cargo_manifest_dir()); + dbg!(cargo_manifest_path()); dbg!(cargo_manifest_links()); dbg!(cargo_pkg_authors()); dbg!(cargo_pkg_description()); diff --git a/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs index 94d19302a90..93faf140041 100644 --- a/crates/build-rs/src/input.rs +++ b/crates/build-rs/src/input.rs @@ -29,6 +29,18 @@ pub fn cargo_manifest_dir() -> PathBuf { to_path(var_or_panic("CARGO_MANIFEST_DIR")) } +/// The path to the manifest of your package. +#[track_caller] +pub fn cargo_manifest_path() -> PathBuf { + var_os("CARGO_MANIFEST_PATH") + .map(to_path) + .unwrap_or_else(|| { + let mut path = cargo_manifest_dir(); + path.push("Cargo.toml"); + path + }) +} + /// The manifest `links` value. #[track_caller] pub fn cargo_manifest_links() -> Option { From 8b43030bb7b6eb8ad5957ea406418c082ef06634 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 13:55:14 -0600 Subject: [PATCH 054/525] chore(build-rs): Remove comment about update At this point, it is assumed to always be up-to-date --- crates/build-rs/src/input.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs index 93faf140041..51c7c85448a 100644 --- a/crates/build-rs/src/input.rs +++ b/crates/build-rs/src/input.rs @@ -11,8 +11,6 @@ use std::path::PathBuf; use crate::ident::{is_ascii_ident, is_crate_name, is_feature_name}; -// docs last updated to match release 1.82.0 reference - /// Path to the `cargo` binary performing the build. #[track_caller] pub fn cargo() -> PathBuf { From c02c9f2517f34a9850f8e297d48c87d1047a07e2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 14:46:11 -0600 Subject: [PATCH 055/525] docs(build-rs): Remove reference to encoding --- crates/build-rs/src/input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs index 51c7c85448a..6f564f8c68b 100644 --- a/crates/build-rs/src/input.rs +++ b/crates/build-rs/src/input.rs @@ -494,7 +494,7 @@ pub fn cargo_pkg_version_pre() -> Option { to_opt(var_or_panic("CARGO_PKG_VERSION_PRE")).map(to_string) } -/// Colon separated list of authors from the manifest of your package. +/// The authors from the manifest of your package. #[track_caller] pub fn cargo_pkg_authors() -> Vec { to_strings(var_or_panic("CARGO_PKG_AUTHORS"), ':') From f7f2206474d751c3137f35809c26837ab100f509 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 15:22:54 -0600 Subject: [PATCH 056/525] test(remove): Don't use shared fixture --- tests/testsuite/cargo_remove/invalid_dep/in | 1 - .../cargo_remove/invalid_dep/in/Cargo.toml | 25 +++++++++++++++++++ .../cargo_remove/invalid_dep/in/src/lib.rs | 1 + 3 files changed, 26 insertions(+), 1 deletion(-) delete mode 120000 tests/testsuite/cargo_remove/invalid_dep/in create mode 100644 tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/invalid_dep/in/src/lib.rs diff --git a/tests/testsuite/cargo_remove/invalid_dep/in b/tests/testsuite/cargo_remove/invalid_dep/in deleted file mode 120000 index 7fd0ba5ebcc..00000000000 --- a/tests/testsuite/cargo_remove/invalid_dep/in +++ /dev/null @@ -1 +0,0 @@ -../remove-basic.in/ \ No newline at end of file diff --git a/tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml b/tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml new file mode 100644 index 00000000000..fe49a10648d --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" +edition = "2015" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/invalid_dep/in/src/lib.rs b/tests/testsuite/cargo_remove/invalid_dep/in/src/lib.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/testsuite/cargo_remove/invalid_dep/in/src/lib.rs @@ -0,0 +1 @@ + From cad379f3e34b3c7167ff67186cbb55039e6ea857 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 15:24:12 -0600 Subject: [PATCH 057/525] test(remove): Reduce the fixture size --- tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml | 4 ---- tests/testsuite/cargo_remove/invalid_dep/mod.rs | 4 ---- tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml | 4 ---- 3 files changed, 12 deletions(-) diff --git a/tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml b/tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml index fe49a10648d..c38ef4c9d71 100644 --- a/tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml +++ b/tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml @@ -12,13 +12,9 @@ semver = "0.1.0" [dependencies] docopt = "0.6" -rustc-serialize = "0.4" semver = "0.1" -toml = "0.1" -clippy = "0.4" [dev-dependencies] -regex = "0.1.1" serde = "1.0.90" [features] diff --git a/tests/testsuite/cargo_remove/invalid_dep/mod.rs b/tests/testsuite/cargo_remove/invalid_dep/mod.rs index 50eb967f2d4..a563f481f1e 100644 --- a/tests/testsuite/cargo_remove/invalid_dep/mod.rs +++ b/tests/testsuite/cargo_remove/invalid_dep/mod.rs @@ -9,11 +9,7 @@ use cargo_test_support::Project; #[cargo_test] fn case() { cargo_test_support::registry::init(); - cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); - cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); - cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); - cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) .publish(); diff --git a/tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml b/tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml index fe49a10648d..c38ef4c9d71 100644 --- a/tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml +++ b/tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml @@ -12,13 +12,9 @@ semver = "0.1.0" [dependencies] docopt = "0.6" -rustc-serialize = "0.4" semver = "0.1" -toml = "0.1" -clippy = "0.4" [dev-dependencies] -regex = "0.1.1" serde = "1.0.90" [features] From 4da1b2ee7892bccf6a0fad8d5643e96f05195c80 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 15:25:15 -0600 Subject: [PATCH 058/525] test(remove): Update fixture for a suggestion --- tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml | 1 + tests/testsuite/cargo_remove/invalid_dep/mod.rs | 2 ++ tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml | 1 + 3 files changed, 4 insertions(+) diff --git a/tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml b/tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml index c38ef4c9d71..1a373b7dae4 100644 --- a/tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml +++ b/tests/testsuite/cargo_remove/invalid_dep/in/Cargo.toml @@ -11,6 +11,7 @@ path = "src/main.rs" semver = "0.1.0" [dependencies] +invalid-dependency-name = "0.6" docopt = "0.6" semver = "0.1" diff --git a/tests/testsuite/cargo_remove/invalid_dep/mod.rs b/tests/testsuite/cargo_remove/invalid_dep/mod.rs index a563f481f1e..40da2984207 100644 --- a/tests/testsuite/cargo_remove/invalid_dep/mod.rs +++ b/tests/testsuite/cargo_remove/invalid_dep/mod.rs @@ -9,6 +9,8 @@ use cargo_test_support::Project; #[cargo_test] fn case() { cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("invalid-dependency-name", "0.6.2+my-package") + .publish(); cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); cargo_test_support::registry::Package::new("semver", "0.1.1") .feature("std", &[]) diff --git a/tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml b/tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml index c38ef4c9d71..1a373b7dae4 100644 --- a/tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml +++ b/tests/testsuite/cargo_remove/invalid_dep/out/Cargo.toml @@ -11,6 +11,7 @@ path = "src/main.rs" semver = "0.1.0" [dependencies] +invalid-dependency-name = "0.6" docopt = "0.6" semver = "0.1" From 33d1361bb88ae974733c16aaca977f59a958bca5 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 13 Nov 2024 15:26:11 -0600 Subject: [PATCH 059/525] fix(remove): On error, suggest other dependencies `cargo remove` already will suggest other tables if appropriate. `cargo add` auto-corrects `_` and `-`. This is an extension of the two by suggesting existing dependencies within the same table that are "close". I chose to make alt tables and alt deps mutually exclusive in the message because I didn't wantto deal with how to separate things if both show up. Most likely, people will only expect one or the other and if its in an alt table, then most likely they mean that. Related to #14814 --- src/cargo/util/toml_mut/manifest.rs | 12 ++++++++++++ .../cargo_remove/invalid_dep/stderr.term.svg | 4 ++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/cargo/util/toml_mut/manifest.rs b/src/cargo/util/toml_mut/manifest.rs index e166a24f2be..98033531e3a 100644 --- a/src/cargo/util/toml_mut/manifest.rs +++ b/src/cargo/util/toml_mut/manifest.rs @@ -9,6 +9,7 @@ use anyhow::Context as _; use super::dependency::Dependency; use crate::core::dependency::DepKind; use crate::core::{FeatureValue, Features, Workspace}; +use crate::util::closest; use crate::util::interning::InternedString; use crate::{CargoResult, GlobalContext}; @@ -381,6 +382,13 @@ impl LocalManifest { } } None => { + let names = parent_table + .as_table_like() + .map(|t| t.iter()) + .into_iter() + .flatten(); + let alt_name = closest(name, names.map(|(k, _)| k), |k| k).map(|n| n.to_owned()); + // Search in other tables. let sections = self.get_sections(); let found_table_path = sections.iter().find_map(|(t, i)| { @@ -393,6 +401,7 @@ impl LocalManifest { name, table_path.join("."), found_table_path, + alt_name.as_deref(), )); } } @@ -605,10 +614,13 @@ fn non_existent_dependency_err( name: impl std::fmt::Display, search_table: impl std::fmt::Display, found_table: Option, + alt_name: Option<&str>, ) -> anyhow::Error { let mut msg = format!("the dependency `{name}` could not be found in `{search_table}`"); if let Some(found_table) = found_table { msg.push_str(&format!("; it is present in `{found_table}`",)); + } else if let Some(alt_name) = alt_name { + msg.push_str(&format!("; dependency `{alt_name}` exists",)); } anyhow::format_err!(msg) } diff --git a/tests/testsuite/cargo_remove/invalid_dep/stderr.term.svg b/tests/testsuite/cargo_remove/invalid_dep/stderr.term.svg index 3b7fb2b807d..5eecfe5e7da 100644 --- a/tests/testsuite/cargo_remove/invalid_dep/stderr.term.svg +++ b/tests/testsuite/cargo_remove/invalid_dep/stderr.term.svg @@ -1,4 +1,4 @@ - +
--edition edition
-
Specify the Rust edition to use. Default is 2021. +
Specify the Rust edition to use. Default is 2024. Possible values: 2015, 2018, 2021, 2024
diff --git a/src/doc/src/commands/cargo-new.md b/src/doc/src/commands/cargo-new.md index e10e2dcb9b1..8aed7fdf1f5 100644 --- a/src/doc/src/commands/cargo-new.md +++ b/src/doc/src/commands/cargo-new.md @@ -34,7 +34,7 @@ This is the default behavior.
--edition edition
-
Specify the Rust edition to use. Default is 2021. +
Specify the Rust edition to use. Default is 2024. Possible values: 2015, 2018, 2021, 2024
diff --git a/src/doc/src/getting-started/first-steps.md b/src/doc/src/getting-started/first-steps.md index e8ed21d1571..86f8b5f14b7 100644 --- a/src/doc/src/getting-started/first-steps.md +++ b/src/doc/src/getting-started/first-steps.md @@ -33,7 +33,7 @@ This is all we need to get started. First, let’s check out `Cargo.toml`: [package] name = "hello_world" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] ``` diff --git a/src/doc/src/guide/creating-a-new-project.md b/src/doc/src/guide/creating-a-new-project.md index f12fb865025..470b8bde41b 100644 --- a/src/doc/src/guide/creating-a-new-project.md +++ b/src/doc/src/guide/creating-a-new-project.md @@ -29,7 +29,7 @@ Let’s take a closer look at `Cargo.toml`: [package] name = "hello_world" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/src/doc/src/guide/dependencies.md b/src/doc/src/guide/dependencies.md index 531fe505b23..5f34b9fe04c 100644 --- a/src/doc/src/guide/dependencies.md +++ b/src/doc/src/guide/dependencies.md @@ -35,7 +35,7 @@ crates: [package] name = "hello_world" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] time = "0.1.12" diff --git a/src/doc/src/reference/build-script-examples.md b/src/doc/src/reference/build-script-examples.md index 9b7464134ce..014579d1f37 100644 --- a/src/doc/src/reference/build-script-examples.md +++ b/src/doc/src/reference/build-script-examples.md @@ -49,7 +49,7 @@ Here we can see that we have a `build.rs` build script and our binary in [package] name = "hello-from-generated-code" version = "0.1.0" -edition = "2021" +edition = "2024" ``` Let’s see what’s inside the build script: @@ -148,7 +148,7 @@ Pretty similar to before! Next, the manifest: [package] name = "hello-world-from-c" version = "0.1.0" -edition = "2021" +edition = "2024" ``` For now we’re not going to use any build dependencies, so let’s take a look at @@ -298,7 +298,7 @@ with `pkg-config` installed. Let's start by setting up the manifest: [package] name = "libz-sys" version = "0.1.0" -edition = "2021" +edition = "2024" links = "z" [build-dependencies] @@ -385,7 +385,7 @@ Here's an example: [package] name = "zuser" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] libz-sys = "1.0.25" diff --git a/src/doc/src/reference/manifest.md b/src/doc/src/reference/manifest.md index 95de8286e4f..5026c9f40a3 100644 --- a/src/doc/src/reference/manifest.md +++ b/src/doc/src/reference/manifest.md @@ -151,12 +151,12 @@ examples, etc. ```toml [package] # ... -edition = '2021' +edition = '2024' ``` Most manifests have the `edition` field filled in automatically by [`cargo new`] with the latest stable edition. By default `cargo new` creates a manifest with -the 2021 edition currently. +the 2024 edition currently. If the `edition` field is not present in `Cargo.toml`, then the 2015 edition is assumed for backwards compatibility. Note that all manifests diff --git a/src/doc/src/reference/registries.md b/src/doc/src/reference/registries.md index f0554be980e..12c91f1ae2a 100644 --- a/src/doc/src/reference/registries.md +++ b/src/doc/src/reference/registries.md @@ -38,7 +38,7 @@ in `Cargo.toml`: [package] name = "my-project" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] other-crate = { version = "1.0", registry = "my-registry" } diff --git a/src/doc/src/reference/resolver.md b/src/doc/src/reference/resolver.md index bc41517b21f..f080509fc58 100644 --- a/src/doc/src/reference/resolver.md +++ b/src/doc/src/reference/resolver.md @@ -507,7 +507,7 @@ resolver = "2" - `"2"` ([`edition = "2021"`](manifest.md#the-edition-field) default): Introduces changes in [feature unification](#features). See the [features chapter][features-2] for more details. -- `"3"` (requires Rust 1.84+): Change the default for [`resolver.incompatible-rust-versions`] from `allow` to `fallback` +- `"3"` ([`edition = "2024"`](manifest.md#the-edition-field) default, requires Rust 1.84+): Change the default for [`resolver.incompatible-rust-versions`] from `allow` to `fallback` The resolver is a global option that affects the entire workspace. The `resolver` version in dependencies is ignored, only the value in the top-level diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index c59600a5d5e..7dcf79dc7b1 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -100,7 +100,6 @@ Each new feature described below should explain how to use it. * [codegen-backend](#codegen-backend) --- Select the codegen backend used by rustc. * [per-package-target](#per-package-target) --- Sets the `--target` to use for each individual package. * [artifact dependencies](#artifact-dependencies) --- Allow build artifacts to be included into other build artifacts and build them for different targets. - * [Edition 2024](#edition-2024) — Adds support for the 2024 Edition. * [Profile `trim-paths` option](#profile-trim-paths-option) --- Control the sanitization of file paths in build outputs. * [`[lints.cargo]`](#lintscargo) --- Allows configuring lints for Cargo. * [path bases](#path-bases) --- Named base directories for path dependencies. @@ -1363,32 +1362,6 @@ Differences between `cargo run --manifest-path ` and `cargo ` ### Documentation Updates -## Edition 2024 -* Tracking Issue: (none created yet) -* RFC: [rust-lang/rfcs#3501](https://github.com/rust-lang/rfcs/pull/3501) - -Support for the 2024 [edition] can be enabled by adding the `edition2024` -unstable feature to the top of `Cargo.toml`: - -```toml -cargo-features = ["edition2024"] - -[package] -name = "my-package" -version = "0.1.0" -edition = "2024" -``` - -If you want to transition an existing project from a previous edition, then -`cargo fix --edition` can be used on the nightly channel. After running `cargo -fix`, you can switch the edition to 2024 as illustrated above. - -This feature is very unstable, and is only intended for early testing and -experimentation. Future nightly releases may introduce changes for the 2024 -edition that may break your build. - -[edition]: ../../edition-guide/index.html - ## Profile `trim-paths` option * Tracking Issue: [rust-lang/cargo#12137](https://github.com/rust-lang/cargo/issues/12137) @@ -2013,3 +1986,10 @@ default behavior. See the [build script documentation](build-scripts.md#rustc-check-cfg) for information about specifying custom cfgs. + +## Edition 2024 + +The 2024 edition has been stabilized in the 1.85 release. +See the [`edition` field](manifest.md#the-edition-field) for more information on setting the edition. +See [`cargo fix --edition`](../commands/cargo-fix.md) and [The Edition Guide](../../edition-guide/index.html) for more information on migrating existing projects. + diff --git a/src/doc/src/reference/workspaces.md b/src/doc/src/reference/workspaces.md index 4cbf212952d..ddbe36fd1ad 100644 --- a/src/doc/src/reference/workspaces.md +++ b/src/doc/src/reference/workspaces.md @@ -76,7 +76,7 @@ resolver = "2" [package] name = "hello_world" # the name of the package version = "0.1.0" # the current version, obeying semver -edition = "2021" # the edition, will have no effect on a resolver used in the workspace +edition = "2024" # the edition, will have no effect on a resolver used in the workspace authors = ["Alice ", "Bob "] ``` diff --git a/src/etc/man/cargo-init.1 b/src/etc/man/cargo-init.1 index b37622b528b..74f02ef09fe 100644 --- a/src/etc/man/cargo-init.1 +++ b/src/etc/man/cargo-init.1 @@ -36,7 +36,7 @@ Create a package with a library target (\fBsrc/lib.rs\fR). .sp \fB\-\-edition\fR \fIedition\fR .RS 4 -Specify the Rust edition to use. Default is 2021. +Specify the Rust edition to use. Default is 2024. Possible values: 2015, 2018, 2021, 2024 .RE .sp diff --git a/src/etc/man/cargo-new.1 b/src/etc/man/cargo-new.1 index f1939a54368..87a1c7f1c7d 100644 --- a/src/etc/man/cargo-new.1 +++ b/src/etc/man/cargo-new.1 @@ -31,7 +31,7 @@ Create a package with a library target (\fBsrc/lib.rs\fR). .sp \fB\-\-edition\fR \fIedition\fR .RS 4 -Specify the Rust edition to use. Default is 2021. +Specify the Rust edition to use. Default is 2024. Possible values: 2015, 2018, 2021, 2024 .RE .sp diff --git a/tests/testsuite/bad_config.rs b/tests/testsuite/bad_config.rs index ace08f4392f..bf1b0da2a34 100644 --- a/tests/testsuite/bad_config.rs +++ b/tests/testsuite/bad_config.rs @@ -831,14 +831,12 @@ fn dev_dependencies2() { "#]]).run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn dev_dependencies2_2024() { let p = project() .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "foo" version = "0.1.0" @@ -861,7 +859,6 @@ fn dev_dependencies2_2024() { .file("a/src/lib.rs", "") .build(); p.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` @@ -949,14 +946,12 @@ fn build_dependencies2() { "#]]).run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn build_dependencies2_2024() { let p = project() .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "foo" version = "0.1.0" @@ -979,7 +974,6 @@ fn build_dependencies2_2024() { .file("a/src/lib.rs", "") .build(); p.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` @@ -1060,14 +1054,12 @@ fn lib_crate_type2() { .run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn lib_crate_type2_2024() { let p = project() .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "foo" version = "0.5.0" @@ -1082,7 +1074,6 @@ fn lib_crate_type2_2024() { .file("src/lib.rs", "pub fn foo() {}") .build(); p.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` @@ -1154,14 +1145,12 @@ fn bin_crate_type2() { .run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn bin_crate_type2_2024() { let p = project() .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "foo" version = "0.5.0" @@ -1177,7 +1166,6 @@ fn bin_crate_type2_2024() { .file("src/main.rs", "fn main() {}") .build(); p.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` @@ -1268,14 +1256,12 @@ fn examples_crate_type2() { .run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn examples_crate_type2_2024() { let p = project() .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "foo" version = "0.5.0" @@ -1307,7 +1293,6 @@ fn examples_crate_type2_2024() { ) .build(); p.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` @@ -1411,7 +1396,7 @@ fn cargo_platform_build_dependencies2() { .run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn cargo_platform_build_dependencies2_2024() { let host = rustc_host(); let p = project() @@ -1419,8 +1404,6 @@ fn cargo_platform_build_dependencies2_2024() { "Cargo.toml", &format!( r#" - cargo-features = ["edition2024"] - [package] name = "foo" version = "0.5.0" @@ -1444,7 +1427,6 @@ fn cargo_platform_build_dependencies2_2024() { .build(); p.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` @@ -1543,7 +1525,7 @@ fn cargo_platform_dev_dependencies2() { .run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn cargo_platform_dev_dependencies2_2024() { let host = rustc_host(); let p = project() @@ -1551,8 +1533,6 @@ fn cargo_platform_dev_dependencies2_2024() { "Cargo.toml", &format!( r#" - cargo-features = ["edition2024"] - [package] name = "foo" version = "0.5.0" @@ -1575,7 +1555,6 @@ fn cargo_platform_dev_dependencies2_2024() { .build(); p.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` @@ -1675,14 +1654,12 @@ fn default_features2() { "#]]).run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn default_features2_2024() { let p = project() .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "foo" version = "0.1.0" @@ -1712,7 +1689,6 @@ fn default_features2_2024() { .build(); p.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` @@ -1852,7 +1828,7 @@ fn workspace_default_features2() { .run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn workspace_default_features2_2024() { let p = project() .file( @@ -1869,8 +1845,6 @@ fn workspace_default_features2_2024() { .file( "workspace_only/Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "workspace_only" version = "0.1.0" @@ -1885,8 +1859,6 @@ fn workspace_default_features2_2024() { .file( "dep_workspace_only/Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "dep_workspace_only" version = "0.1.0" @@ -1898,8 +1870,6 @@ fn workspace_default_features2_2024() { .file( "package_only/Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "package_only" version = "0.1.0" @@ -1914,8 +1884,6 @@ fn workspace_default_features2_2024() { .file( "dep_package_only/Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "dep_package_only" version = "0.1.0" @@ -1927,7 +1895,6 @@ fn workspace_default_features2_2024() { .build(); p.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to load manifest for workspace member `[ROOT]/foo/workspace_only` @@ -1972,14 +1939,12 @@ fn lib_proc_macro2() { .run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn lib_proc_macro2_2024() { let foo = project() .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "foo" version = "0.1.0" @@ -1992,7 +1957,6 @@ fn lib_proc_macro2_2024() { .build(); foo.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` @@ -2063,14 +2027,12 @@ fn bin_proc_macro2() { .run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn bin_proc_macro2_2024() { let foo = project() .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "foo" version = "0.5.0" @@ -2087,7 +2049,6 @@ fn bin_proc_macro2_2024() { .build(); foo.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` diff --git a/tests/testsuite/cargo_init/auto_git/out/Cargo.toml b/tests/testsuite/cargo_init/auto_git/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/auto_git/out/Cargo.toml +++ b/tests/testsuite/cargo_init/auto_git/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/bin_already_exists_explicit/out/Cargo.toml b/tests/testsuite/cargo_init/bin_already_exists_explicit/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/bin_already_exists_explicit/out/Cargo.toml +++ b/tests/testsuite/cargo_init/bin_already_exists_explicit/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/out/Cargo.toml b/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/out/Cargo.toml index 2a5dcadee6c..d0ebc5d6b30 100644 --- a/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/out/Cargo.toml +++ b/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/out/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/bin_already_exists_implicit/out/Cargo.toml b/tests/testsuite/cargo_init/bin_already_exists_implicit/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/bin_already_exists_implicit/out/Cargo.toml +++ b/tests/testsuite/cargo_init/bin_already_exists_implicit/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/out/Cargo.toml b/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/out/Cargo.toml index a16d0374d2e..fc26d6b815d 100644 --- a/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/out/Cargo.toml +++ b/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/out/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/out/Cargo.toml b/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/out/Cargo.toml index c318f457a3b..18e8d4eef01 100644 --- a/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/out/Cargo.toml +++ b/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/out/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/out/Cargo.toml b/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/out/Cargo.toml index 2a5dcadee6c..d0ebc5d6b30 100644 --- a/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/out/Cargo.toml +++ b/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/out/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/Cargo.toml b/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/Cargo.toml index 7b042d4bb13..5add2f88f26 100644 --- a/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/Cargo.toml +++ b/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml b/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml index a16d0374d2e..fc26d6b815d 100644 --- a/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml +++ b/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml b/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml index 6fa93832300..51e0c792066 100644 --- a/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml +++ b/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/explicit_bin_with_git/out/Cargo.toml b/tests/testsuite/cargo_init/explicit_bin_with_git/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/explicit_bin_with_git/out/Cargo.toml +++ b/tests/testsuite/cargo_init/explicit_bin_with_git/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/formats_source/out/Cargo.toml b/tests/testsuite/cargo_init/formats_source/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/formats_source/out/Cargo.toml +++ b/tests/testsuite/cargo_init/formats_source/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/fossil_autodetect/out/Cargo.toml b/tests/testsuite/cargo_init/fossil_autodetect/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/fossil_autodetect/out/Cargo.toml +++ b/tests/testsuite/cargo_init/fossil_autodetect/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/git_autodetect/out/Cargo.toml b/tests/testsuite/cargo_init/git_autodetect/out/Cargo.toml index 878c6729147..05126080007 100644 --- a/tests/testsuite/cargo_init/git_autodetect/out/Cargo.toml +++ b/tests/testsuite/cargo_init/git_autodetect/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "foo" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/Cargo.toml b/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/Cargo.toml +++ b/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/inferred_bin_with_git/out/Cargo.toml b/tests/testsuite/cargo_init/inferred_bin_with_git/out/Cargo.toml index 2a5dcadee6c..d0ebc5d6b30 100644 --- a/tests/testsuite/cargo_init/inferred_bin_with_git/out/Cargo.toml +++ b/tests/testsuite/cargo_init/inferred_bin_with_git/out/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/inferred_lib_with_git/out/Cargo.toml b/tests/testsuite/cargo_init/inferred_lib_with_git/out/Cargo.toml index 290552e56b5..2feac72d2b9 100644 --- a/tests/testsuite/cargo_init/inferred_lib_with_git/out/Cargo.toml +++ b/tests/testsuite/cargo_init/inferred_lib_with_git/out/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/lib_already_exists_nosrc/out/Cargo.toml b/tests/testsuite/cargo_init/lib_already_exists_nosrc/out/Cargo.toml index 290552e56b5..2feac72d2b9 100644 --- a/tests/testsuite/cargo_init/lib_already_exists_nosrc/out/Cargo.toml +++ b/tests/testsuite/cargo_init/lib_already_exists_nosrc/out/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/lib_already_exists_src/out/Cargo.toml b/tests/testsuite/cargo_init/lib_already_exists_src/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/lib_already_exists_src/out/Cargo.toml +++ b/tests/testsuite/cargo_init/lib_already_exists_src/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/mercurial_autodetect/out/Cargo.toml b/tests/testsuite/cargo_init/mercurial_autodetect/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/mercurial_autodetect/out/Cargo.toml +++ b/tests/testsuite/cargo_init/mercurial_autodetect/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/pijul_autodetect/out/Cargo.toml b/tests/testsuite/cargo_init/pijul_autodetect/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/pijul_autodetect/out/Cargo.toml +++ b/tests/testsuite/cargo_init/pijul_autodetect/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/simple_git/out/Cargo.toml b/tests/testsuite/cargo_init/simple_git/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/simple_git/out/Cargo.toml +++ b/tests/testsuite/cargo_init/simple_git/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/simple_hg/out/Cargo.toml b/tests/testsuite/cargo_init/simple_hg/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/simple_hg/out/Cargo.toml +++ b/tests/testsuite/cargo_init/simple_hg/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/Cargo.toml b/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/Cargo.toml index dfab9e3fdbe..797f753b430 100644 --- a/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/Cargo.toml +++ b/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "case" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/with_argument/out/foo/Cargo.toml b/tests/testsuite/cargo_init/with_argument/out/foo/Cargo.toml index 878c6729147..05126080007 100644 --- a/tests/testsuite/cargo_init/with_argument/out/foo/Cargo.toml +++ b/tests/testsuite/cargo_init/with_argument/out/foo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "foo" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/Cargo.toml b/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/Cargo.toml index 878c6729147..05126080007 100644 --- a/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/Cargo.toml +++ b/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "foo" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_new/add_members_to_non_workspace/out/bar/Cargo.toml b/tests/testsuite/cargo_new/add_members_to_non_workspace/out/bar/Cargo.toml index fe23452f35a..845311fc806 100644 --- a/tests/testsuite/cargo_new/add_members_to_non_workspace/out/bar/Cargo.toml +++ b/tests/testsuite/cargo_new/add_members_to_non_workspace/out/bar/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bar" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/Cargo.toml b/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/Cargo.toml index d4c52492114..956aa0f1800 100644 --- a/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/Cargo.toml +++ b/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "foo" version = "0.1.0" -edition = "2021" +edition = "2024" authors.workspace = true [dependencies] diff --git a/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/Cargo.toml b/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/Cargo.toml index 878c6729147..05126080007 100644 --- a/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/Cargo.toml +++ b/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "foo" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/Cargo.toml b/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/Cargo.toml index 878c6729147..05126080007 100644 --- a/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/Cargo.toml +++ b/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "foo" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/Cargo.toml b/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/Cargo.toml index 878c6729147..05126080007 100644 --- a/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/Cargo.toml +++ b/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "foo" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/Cargo.toml b/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/Cargo.toml index 878c6729147..05126080007 100644 --- a/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/Cargo.toml +++ b/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "foo" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/Cargo.toml b/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/Cargo.toml index 878c6729147..05126080007 100644 --- a/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/Cargo.toml +++ b/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "foo" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_new/add_members_to_workspace_without_members/out/bar/Cargo.toml b/tests/testsuite/cargo_new/add_members_to_workspace_without_members/out/bar/Cargo.toml index fe23452f35a..845311fc806 100644 --- a/tests/testsuite/cargo_new/add_members_to_workspace_without_members/out/bar/Cargo.toml +++ b/tests/testsuite/cargo_new/add_members_to_workspace_without_members/out/bar/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bar" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_new/ignore_current_dir_workspace/out/out-of-workspace/Cargo.toml b/tests/testsuite/cargo_new/ignore_current_dir_workspace/out/out-of-workspace/Cargo.toml index e0fde467294..c7dc68a9f37 100644 --- a/tests/testsuite/cargo_new/ignore_current_dir_workspace/out/out-of-workspace/Cargo.toml +++ b/tests/testsuite/cargo_new/ignore_current_dir_workspace/out/out-of-workspace/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "out-of-workspace" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/Cargo.toml b/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/Cargo.toml index 836294e0da7..e5e0f22462c 100644 --- a/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/Cargo.toml +++ b/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "foo" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/bar/Cargo.toml b/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/bar/Cargo.toml index fe23452f35a..845311fc806 100644 --- a/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/bar/Cargo.toml +++ b/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/bar/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bar" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] diff --git a/tests/testsuite/check.rs b/tests/testsuite/check.rs index 36f95258cab..cff23929cf6 100644 --- a/tests/testsuite/check.rs +++ b/tests/testsuite/check.rs @@ -1077,14 +1077,12 @@ fn warn_manifest_with_project() { .run(); } -#[cargo_test(nightly, reason = "edition2024")] +#[cargo_test] fn error_manifest_with_project_on_2024() { let p = project() .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [project] name = "foo" version = "0.0.1" @@ -1095,7 +1093,6 @@ fn error_manifest_with_project_on_2024() { .build(); p.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` diff --git a/tests/testsuite/edition.rs b/tests/testsuite/edition.rs index 92f75b181d4..7b22c2dbda8 100644 --- a/tests/testsuite/edition.rs +++ b/tests/testsuite/edition.rs @@ -135,7 +135,7 @@ fn unset_edition_with_unset_rust_version() { p.cargo("check -v") .with_stderr_data(str![[r#" -[WARNING] no edition set: defaulting to the 2015 edition while the latest is 2021 +[WARNING] no edition set: defaulting to the 2015 edition while the latest is 2024 [CHECKING] foo v0.1.0 ([ROOT]/foo) [RUNNING] `rustc [..] --edition=2015 [..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s diff --git a/tests/testsuite/fix.rs b/tests/testsuite/fix.rs index 15fb306342c..bd47056c0dc 100644 --- a/tests/testsuite/fix.rs +++ b/tests/testsuite/fix.rs @@ -869,7 +869,7 @@ fn prepare_for_already_on_latest_unstable() { .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn prepare_for_already_on_latest_stable() { // Stable counterpart of prepare_for_already_on_latest_unstable. if Edition::LATEST_UNSTABLE.is_some() { @@ -2361,8 +2361,6 @@ fn migrate_project_to_package() { .file( "Cargo.toml", r#" -cargo-features = ["edition2024"] - # Before project [ project ] # After project header # After project header line @@ -2375,7 +2373,6 @@ edition = "2021" .build(); p.cargo("fix --edition --allow-no-vcs") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2021 edition to 2024 [FIXED] Cargo.toml (1 fix) @@ -2389,8 +2386,6 @@ edition = "2021" p.read_file("Cargo.toml"), str![[r#" -cargo-features = ["edition2024"] - # Before project [ package ] # After project header # After project header line @@ -2408,8 +2403,6 @@ fn migrate_removes_project() { .file( "Cargo.toml", r#" -cargo-features = ["edition2024"] - # Before package [ package ] # After package header # After package header line @@ -2429,7 +2422,6 @@ edition = "2021" .build(); p.cargo("fix --edition --allow-no-vcs") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2021 edition to 2024 [FIXED] Cargo.toml (1 fix) @@ -2443,8 +2435,6 @@ edition = "2021" p.read_file("Cargo.toml"), str![[r#" -cargo-features = ["edition2024"] - # Before package [ package ] # After package header # After package header line @@ -2462,8 +2452,6 @@ fn migrate_rename_underscore_fields() { .file( "Cargo.toml", r#" -cargo-features = ["edition2024"] - [workspace.dependencies] # Before default_features a = {path = "a", default_features = false} # After default_features value @@ -2531,7 +2519,6 @@ a = {path = "a", default_features = false} .build(); p.cargo("fix --edition --allow-no-vcs") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stderr_data(str![[r#" [MIGRATING] Cargo.toml from 2021 edition to 2024 [FIXED] Cargo.toml (11 fixes) @@ -2547,8 +2534,6 @@ a = {path = "a", default_features = false} p.read_file("Cargo.toml"), str![[r#" -cargo-features = ["edition2024"] - [workspace.dependencies] # Before default_features a = {path = "a", default-features = false} # After default_features value @@ -2691,7 +2676,6 @@ dep_df_false = { version = "0.1.0", default-features = false } .build(); p.cargo("fix --all --edition --allow-no-vcs") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stderr_data( str![[r#" [MIGRATING] pkg_default/Cargo.toml from 2021 edition to 2024 diff --git a/tests/testsuite/inheritable_workspace_fields.rs b/tests/testsuite/inheritable_workspace_fields.rs index 7ac3252eb2e..14e02aa831d 100644 --- a/tests/testsuite/inheritable_workspace_fields.rs +++ b/tests/testsuite/inheritable_workspace_fields.rs @@ -1549,7 +1549,7 @@ fn warn_inherit_def_feat_true_member_def_feat_false() { "#]]).run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn warn_inherit_def_feat_true_member_def_feat_false_2024_edition() { Package::new("dep", "0.1.0") .feature("default", &["fancy_dep"]) @@ -1563,8 +1563,6 @@ fn warn_inherit_def_feat_true_member_def_feat_false_2024_edition() { .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "bar" version = "0.2.0" @@ -1583,7 +1581,6 @@ fn warn_inherit_def_feat_true_member_def_feat_false_2024_edition() { .build(); p.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` @@ -1644,7 +1641,7 @@ fn warn_inherit_simple_member_def_feat_false() { "#]]).run(); } -#[cargo_test(nightly, reason = "edition2024 is not stable")] +#[cargo_test] fn warn_inherit_simple_member_def_feat_false_2024_edition() { Package::new("dep", "0.1.0") .feature("default", &["fancy_dep"]) @@ -1658,8 +1655,6 @@ fn warn_inherit_simple_member_def_feat_false_2024_edition() { .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "bar" version = "0.2.0" @@ -1678,7 +1673,6 @@ fn warn_inherit_simple_member_def_feat_false_2024_edition() { .build(); p.cargo("check") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` diff --git a/tests/testsuite/lockfile_path.rs b/tests/testsuite/lockfile_path.rs index 45fc62a23fe..b382064bf1c 100644 --- a/tests/testsuite/lockfile_path.rs +++ b/tests/testsuite/lockfile_path.rs @@ -494,7 +494,7 @@ fn install_lock_file_path_must_present() { .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn run_embed() { let lockfile_path = "mylockfile/Cargo.lock"; let invalid_lockfile = "Cargo.lock"; @@ -525,7 +525,7 @@ fn run_embed() { .arg("src/main.rs") .with_status(101) .with_stderr_data(str![[ - r#"[WARNING] `package.edition` is unspecified, defaulting to `2021` + r#"[WARNING] `package.edition` is unspecified, defaulting to `2024` [ERROR] failed to parse lock file at: [ROOT]/foo/Cargo.lock ... diff --git a/tests/testsuite/new.rs b/tests/testsuite/new.rs index f60849b562f..4c007bec168 100644 --- a/tests/testsuite/new.rs +++ b/tests/testsuite/new.rs @@ -393,7 +393,7 @@ fn new_with_edition_2018() { fn new_default_edition() { cargo_process("new foo").run(); let manifest = fs::read_to_string(paths::root().join("foo/Cargo.toml")).unwrap(); - assert!(manifest.contains("edition = \"2021\"")); + assert!(manifest.contains("edition = \"2024\"")); } #[cargo_test] @@ -407,7 +407,7 @@ fn new_with_bad_edition() { .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn lockfile_constant_during_new() { cargo_process("new foo").run(); diff --git a/tests/testsuite/publish.rs b/tests/testsuite/publish.rs index 403473ed3a3..961af6ad088 100644 --- a/tests/testsuite/publish.rs +++ b/tests/testsuite/publish.rs @@ -160,7 +160,7 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for [PACKAGING] foo v0.0.1 ([ROOT]/foo) [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] foo v0.0.1 ([ROOT]/foo) -[WARNING] no edition set: defaulting to the 2015 edition while the latest is 2021 +[WARNING] no edition set: defaulting to the 2015 edition while the latest is 2024 [COMPILING] foo v0.0.1 ([ROOT]/foo/target/package/foo-0.0.1) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] foo v0.0.1 ([ROOT]/foo) diff --git a/tests/testsuite/rust_version.rs b/tests/testsuite/rust_version.rs index a33a6576f4d..a007eb06066 100644 --- a/tests/testsuite/rust_version.rs +++ b/tests/testsuite/rust_version.rs @@ -515,7 +515,7 @@ higher v0.0.1 ([ROOT]/foo) .run(); } -#[cargo_test(nightly, reason = "edition2024 in rustc is unstable")] +#[cargo_test] fn resolve_edition2024() { Package::new("only-newer", "1.6.0") .rust_version("1.90.0") @@ -534,8 +534,6 @@ fn resolve_edition2024() { .file( "Cargo.toml", r#" - cargo-features = ["edition2024"] - [package] name = "foo" version = "0.0.1" @@ -553,7 +551,6 @@ fn resolve_edition2024() { // Edition2024 should resolve for MSRV p.cargo("generate-lockfile") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [LOCKING] 2 packages to latest Rust 1.85.0 compatible versions @@ -563,7 +560,6 @@ fn resolve_edition2024() { "#]]) .run(); p.cargo("tree") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.5.0 @@ -581,10 +577,8 @@ foo v0.0.1 ([ROOT]/foo) [ADDING] only-newer v1.6.0 (requires Rust 1.90.0) "#]]) - .masquerade_as_nightly_cargo(&["edition2024"]) .run(); p.cargo("tree") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.6.0 @@ -603,10 +597,8 @@ foo v0.0.1 ([ROOT]/foo) [ADDING] only-newer v1.6.0 (requires Rust 1.90.0) "#]]) - .masquerade_as_nightly_cargo(&["edition2024"]) .run(); p.cargo("tree") - .masquerade_as_nightly_cargo(&["edition2024"]) .with_stdout_data(str![[r#" foo v0.0.1 ([ROOT]/foo) ├── newer-and-older v1.6.0 diff --git a/tests/testsuite/script.rs b/tests/testsuite/script.rs index 3adcd93f569..3c31c77f360 100644 --- a/tests/testsuite/script.rs +++ b/tests/testsuite/script.rs @@ -25,7 +25,7 @@ fn path() -> Vec { std::env::split_paths(&std::env::var_os("PATH").unwrap_or_default()).collect() } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn basic_rs() { let p = cargo_test_support::project() .file("echo.rs", ECHO_SCRIPT) @@ -39,7 +39,7 @@ args: [] "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] echo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]` @@ -48,7 +48,7 @@ args: [] .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn basic_path() { let p = cargo_test_support::project() .file("echo", ECHO_SCRIPT) @@ -62,7 +62,7 @@ args: [] "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] echo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]` @@ -94,7 +94,7 @@ fn path_required() { .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] #[cfg(unix)] fn manifest_precedence_over_plugins() { let p = cargo_test_support::project() @@ -116,7 +116,7 @@ args: [] "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] echo v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/echo[EXE]` @@ -125,7 +125,7 @@ args: [] .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] #[cfg(unix)] fn warn_when_plugin_masks_manifest_on_stable() { let p = cargo_test_support::project() @@ -182,7 +182,7 @@ fn requires_z_flag() { .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn clean_output_with_edition() { let script = r#"#!/usr/bin/env cargo --- @@ -212,7 +212,7 @@ Hello world! .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn warning_without_edition() { let script = r#"#!/usr/bin/env cargo --- @@ -233,7 +233,7 @@ Hello world! "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` @@ -242,7 +242,7 @@ Hello world! .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn rebuild() { let script = r#"#!/usr/bin/env cargo-eval @@ -261,7 +261,7 @@ msg = undefined "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` @@ -277,7 +277,7 @@ msg = undefined "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` @@ -293,7 +293,7 @@ msg = hello "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` @@ -302,7 +302,7 @@ msg = hello .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn use_cargo_home_config() { let script = ECHO_SCRIPT; let _ = cargo_test_support::project() @@ -366,7 +366,7 @@ Caused by: .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn default_programmatic_verbosity() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -384,7 +384,7 @@ args: ["-NotAnArg"] .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn quiet() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -402,7 +402,7 @@ args: ["-NotAnArg"] .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn test_line_numbering_preserved() { let script = r#"#!/usr/bin/env cargo @@ -421,7 +421,7 @@ line: 4 "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` @@ -430,7 +430,7 @@ line: 4 .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn test_escaped_hyphen_arg() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -445,7 +445,7 @@ args: ["-NotAnArg"] "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] -NotAnArg` @@ -454,7 +454,7 @@ args: ["-NotAnArg"] .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn test_unescaped_hyphen_arg() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -469,7 +469,7 @@ args: ["-NotAnArg"] "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] -NotAnArg` @@ -478,7 +478,7 @@ args: ["-NotAnArg"] .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn test_same_flags() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -493,7 +493,7 @@ args: ["--help"] "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] --help` @@ -502,7 +502,7 @@ args: ["--help"] .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn test_name_has_weird_chars() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -517,7 +517,7 @@ args: [] "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] s-h-w-c- v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/s-h-w-c-[EXE]` @@ -526,7 +526,7 @@ args: [] .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn test_name_has_leading_number() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -541,7 +541,7 @@ args: [] "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] answer v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/answer[EXE]` @@ -550,7 +550,7 @@ args: [] .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn test_name_is_number() { let script = ECHO_SCRIPT; let p = cargo_test_support::project().file("42.rs", script).build(); @@ -563,7 +563,7 @@ args: [] "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] package v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/package[EXE]` @@ -686,7 +686,7 @@ fn did_you_mean_command_stable() { .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn test_name_same_as_dependency() { Package::new("script", "1.0.0").publish(); let script = r#"#!/usr/bin/env cargo @@ -709,9 +709,9 @@ Hello world! "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [UPDATING] `dummy-registry` index -[LOCKING] 1 package to latest compatible version +[LOCKING] 1 package to latest Rust [..] compatible version [DOWNLOADING] crates ... [DOWNLOADED] script v1.0.0 (registry `dummy-registry`) [COMPILING] script v1.0.0 @@ -723,7 +723,7 @@ Hello world! .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn test_path_dep() { let script = r#"#!/usr/bin/env cargo --- @@ -748,8 +748,8 @@ Hello world! "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` -[LOCKING] 1 package to latest compatible version +[WARNING] `package.edition` is unspecified, defaulting to `2024` +[LOCKING] 1 package to latest Rust [..] compatible version [COMPILING] bar v0.0.1 ([ROOT]/foo/bar) [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -759,7 +759,7 @@ Hello world! .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn test_no_build_rs() { let script = r#"#!/usr/bin/env cargo @@ -778,7 +778,7 @@ Hello world! "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] --help` @@ -787,7 +787,7 @@ Hello world! .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn test_no_autobins() { let script = r#"#!/usr/bin/env cargo @@ -806,7 +806,7 @@ Hello world! "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] --help` @@ -815,7 +815,7 @@ Hello world! .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn test_no_autolib() { let script = r#"#!/usr/bin/env cargo @@ -834,7 +834,7 @@ Hello world! "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE] --help` @@ -843,7 +843,7 @@ Hello world! .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn implicit_target_dir() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -858,7 +858,7 @@ args: [] "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` @@ -867,7 +867,7 @@ args: [] .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn no_local_lockfile() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -885,7 +885,7 @@ args: [] "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` @@ -931,7 +931,7 @@ fn cmd_check_requires_z_flag() { .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn cmd_check_with_embedded() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -942,7 +942,7 @@ fn cmd_check_with_embedded() { .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data("") .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [CHECKING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -980,7 +980,7 @@ fn cmd_check_with_missing_script() { .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn cmd_build_with_embedded() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -991,7 +991,7 @@ fn cmd_build_with_embedded() { .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data("") .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -999,7 +999,7 @@ fn cmd_build_with_embedded() { .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn cmd_test_with_embedded() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -1018,7 +1018,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests script.rs ([ROOT]/home/.cargo/target/[HASH]/debug/deps/script-[HASH][EXE]) @@ -1027,7 +1027,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn cmd_clean_with_embedded() { let script = ECHO_SCRIPT; let p = cargo_test_support::project() @@ -1043,7 +1043,7 @@ fn cmd_clean_with_embedded() { .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data("") .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [REMOVED] [FILE_NUM] files, [FILE_SIZE]B total "#]]) @@ -1061,7 +1061,7 @@ fn cmd_generate_lockfile_with_embedded() { .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data("") .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); @@ -1088,7 +1088,7 @@ fn cmd_metadata_with_embedded() { "dependencies": [], "description": null, "documentation": null, - "edition": "2021", + "edition": "2024", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#script@0.0.0", @@ -1111,7 +1111,7 @@ fn cmd_metadata_with_embedded() { ], "doc": true, "doctest": false, - "edition": "2021", + "edition": "2024", "kind": [ "bin" ], @@ -1148,7 +1148,7 @@ fn cmd_metadata_with_embedded() { .is_json(), ) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); @@ -1172,7 +1172,7 @@ fn cmd_read_manifest_with_embedded() { "dependencies": [], "description": null, "documentation": null, - "edition": "2021", + "edition": "2024", "features": {}, "homepage": null, "id": "path+[ROOTURL]/foo#script@0.0.0", @@ -1195,7 +1195,7 @@ fn cmd_read_manifest_with_embedded() { ], "doc": true, "doctest": false, - "edition": "2021", + "edition": "2024", "kind": [ "bin" ], @@ -1210,13 +1210,13 @@ fn cmd_read_manifest_with_embedded() { .is_json(), ) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn cmd_run_with_embedded() { let p = cargo_test_support::project() .file("script.rs", ECHO_SCRIPT) @@ -1230,7 +1230,7 @@ args: [] "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` @@ -1252,7 +1252,7 @@ script v0.0.0 ([ROOT]/foo) "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); @@ -1268,7 +1268,7 @@ fn cmd_update_with_embedded() { .masquerade_as_nightly_cargo(&["script"]) .with_stdout_data("") .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); @@ -1291,7 +1291,7 @@ fn cmd_verify_project_with_embedded() { .is_json(), ) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` "#]]) .run(); @@ -1307,7 +1307,7 @@ fn cmd_pkgid_with_embedded() { .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [ERROR] [ROOT]/foo/script.rs is unsupported by `cargo pkgid` "#]]) @@ -1324,7 +1324,7 @@ fn cmd_package_with_embedded() { .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [ERROR] [ROOT]/foo/script.rs is unsupported by `cargo package` "#]]) @@ -1341,14 +1341,14 @@ fn cmd_publish_with_embedded() { .masquerade_as_nightly_cargo(&["script"]) .with_status(101) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [ERROR] [ROOT]/foo/script.rs is unsupported by `cargo publish` "#]]) .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn manifest_path_env() { let p = cargo_test_support::project() .file( @@ -1369,7 +1369,7 @@ CARGO_MANIFEST_PATH: [ROOT]/foo/script.rs "#]]) .with_stderr_data(str![[r#" -[WARNING] `package.edition` is unspecified, defaulting to `2021` +[WARNING] `package.edition` is unspecified, defaulting to `2024` [COMPILING] script v0.0.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `[ROOT]/home/.cargo/target/[HASH]/debug/script[EXE]` From 80dde854b4745fd0fabe6319684774682ae57f12 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 6 Oct 2024 17:05:34 +0200 Subject: [PATCH 100/525] Bump cargo-platform to 0.2.0 in preparation for `cfg()` --- Cargo.lock | 6 +++--- Cargo.toml | 2 +- crates/cargo-platform/Cargo.toml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2a167643abe..a9c95951a82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -318,7 +318,7 @@ dependencies = [ "cargo-credential-libsecret", "cargo-credential-macos-keychain", "cargo-credential-wincred", - "cargo-platform 0.1.9", + "cargo-platform 0.2.0", "cargo-test-support", "cargo-util", "cargo-util-schemas", @@ -444,7 +444,7 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.9" +version = "0.2.0" dependencies = [ "serde", ] @@ -3026,7 +3026,7 @@ name = "resolver-tests" version = "0.0.0" dependencies = [ "cargo", - "cargo-platform 0.1.9", + "cargo-platform 0.2.0", "cargo-util", "cargo-util-schemas", "proptest", diff --git a/Cargo.toml b/Cargo.toml index c2cbff88b5d..ad045fa34ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,7 @@ cargo-credential = { version = "0.4.2", path = "credential/cargo-credential" } cargo-credential-libsecret = { version = "0.4.7", path = "credential/cargo-credential-libsecret" } cargo-credential-macos-keychain = { version = "0.4.7", path = "credential/cargo-credential-macos-keychain" } cargo-credential-wincred = { version = "0.4.7", path = "credential/cargo-credential-wincred" } -cargo-platform = { path = "crates/cargo-platform", version = "0.1.5" } +cargo-platform = { path = "crates/cargo-platform", version = "0.2.0" } cargo-test-macro = { version = "0.3.0", path = "crates/cargo-test-macro" } cargo-test-support = { version = "0.6.0", path = "crates/cargo-test-support" } cargo-util = { version = "0.2.14", path = "crates/cargo-util" } diff --git a/crates/cargo-platform/Cargo.toml b/crates/cargo-platform/Cargo.toml index 02dd6da8232..778a7cf6742 100644 --- a/crates/cargo-platform/Cargo.toml +++ b/crates/cargo-platform/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-platform" -version = "0.1.9" +version = "0.2.0" edition.workspace = true license.workspace = true rust-version.workspace = true From ac10b55e7289a12e94d2c2da0c804688eebd55a7 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 10 Oct 2024 20:03:16 +0200 Subject: [PATCH 101/525] Add preliminary test for keywords in cfgs --- tests/testsuite/cfg.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/testsuite/cfg.rs b/tests/testsuite/cfg.rs index 7e90b51a626..9afd8495b0a 100644 --- a/tests/testsuite/cfg.rs +++ b/tests/testsuite/cfg.rs @@ -521,3 +521,38 @@ error[E0463]: can't find crate for `bar` "#]]) .run(); } + +#[cargo_test] +fn cfg_keywords() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2015" + + [target.'cfg(any(async, fn, const, return, true))'.dependencies] + b = { path = "b/" } + "#, + ) + .file( + ".cargo/config.toml", + r#" + [target."cfg(any(for, match, extern, crate, false))"] + "#, + ) + .file("src/lib.rs", "") + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/lib.rs", "pub fn foo() {}") + .build(); + + p.cargo("check") + .with_stderr_data(str![[r#" +[LOCKING] 1 package to latest compatible version +[CHECKING] foo v0.1.0 ([ROOT]/foo) +... +"#]]) + .run(); +} From 49468282111506833a1992b25dce4bd0fb323273 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 11 Oct 2024 10:34:24 +0200 Subject: [PATCH 102/525] Add future-incompatibility warning against some keyword-as-ident --- crates/cargo-platform/src/cfg.rs | 8 ++++++ crates/cargo-platform/src/lib.rs | 43 +++++++++++++++++++++++++++++++- src/cargo/util/context/target.rs | 9 ++++++- src/cargo/util/toml/mod.rs | 1 + tests/testsuite/cfg.rs | 13 +++++++++- 5 files changed, 71 insertions(+), 3 deletions(-) diff --git a/crates/cargo-platform/src/cfg.rs b/crates/cargo-platform/src/cfg.rs index c3ddb69bc88..5753813a5e0 100644 --- a/crates/cargo-platform/src/cfg.rs +++ b/crates/cargo-platform/src/cfg.rs @@ -31,6 +31,14 @@ enum Token<'a> { String(&'a str), } +/// The list of keywords. +/// +/// We should consider all the keywords, but some are conditional on +/// the edition so for now we just consider true/false. +/// +/// +pub(crate) const KEYWORDS: &[&str; 2] = &["true", "false"]; + #[derive(Clone)] struct Tokenizer<'a> { s: iter::Peekable>, diff --git a/crates/cargo-platform/src/lib.rs b/crates/cargo-platform/src/lib.rs index 71e9140bae9..c1b42880438 100644 --- a/crates/cargo-platform/src/lib.rs +++ b/crates/cargo-platform/src/lib.rs @@ -11,12 +11,13 @@ //! //! [`Platform`]: enum.Platform.html -use std::fmt; use std::str::FromStr; +use std::{fmt, path::Path}; mod cfg; mod error; +use cfg::KEYWORDS; pub use cfg::{Cfg, CfgExpr}; pub use error::{ParseError, ParseErrorKind}; @@ -104,6 +105,46 @@ impl Platform { check_cfg_expr(cfg, warnings); } } + + pub fn check_cfg_keywords(&self, warnings: &mut Vec, path: &Path) { + fn check_cfg_expr(expr: &CfgExpr, warnings: &mut Vec, path: &Path) { + match *expr { + CfgExpr::Not(ref e) => check_cfg_expr(e, warnings, path), + CfgExpr::All(ref e) | CfgExpr::Any(ref e) => { + for e in e { + check_cfg_expr(e, warnings, path); + } + } + CfgExpr::Value(ref e) => match e { + Cfg::Name(name) | Cfg::KeyPair(name, _) => { + if KEYWORDS.contains(&name.as_str()) { + if name.as_str() == "true" || name.as_str() == "false" { + warnings.push(format!( + "[{}] future-incompatibility: the meaning of `cfg({e})` will change in the future\n \ + | Cargo is erroneously allowing `cfg(true)` and `cfg(false)`, but both forms are interpreted as false unless manually overridden with `--cfg`.\n \ + | In the future these will be built-in defines that will have the corresponding true/false value.\n \ + | It is recommended to avoid using these configs until they are properly supported.\n \ + | See for more information.", + path.display() + )); + } else { + warnings.push(format!( + "[{}] future-incompatibility: `cfg({e})` is deprecated as `{name}` is a keyword \ + and not an identifier and should not have have been accepted in this position.\n \ + | this was previously accepted by Cargo but is being phased out; it will become a hard error in a future release!", + path.display() + )); + } + } + } + }, + } + } + + if let Platform::Cfg(cfg) = self { + check_cfg_expr(cfg, warnings, path); + } + } } impl serde::Serialize for Platform { diff --git a/src/cargo/util/context/target.rs b/src/cargo/util/context/target.rs index f306ecc1731..3de3d826a59 100644 --- a/src/cargo/util/context/target.rs +++ b/src/cargo/util/context/target.rs @@ -3,7 +3,7 @@ use crate::core::compiler::{BuildOutput, LinkArgTarget}; use crate::util::CargoResult; use serde::Deserialize; use std::collections::{BTreeMap, HashMap}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::rc::Rc; /// Config definition of a `[target.'cfg(…)']` table. @@ -53,6 +53,13 @@ pub(super) fn load_target_cfgs( let target: BTreeMap = gctx.get("target")?; tracing::debug!("Got all targets {:#?}", target); for (key, cfg) in target { + if let Ok(platform) = key.parse::() { + let mut warnings = Vec::new(); + platform.check_cfg_keywords(&mut warnings, &Path::new(".cargo/config.toml")); + for w in warnings { + gctx.shell().warn(w)?; + } + } if key.starts_with("cfg(") { // Unfortunately this is not able to display the location of the // unused key. Using config::Value doesn't work. One diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index c0f6df61d90..59b2cab3918 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1314,6 +1314,7 @@ pub fn to_real_manifest( for (name, platform) in original_toml.target.iter().flatten() { let platform_kind: Platform = name.parse()?; platform_kind.check_cfg_attributes(warnings); + platform_kind.check_cfg_keywords(warnings, manifest_file); let platform_kind = Some(platform_kind); validate_dependencies( platform.dependencies.as_ref(), diff --git a/tests/testsuite/cfg.rs b/tests/testsuite/cfg.rs index 9afd8495b0a..ddf1382fa1a 100644 --- a/tests/testsuite/cfg.rs +++ b/tests/testsuite/cfg.rs @@ -550,9 +550,20 @@ fn cfg_keywords() { p.cargo("check") .with_stderr_data(str![[r#" +[WARNING] [[ROOT]/foo/Cargo.toml] future-incompatibility: the meaning of `cfg(true)` will change in the future + | Cargo is erroneously allowing `cfg(true)` and `cfg(false)`, but both forms are interpreted as false unless manually overridden with `--cfg`. + | In the future these will be built-in defines that will have the corresponding true/false value. + | It is recommended to avoid using these configs until they are properly supported. + | See for more information. +[WARNING] [.cargo/config.toml] future-incompatibility: the meaning of `cfg(false)` will change in the future + | Cargo is erroneously allowing `cfg(true)` and `cfg(false)`, but both forms are interpreted as false unless manually overridden with `--cfg`. + | In the future these will be built-in defines that will have the corresponding true/false value. + | It is recommended to avoid using these configs until they are properly supported. + | See for more information. [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.1.0 ([ROOT]/foo) -... +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + "#]]) .run(); } From 3c3bfb0310fe3097b674a50aed03516d2bc92e3a Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 11 Oct 2024 11:53:35 +0200 Subject: [PATCH 103/525] Add preliminary test for raw-idents in cfgs --- tests/testsuite/cfg.rs | 90 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/tests/testsuite/cfg.rs b/tests/testsuite/cfg.rs index ddf1382fa1a..cb32596a354 100644 --- a/tests/testsuite/cfg.rs +++ b/tests/testsuite/cfg.rs @@ -522,6 +522,96 @@ error[E0463]: can't find crate for `bar` .run(); } +#[cargo_test] +fn cfg_raw_idents() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2015" + + [target.'cfg(any(r#true, r#all, r#target_os = "<>"))'.dependencies] + b = { path = "b/" } + "#, + ) + .file("src/lib.rs", "") + .file("b/Cargo.toml", &basic_manifest("b", "0.0.1")) + .file("b/src/lib.rs", "pub fn foo() {}") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + failed to parse `any(r#true, r#all, r#target_os = "<>")` as a cfg expression: unexpected character `#` in cfg, expected parens, a comma, an identifier, or a string + +"#]]) + .run(); +} + +#[cargo_test] +fn cfg_raw_idents_empty() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2015" + + [target.'cfg(r#))'.dependencies] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + failed to parse `r#)` as a cfg expression: unexpected content `#)` found after cfg expression + +"#]]) + .run(); +} + +#[cargo_test] +fn cfg_raw_idents_not_really() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2015" + + [target.'cfg(r#11))'.dependencies] + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + failed to parse `r#11)` as a cfg expression: unexpected content `#11)` found after cfg expression + +"#]]) + .run(); +} + #[cargo_test] fn cfg_keywords() { let p = project() From 9450706a035783db01a0d78f21a394757e7c0b78 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 11 Oct 2024 12:09:39 +0200 Subject: [PATCH 104/525] Add support for raw-idents in cfgs --- crates/cargo-platform/src/cfg.rs | 112 +++++++++++++++++++++--- crates/cargo-platform/src/lib.rs | 2 +- crates/cargo-platform/tests/test_cfg.rs | 40 ++++++++- src/cargo/core/compiler/custom_build.rs | 4 +- tests/testsuite/cfg.rs | 17 ++-- 5 files changed, 153 insertions(+), 22 deletions(-) diff --git a/crates/cargo-platform/src/cfg.rs b/crates/cargo-platform/src/cfg.rs index 5753813a5e0..79aaa02c23f 100644 --- a/crates/cargo-platform/src/cfg.rs +++ b/crates/cargo-platform/src/cfg.rs @@ -1,5 +1,6 @@ use crate::error::{ParseError, ParseErrorKind::*}; use std::fmt; +use std::hash::{Hash, Hasher}; use std::iter; use std::str::{self, FromStr}; @@ -16,16 +17,28 @@ pub enum CfgExpr { #[derive(Eq, PartialEq, Hash, Ord, PartialOrd, Clone, Debug)] pub enum Cfg { /// A named cfg value, like `unix`. - Name(String), + Name(Ident), /// A key/value cfg pair, like `target_os = "linux"`. - KeyPair(String, String), + KeyPair(Ident, String), +} + +/// A identifier +#[derive(Eq, Ord, PartialOrd, Clone, Debug)] +pub struct Ident { + /// The identifier + pub name: String, + /// Is this a raw ident: `r#async` + /// + /// It's mainly used for display and doesn't take + /// part in the hash or equality (`foo` == `r#foo`). + pub raw: bool, } #[derive(PartialEq)] enum Token<'a> { LeftParen, RightParen, - Ident(&'a str), + Ident(bool, &'a str), Comma, Equals, String(&'a str), @@ -49,6 +62,45 @@ struct Parser<'a> { t: Tokenizer<'a>, } +impl Ident { + pub fn as_str(&self) -> &str { + &self.name + } +} + +impl Hash for Ident { + fn hash(&self, state: &mut H) { + self.name.hash(state); + } +} + +impl PartialEq for Ident { + fn eq(&self, other: &str) -> bool { + self.name == other + } +} + +impl PartialEq<&str> for Ident { + fn eq(&self, other: &&str) -> bool { + self.name == *other + } +} + +impl PartialEq for Ident { + fn eq(&self, other: &Ident) -> bool { + self.name == other.name + } +} + +impl fmt::Display for Ident { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.raw { + f.write_str("r#")?; + } + f.write_str(&*self.name) + } +} + impl FromStr for Cfg { type Err = ParseError; @@ -152,7 +204,8 @@ impl<'a> Parser<'a> { fn expr(&mut self) -> Result { match self.peek() { - Some(Ok(Token::Ident(op @ "all"))) | Some(Ok(Token::Ident(op @ "any"))) => { + Some(Ok(Token::Ident(false, op @ "all"))) + | Some(Ok(Token::Ident(false, op @ "any"))) => { self.t.next(); let mut e = Vec::new(); self.eat(&Token::LeftParen)?; @@ -169,7 +222,7 @@ impl<'a> Parser<'a> { Ok(CfgExpr::Any(e)) } } - Some(Ok(Token::Ident("not"))) => { + Some(Ok(Token::Ident(false, "not"))) => { self.t.next(); self.eat(&Token::LeftParen)?; let e = self.expr()?; @@ -187,7 +240,7 @@ impl<'a> Parser<'a> { fn cfg(&mut self) -> Result { match self.t.next() { - Some(Ok(Token::Ident(name))) => { + Some(Ok(Token::Ident(raw, name))) => { let e = if self.r#try(&Token::Equals) { let val = match self.t.next() { Some(Ok(Token::String(s))) => s, @@ -205,9 +258,18 @@ impl<'a> Parser<'a> { return Err(ParseError::new(self.t.orig, IncompleteExpr("a string"))) } }; - Cfg::KeyPair(name.to_string(), val.to_string()) + Cfg::KeyPair( + Ident { + name: name.to_string(), + raw, + }, + val.to_string(), + ) } else { - Cfg::Name(name.to_string()) + Cfg::Name(Ident { + name: name.to_string(), + raw, + }) }; Ok(e) } @@ -287,14 +349,44 @@ impl<'a> Iterator for Tokenizer<'a> { return Some(Err(ParseError::new(self.orig, UnterminatedString))); } Some((start, ch)) if is_ident_start(ch) => { + let (start, raw) = if ch == 'r' { + if let Some(&(_pos, '#')) = self.s.peek() { + // starts with `r#` is a raw ident + self.s.next(); + if let Some((start, ch)) = self.s.next() { + if is_ident_start(ch) { + (start, true) + } else { + // not a starting ident character + return Some(Err(ParseError::new( + self.orig, + UnexpectedChar(ch), + ))); + } + } else { + // not followed by a ident, error out + return Some(Err(ParseError::new( + self.orig, + IncompleteExpr("identifier"), + ))); + } + } else { + // starts with `r` but not does continue with `#` + // cannot be a raw ident + (start, false) + } + } else { + // do not start with `r`, cannot be a raw ident + (start, false) + }; while let Some(&(end, ch)) = self.s.peek() { if !is_ident_rest(ch) { - return Some(Ok(Token::Ident(&self.orig[start..end]))); + return Some(Ok(Token::Ident(raw, &self.orig[start..end]))); } else { self.s.next(); } } - return Some(Ok(Token::Ident(&self.orig[start..]))); + return Some(Ok(Token::Ident(raw, &self.orig[start..]))); } Some((_, ch)) => { return Some(Err(ParseError::new(self.orig, UnexpectedChar(ch)))); diff --git a/crates/cargo-platform/src/lib.rs b/crates/cargo-platform/src/lib.rs index c1b42880438..421085b61b7 100644 --- a/crates/cargo-platform/src/lib.rs +++ b/crates/cargo-platform/src/lib.rs @@ -18,7 +18,7 @@ mod cfg; mod error; use cfg::KEYWORDS; -pub use cfg::{Cfg, CfgExpr}; +pub use cfg::{Cfg, CfgExpr, Ident}; pub use error::{ParseError, ParseErrorKind}; /// Platform definition. diff --git a/crates/cargo-platform/tests/test_cfg.rs b/crates/cargo-platform/tests/test_cfg.rs index dd99d9a79b8..0fdf307fab7 100644 --- a/crates/cargo-platform/tests/test_cfg.rs +++ b/crates/cargo-platform/tests/test_cfg.rs @@ -1,13 +1,37 @@ -use cargo_platform::{Cfg, CfgExpr, Platform}; +use cargo_platform::{Cfg, CfgExpr, Ident, Platform}; use std::fmt; use std::str::FromStr; macro_rules! c { ($a:ident) => { - Cfg::Name(stringify!($a).to_string()) + Cfg::Name(Ident { + name: stringify!($a).to_string(), + raw: false, + }) + }; + (r # $a:ident) => { + Cfg::Name(Ident { + name: stringify!($a).to_string(), + raw: true, + }) }; ($a:ident = $e:expr) => { - Cfg::KeyPair(stringify!($a).to_string(), $e.to_string()) + Cfg::KeyPair( + Ident { + name: stringify!($a).to_string(), + raw: false, + }, + $e.to_string(), + ) + }; + (r # $a:ident = $e:expr) => { + Cfg::KeyPair( + Ident { + name: stringify!($a).to_string(), + raw: true, + }, + $e.to_string(), + ) }; } @@ -56,10 +80,13 @@ fn cfg_syntax() { good("_bar", c!(_bar)); good(" foo", c!(foo)); good(" foo ", c!(foo)); + good("r#foo", c!(r # foo)); good(" foo = \"bar\"", c!(foo = "bar")); good("foo=\"\"", c!(foo = "")); + good("r#foo=\"\"", c!(r # foo = "")); good(" foo=\"3\" ", c!(foo = "3")); good("foo = \"3 e\"", c!(foo = "3 e")); + good(" r#foo = \"3 e\"", c!(r # foo = "3 e")); } #[test] @@ -78,6 +105,10 @@ fn cfg_syntax_bad() { "foo, bar", "unexpected content `, bar` found after cfg expression", ); + bad::("r# foo", "unexpected character"); + bad::("r #foo", "unexpected content"); + bad::("r#\"foo\"", "unexpected character"); + bad::("foo = r#\"\"", "unexpected character"); } #[test] @@ -126,6 +157,9 @@ fn cfg_matches() { assert!(e!(not(foo)).matches(&[])); assert!(e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(bar)])); assert!(e!(any((not(foo)), (all(foo, bar)))).matches(&[c!(foo), c!(bar)])); + assert!(e!(foo).matches(&[c!(r # foo)])); + assert!(e!(r # foo).matches(&[c!(foo)])); + assert!(e!(r # foo).matches(&[c!(r # foo)])); assert!(!e!(foo).matches(&[])); assert!(!e!(foo).matches(&[c!(bar)])); diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index 95126f1f2e9..67e385688c0 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -353,7 +353,9 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul // That is because Cargo queries rustc without any profile settings. continue; } - let k = format!("CARGO_CFG_{}", super::envify(&k)); + // FIXME: We should handle raw-idents somehow instead of predenting they + // don't exist here + let k = format!("CARGO_CFG_{}", super::envify(k.as_str())); cmd.env(&k, v.join(",")); } diff --git a/tests/testsuite/cfg.rs b/tests/testsuite/cfg.rs index cb32596a354..e0e6ac8b87b 100644 --- a/tests/testsuite/cfg.rs +++ b/tests/testsuite/cfg.rs @@ -543,12 +543,15 @@ fn cfg_raw_idents() { .build(); p.cargo("check") - .with_status(101) .with_stderr_data(str![[r#" -[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` - -Caused by: - failed to parse `any(r#true, r#all, r#target_os = "<>")` as a cfg expression: unexpected character `#` in cfg, expected parens, a comma, an identifier, or a string +[WARNING] [[ROOT]/foo/Cargo.toml] future-incompatibility: the meaning of `cfg(r#true)` will change in the future + | Cargo is erroneously allowing `cfg(true)` and `cfg(false)`, but both forms are interpreted as false unless manually overridden with `--cfg`. + | In the future these will be built-in defines that will have the corresponding true/false value. + | It is recommended to avoid using these configs until they are properly supported. + | See for more information. +[LOCKING] 1 package to latest compatible version +[CHECKING] foo v0.1.0 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); @@ -577,7 +580,7 @@ fn cfg_raw_idents_empty() { [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: - failed to parse `r#)` as a cfg expression: unexpected content `#)` found after cfg expression + failed to parse `r#)` as a cfg expression: unexpected character `)` in cfg, expected parens, a comma, an identifier, or a string "#]]) .run(); @@ -606,7 +609,7 @@ fn cfg_raw_idents_not_really() { [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: - failed to parse `r#11)` as a cfg expression: unexpected content `#11)` found after cfg expression + failed to parse `r#11)` as a cfg expression: unexpected character `1` in cfg, expected parens, a comma, an identifier, or a string "#]]) .run(); From e2028d4bc25a6c143d0d8ed69f7db1f6b5736e27 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 3 Nov 2024 00:09:21 +0100 Subject: [PATCH 105/525] Adjust future-incompatibility cfg keyword with raw-idents in cfgs --- crates/cargo-platform/src/lib.rs | 10 +++++++--- tests/testsuite/cfg.rs | 9 ++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/cargo-platform/src/lib.rs b/crates/cargo-platform/src/lib.rs index 421085b61b7..f91b61708bb 100644 --- a/crates/cargo-platform/src/lib.rs +++ b/crates/cargo-platform/src/lib.rs @@ -117,21 +117,25 @@ impl Platform { } CfgExpr::Value(ref e) => match e { Cfg::Name(name) | Cfg::KeyPair(name, _) => { - if KEYWORDS.contains(&name.as_str()) { + if !name.raw && KEYWORDS.contains(&name.as_str()) { if name.as_str() == "true" || name.as_str() == "false" { warnings.push(format!( "[{}] future-incompatibility: the meaning of `cfg({e})` will change in the future\n \ | Cargo is erroneously allowing `cfg(true)` and `cfg(false)`, but both forms are interpreted as false unless manually overridden with `--cfg`.\n \ | In the future these will be built-in defines that will have the corresponding true/false value.\n \ | It is recommended to avoid using these configs until they are properly supported.\n \ - | See for more information.", + | See for more information.\n \ + |\n \ + | help: use raw-idents instead: `cfg(r#{name})`", path.display() )); } else { warnings.push(format!( "[{}] future-incompatibility: `cfg({e})` is deprecated as `{name}` is a keyword \ and not an identifier and should not have have been accepted in this position.\n \ - | this was previously accepted by Cargo but is being phased out; it will become a hard error in a future release!", + | this was previously accepted by Cargo but is being phased out; it will become a hard error in a future release!\n \ + |\n \ + | help: use raw-idents instead: `cfg(r#{name})`", path.display() )); } diff --git a/tests/testsuite/cfg.rs b/tests/testsuite/cfg.rs index e0e6ac8b87b..9b5cd240765 100644 --- a/tests/testsuite/cfg.rs +++ b/tests/testsuite/cfg.rs @@ -544,11 +544,6 @@ fn cfg_raw_idents() { p.cargo("check") .with_stderr_data(str![[r#" -[WARNING] [[ROOT]/foo/Cargo.toml] future-incompatibility: the meaning of `cfg(r#true)` will change in the future - | Cargo is erroneously allowing `cfg(true)` and `cfg(false)`, but both forms are interpreted as false unless manually overridden with `--cfg`. - | In the future these will be built-in defines that will have the corresponding true/false value. - | It is recommended to avoid using these configs until they are properly supported. - | See for more information. [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -648,11 +643,15 @@ fn cfg_keywords() { | In the future these will be built-in defines that will have the corresponding true/false value. | It is recommended to avoid using these configs until they are properly supported. | See for more information. + | + | [HELP] use raw-idents instead: `cfg(r#true)` [WARNING] [.cargo/config.toml] future-incompatibility: the meaning of `cfg(false)` will change in the future | Cargo is erroneously allowing `cfg(true)` and `cfg(false)`, but both forms are interpreted as false unless manually overridden with `--cfg`. | In the future these will be built-in defines that will have the corresponding true/false value. | It is recommended to avoid using these configs until they are properly supported. | See for more information. + | + | [HELP] use raw-idents instead: `cfg(r#false)` [LOCKING] 1 package to latest compatible version [CHECKING] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s From 02e25d560868bab13e0990edae684cf7ca394087 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 26 Nov 2024 09:42:31 -0500 Subject: [PATCH 106/525] test(pgo): ensure PGO works This is a regression test to prevent issues like #7416. The test only run on Linux, as other platforms have different requirements for PGO, or emit different PGO function missing warnings. --- tests/testsuite/main.rs | 1 + tests/testsuite/pgo.rs | 111 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 tests/testsuite/pgo.rs diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index 4b52381e3e4..72146c4ab79 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -136,6 +136,7 @@ mod package_features; mod patch; mod path; mod paths; +mod pgo; mod pkgid; mod precise_pre_release; mod proc_macro; diff --git a/tests/testsuite/pgo.rs b/tests/testsuite/pgo.rs new file mode 100644 index 00000000000..631a80c9000 --- /dev/null +++ b/tests/testsuite/pgo.rs @@ -0,0 +1,111 @@ +//! Test if PGO works. + +use std::path::PathBuf; +use std::process::Command; + +use cargo_test_support::prelude::*; +use cargo_test_support::project; +use cargo_test_support::str; + +fn llvm_profdata() -> Option { + let output = Command::new("rustc") + .arg("--print=target-libdir") + .output() + .expect("rustc to run"); + assert!(output.status.success()); + let mut libdir = PathBuf::from(String::from_utf8(output.stdout).unwrap()); + assert!(libdir.pop()); + let mut bin = libdir.join("bin").join("llvm-profdata"); + bin.exists().then(|| bin.clone()).or_else(|| { + bin.set_extension("exe"); + bin.exists().then_some(bin) + }) +} + +#[cargo_test] +fn pgo_works() { + if cfg!(not(target_os = "linux")) { + // macOS may emit different LLVM PGO warnings. + // Windows LLVM has different requirements. + return; + } + + let Some(llvm_profdata) = llvm_profdata() else { + return; + }; + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + edition = "2021" + "#, + ) + .file( + "src/main.rs", + r#" + fn fibonacci(n: u64) -> u64 { + match n { + 0 => 0, + 1 => 1, + _ => fibonacci(n - 1) + fibonacci(n - 2), + } + } + + fn main() { + for i in [15, 20, 25] { + let _ = fibonacci(i); + } + } + "#, + ) + .build(); + + let target_dir = p.build_dir(); + let release_bin = target_dir.join("release").join("foo"); + let pgo_data_dir = target_dir.join("pgo-data"); + let profdata_path = target_dir.join("merged.profdata"); + + // Build the instrumented binary + p.cargo("build --release") + .env( + "RUSTFLAGS", + format!("-Cprofile-generate={}", pgo_data_dir.display()), + ) + .run(); + // Run the instrumented binary + cargo_test_support::execs() + .with_process_builder(cargo_test_support::process(release_bin)) + .run(); + + cargo_test_support::process(llvm_profdata) + .arg("merge") + .arg("-o") + .arg(&profdata_path) + .arg(pgo_data_dir) + .status() + .unwrap(); + + // Use merged profdata during optimization. + // + // -Cllvm-args=-pgo-warn-missing-function is essential. + // If there are LLVM warnings, there might be something wrong. + p.cargo("build --release -v") + .env( + "RUSTFLAGS", + format!( + "-Cprofile-use={} -Cllvm-args=-pgo-warn-missing-function", + profdata_path.display() + ), + ) + .with_stderr_data(str![[r#" +[DIRTY] foo v0.0.0 ([ROOT]/foo): the rustflags changed +[COMPILING] foo v0.0.0 ([ROOT]/foo) +[RUNNING] `rustc [..]-Cprofile-use=[ROOT]/foo/target/merged.profdata -Cllvm-args=-pgo-warn-missing-function` +[FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s + +"#]]) + .run(); +} From b38d06d6d0dc6c95f4d63887c2e31dc91c4c9dc2 Mon Sep 17 00:00:00 2001 From: Charalampos Mitrodimas Date: Tue, 26 Nov 2024 17:35:39 +0200 Subject: [PATCH 107/525] git-fetch-with-cli: Set GIT_DIR for bare repository compatibility When using `net.git-fetch-with-cli = true` , Cargo fails to clone the git repository into the cache because `safe.bareRepository` defaults to `explicit`. This results in an error stating that a bare repository cannot be used. This patch sets the `GIT_DIR` environment variable to the correct repository path instead of removing it. This ensures that Git uses the correct repository directory and avoids the "cannot use bare repository" error. Fixes #14758 Signed-off-by: Charalampos Mitrodimas --- src/cargo/sources/git/utils.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index 6a68c79f254..781699b85e7 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -1101,9 +1101,8 @@ fn fetch_with_cli( .args(refspecs) // If cargo is run by git (for example, the `exec` command in `git // rebase`), the GIT_DIR is set by git and will point to the wrong - // location (this takes precedence over the cwd). Make sure this is - // unset so git will look at cwd for the repo. - .env_remove("GIT_DIR") + // location. This makes sure GIT_DIR is always the repository path. + .env("GIT_DIR", repo.path()) // The reset of these may not be necessary, but I'm including them // just to be extra paranoid and avoid any issues. .env_remove("GIT_WORK_TREE") From e0477a38690817a4bfd92982da7b8ab99c183534 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 25 Nov 2024 10:14:00 -0600 Subject: [PATCH 108/525] test(remove): Show what happens with last dep in a table --- .../cargo_remove/last_dep/in/Cargo.toml | 13 +++++++++ .../cargo_remove/last_dep/in/src/lib.rs | 1 + tests/testsuite/cargo_remove/last_dep/mod.rs | 28 +++++++++++++++++++ .../cargo_remove/last_dep/out/Cargo.toml | 10 +++++++ .../cargo_remove/last_dep/stderr.term.svg | 27 ++++++++++++++++++ tests/testsuite/cargo_remove/mod.rs | 1 + 6 files changed, 80 insertions(+) create mode 100644 tests/testsuite/cargo_remove/last_dep/in/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/last_dep/in/src/lib.rs create mode 100644 tests/testsuite/cargo_remove/last_dep/mod.rs create mode 100644 tests/testsuite/cargo_remove/last_dep/out/Cargo.toml create mode 100644 tests/testsuite/cargo_remove/last_dep/stderr.term.svg diff --git a/tests/testsuite/cargo_remove/last_dep/in/Cargo.toml b/tests/testsuite/cargo_remove/last_dep/in/Cargo.toml new file mode 100644 index 00000000000..5e6cc267d4e --- /dev/null +++ b/tests/testsuite/cargo_remove/last_dep/in/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" +edition = "2015" + +[[bin]] +name = "main" +path = "src/main.rs" + +[dependencies] +docopt = "0.6" + +[features] diff --git a/tests/testsuite/cargo_remove/last_dep/in/src/lib.rs b/tests/testsuite/cargo_remove/last_dep/in/src/lib.rs new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/tests/testsuite/cargo_remove/last_dep/in/src/lib.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/last_dep/mod.rs b/tests/testsuite/cargo_remove/last_dep/mod.rs new file mode 100644 index 00000000000..6e287f223ce --- /dev/null +++ b/tests/testsuite/cargo_remove/last_dep/mod.rs @@ -0,0 +1,28 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::current_dir; +use cargo_test_support::file; +use cargo_test_support::prelude::*; +use cargo_test_support::str; +use cargo_test_support::CargoCommandExt; +use cargo_test_support::Project; + +#[cargo_test] +fn case() { + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + + let project = Project::from_template(current_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["docopt"]) + .current_dir(cwd) + .assert() + .success() + .stdout_eq(str![""]) + .stderr_eq(file!["stderr.term.svg"]); + + assert_ui().subset_matches(current_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/last_dep/out/Cargo.toml b/tests/testsuite/cargo_remove/last_dep/out/Cargo.toml new file mode 100644 index 00000000000..ad97bd5ca25 --- /dev/null +++ b/tests/testsuite/cargo_remove/last_dep/out/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "cargo-remove-test-fixture" +version = "0.1.0" +edition = "2015" + +[[bin]] +name = "main" +path = "src/main.rs" + +[features] diff --git a/tests/testsuite/cargo_remove/last_dep/stderr.term.svg b/tests/testsuite/cargo_remove/last_dep/stderr.term.svg new file mode 100644 index 00000000000..51bcb0f99a9 --- /dev/null +++ b/tests/testsuite/cargo_remove/last_dep/stderr.term.svg @@ -0,0 +1,27 @@ + + + + + + + Removing docopt from dependencies + + + + + + diff --git a/tests/testsuite/cargo_remove/mod.rs b/tests/testsuite/cargo_remove/mod.rs index 7b9190642ca..92a62439da5 100644 --- a/tests/testsuite/cargo_remove/mod.rs +++ b/tests/testsuite/cargo_remove/mod.rs @@ -15,6 +15,7 @@ mod invalid_section; mod invalid_section_dep; mod invalid_target; mod invalid_target_dep; +mod last_dep; mod multiple_deps; mod multiple_dev; mod no_arg; From 589d09917cbadd5e63655f38173d223615221fc1 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 22 Nov 2024 16:29:38 -0600 Subject: [PATCH 109/525] test(script): Verify manifest modification commands --- tests/testsuite/cargo_add/mod.rs | 3 ++ .../script_bare/in/cargo-test-fixture.rs | 1 + tests/testsuite/cargo_add/script_bare/mod.rs | 39 +++++++++++++++ .../script_bare/out/cargo-test-fixture.rs | 1 + .../cargo_add/script_bare/stderr.term.svg | 50 +++++++++++++++++++ .../in/cargo-test-fixture.rs | 7 +++ .../cargo_add/script_frontmatter/mod.rs | 39 +++++++++++++++ .../out/cargo-test-fixture.rs | 7 +++ .../script_frontmatter/stderr.term.svg | 47 +++++++++++++++++ .../script_shebang/in/cargo-test-fixture.rs | 3 ++ .../testsuite/cargo_add/script_shebang/mod.rs | 39 +++++++++++++++ .../script_shebang/out/cargo-test-fixture.rs | 3 ++ .../cargo_add/script_shebang/stderr.term.svg | 50 +++++++++++++++++++ tests/testsuite/cargo_remove/mod.rs | 2 + .../script/in/cargo-remove-test-fixture.rs | 23 +++++++++ tests/testsuite/cargo_remove/script/mod.rs | 40 +++++++++++++++ .../script/out/cargo-remove-test-fixture.rs | 23 +++++++++ .../cargo_remove/script/stderr.term.svg | 50 +++++++++++++++++++ .../in/cargo-remove-test-fixture.rs | 7 +++ .../testsuite/cargo_remove/script_last/mod.rs | 30 +++++++++++ .../out/cargo-remove-test-fixture.rs | 7 +++ .../cargo_remove/script_last/stderr.term.svg | 50 +++++++++++++++++++ 22 files changed, 521 insertions(+) create mode 100644 tests/testsuite/cargo_add/script_bare/in/cargo-test-fixture.rs create mode 100644 tests/testsuite/cargo_add/script_bare/mod.rs create mode 100644 tests/testsuite/cargo_add/script_bare/out/cargo-test-fixture.rs create mode 100644 tests/testsuite/cargo_add/script_bare/stderr.term.svg create mode 100644 tests/testsuite/cargo_add/script_frontmatter/in/cargo-test-fixture.rs create mode 100644 tests/testsuite/cargo_add/script_frontmatter/mod.rs create mode 100644 tests/testsuite/cargo_add/script_frontmatter/out/cargo-test-fixture.rs create mode 100644 tests/testsuite/cargo_add/script_frontmatter/stderr.term.svg create mode 100644 tests/testsuite/cargo_add/script_shebang/in/cargo-test-fixture.rs create mode 100644 tests/testsuite/cargo_add/script_shebang/mod.rs create mode 100644 tests/testsuite/cargo_add/script_shebang/out/cargo-test-fixture.rs create mode 100644 tests/testsuite/cargo_add/script_shebang/stderr.term.svg create mode 100644 tests/testsuite/cargo_remove/script/in/cargo-remove-test-fixture.rs create mode 100644 tests/testsuite/cargo_remove/script/mod.rs create mode 100644 tests/testsuite/cargo_remove/script/out/cargo-remove-test-fixture.rs create mode 100644 tests/testsuite/cargo_remove/script/stderr.term.svg create mode 100644 tests/testsuite/cargo_remove/script_last/in/cargo-remove-test-fixture.rs create mode 100644 tests/testsuite/cargo_remove/script_last/mod.rs create mode 100644 tests/testsuite/cargo_remove/script_last/out/cargo-remove-test-fixture.rs create mode 100644 tests/testsuite/cargo_remove/script_last/stderr.term.svg diff --git a/tests/testsuite/cargo_add/mod.rs b/tests/testsuite/cargo_add/mod.rs index ff910628ee8..ec00ba48c8a 100644 --- a/tests/testsuite/cargo_add/mod.rs +++ b/tests/testsuite/cargo_add/mod.rs @@ -138,6 +138,9 @@ mod rustc_ignore; mod rustc_incompatible; mod rustc_latest; mod rustc_older; +mod script_bare; +mod script_frontmatter; +mod script_shebang; mod sorted_table_with_dotted_item; mod target; mod target_cfg; diff --git a/tests/testsuite/cargo_add/script_bare/in/cargo-test-fixture.rs b/tests/testsuite/cargo_add/script_bare/in/cargo-test-fixture.rs new file mode 100644 index 00000000000..f328e4d9d04 --- /dev/null +++ b/tests/testsuite/cargo_add/script_bare/in/cargo-test-fixture.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/testsuite/cargo_add/script_bare/mod.rs b/tests/testsuite/cargo_add/script_bare/mod.rs new file mode 100644 index 00000000000..3b0f34317ab --- /dev/null +++ b/tests/testsuite/cargo_add/script_bare/mod.rs @@ -0,0 +1,39 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::current_dir; +use cargo_test_support::file; +use cargo_test_support::prelude::*; +use cargo_test_support::str; +use cargo_test_support::Project; + +#[cargo_test] +fn case() { + cargo_test_support::registry::init(); + for ver in [ + "0.1.1+my-package", + "0.2.0+my-package", + "0.2.3+my-package", + "0.4.1+my-package", + "20.0.0+my-package", + "99999.0.0+my-package", + "99999.0.0-alpha.1+my-package", + ] { + cargo_test_support::registry::Package::new("my-package", ver).publish(); + } + + let project = Project::from_template(current_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .masquerade_as_nightly_cargo(&["script"]) + .arg("-Zscript") + .arg("add") + .arg_line("--manifest-path cargo-test-fixture.rs my-package") + .current_dir(cwd) + .assert() + .failure() + .stdout_eq(str![""]) + .stderr_eq(file!["stderr.term.svg"]); + + assert_ui().subset_matches(current_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/script_bare/out/cargo-test-fixture.rs b/tests/testsuite/cargo_add/script_bare/out/cargo-test-fixture.rs new file mode 100644 index 00000000000..f328e4d9d04 --- /dev/null +++ b/tests/testsuite/cargo_add/script_bare/out/cargo-test-fixture.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/testsuite/cargo_add/script_bare/stderr.term.svg b/tests/testsuite/cargo_add/script_bare/stderr.term.svg new file mode 100644 index 00000000000..f0187d20a34 --- /dev/null +++ b/tests/testsuite/cargo_add/script_bare/stderr.term.svg @@ -0,0 +1,50 @@ + + + + + + + warning: `package.edition` is unspecified, defaulting to `[..]` + + error: Unable to parse Cargo.toml + + + + Caused by: + + Manifest not valid TOML + + + + Caused by: + + TOML parse error at line 1, column 4 + + | + + 1 | fn main() {} + + | ^ + + expected `.`, `=` + + + + + + diff --git a/tests/testsuite/cargo_add/script_frontmatter/in/cargo-test-fixture.rs b/tests/testsuite/cargo_add/script_frontmatter/in/cargo-test-fixture.rs new file mode 100644 index 00000000000..129939352ae --- /dev/null +++ b/tests/testsuite/cargo_add/script_frontmatter/in/cargo-test-fixture.rs @@ -0,0 +1,7 @@ +--- +[package] +edition = "2015" +--- + +fn main() { +} diff --git a/tests/testsuite/cargo_add/script_frontmatter/mod.rs b/tests/testsuite/cargo_add/script_frontmatter/mod.rs new file mode 100644 index 00000000000..3b0f34317ab --- /dev/null +++ b/tests/testsuite/cargo_add/script_frontmatter/mod.rs @@ -0,0 +1,39 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::current_dir; +use cargo_test_support::file; +use cargo_test_support::prelude::*; +use cargo_test_support::str; +use cargo_test_support::Project; + +#[cargo_test] +fn case() { + cargo_test_support::registry::init(); + for ver in [ + "0.1.1+my-package", + "0.2.0+my-package", + "0.2.3+my-package", + "0.4.1+my-package", + "20.0.0+my-package", + "99999.0.0+my-package", + "99999.0.0-alpha.1+my-package", + ] { + cargo_test_support::registry::Package::new("my-package", ver).publish(); + } + + let project = Project::from_template(current_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .masquerade_as_nightly_cargo(&["script"]) + .arg("-Zscript") + .arg("add") + .arg_line("--manifest-path cargo-test-fixture.rs my-package") + .current_dir(cwd) + .assert() + .failure() + .stdout_eq(str![""]) + .stderr_eq(file!["stderr.term.svg"]); + + assert_ui().subset_matches(current_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/script_frontmatter/out/cargo-test-fixture.rs b/tests/testsuite/cargo_add/script_frontmatter/out/cargo-test-fixture.rs new file mode 100644 index 00000000000..129939352ae --- /dev/null +++ b/tests/testsuite/cargo_add/script_frontmatter/out/cargo-test-fixture.rs @@ -0,0 +1,7 @@ +--- +[package] +edition = "2015" +--- + +fn main() { +} diff --git a/tests/testsuite/cargo_add/script_frontmatter/stderr.term.svg b/tests/testsuite/cargo_add/script_frontmatter/stderr.term.svg new file mode 100644 index 00000000000..2b06ad6ded0 --- /dev/null +++ b/tests/testsuite/cargo_add/script_frontmatter/stderr.term.svg @@ -0,0 +1,47 @@ + + + + + + + error: Unable to parse Cargo.toml + + + + Caused by: + + Manifest not valid TOML + + + + Caused by: + + TOML parse error at line 1, column 4 + + | + + 1 | --- + + | ^ + + expected `.`, `=` + + + + + + diff --git a/tests/testsuite/cargo_add/script_shebang/in/cargo-test-fixture.rs b/tests/testsuite/cargo_add/script_shebang/in/cargo-test-fixture.rs new file mode 100644 index 00000000000..627aa3d895f --- /dev/null +++ b/tests/testsuite/cargo_add/script_shebang/in/cargo-test-fixture.rs @@ -0,0 +1,3 @@ +#!/usr/bin/env cargo + +fn main() {} diff --git a/tests/testsuite/cargo_add/script_shebang/mod.rs b/tests/testsuite/cargo_add/script_shebang/mod.rs new file mode 100644 index 00000000000..3b0f34317ab --- /dev/null +++ b/tests/testsuite/cargo_add/script_shebang/mod.rs @@ -0,0 +1,39 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::current_dir; +use cargo_test_support::file; +use cargo_test_support::prelude::*; +use cargo_test_support::str; +use cargo_test_support::Project; + +#[cargo_test] +fn case() { + cargo_test_support::registry::init(); + for ver in [ + "0.1.1+my-package", + "0.2.0+my-package", + "0.2.3+my-package", + "0.4.1+my-package", + "20.0.0+my-package", + "99999.0.0+my-package", + "99999.0.0-alpha.1+my-package", + ] { + cargo_test_support::registry::Package::new("my-package", ver).publish(); + } + + let project = Project::from_template(current_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .masquerade_as_nightly_cargo(&["script"]) + .arg("-Zscript") + .arg("add") + .arg_line("--manifest-path cargo-test-fixture.rs my-package") + .current_dir(cwd) + .assert() + .failure() + .stdout_eq(str![""]) + .stderr_eq(file!["stderr.term.svg"]); + + assert_ui().subset_matches(current_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/script_shebang/out/cargo-test-fixture.rs b/tests/testsuite/cargo_add/script_shebang/out/cargo-test-fixture.rs new file mode 100644 index 00000000000..627aa3d895f --- /dev/null +++ b/tests/testsuite/cargo_add/script_shebang/out/cargo-test-fixture.rs @@ -0,0 +1,3 @@ +#!/usr/bin/env cargo + +fn main() {} diff --git a/tests/testsuite/cargo_add/script_shebang/stderr.term.svg b/tests/testsuite/cargo_add/script_shebang/stderr.term.svg new file mode 100644 index 00000000000..934cf3776bc --- /dev/null +++ b/tests/testsuite/cargo_add/script_shebang/stderr.term.svg @@ -0,0 +1,50 @@ + + + + + + + warning: `package.edition` is unspecified, defaulting to `[..]` + + error: Unable to parse Cargo.toml + + + + Caused by: + + Manifest not valid TOML + + + + Caused by: + + TOML parse error at line 3, column 4 + + | + + 3 | fn main() {} + + | ^ + + expected `.`, `=` + + + + + + diff --git a/tests/testsuite/cargo_remove/mod.rs b/tests/testsuite/cargo_remove/mod.rs index 92a62439da5..3510ece4b40 100644 --- a/tests/testsuite/cargo_remove/mod.rs +++ b/tests/testsuite/cargo_remove/mod.rs @@ -25,6 +25,8 @@ mod optional_dep_feature_formatting; mod optional_feature; mod package; mod remove_basic; +mod script; +mod script_last; mod skip_gc_glob_profile; mod target; mod target_build; diff --git a/tests/testsuite/cargo_remove/script/in/cargo-remove-test-fixture.rs b/tests/testsuite/cargo_remove/script/in/cargo-remove-test-fixture.rs new file mode 100644 index 00000000000..99662ce110a --- /dev/null +++ b/tests/testsuite/cargo_remove/script/in/cargo-remove-test-fixture.rs @@ -0,0 +1,23 @@ +--- +edition = "2015" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] +--- + +fn main() { +} diff --git a/tests/testsuite/cargo_remove/script/mod.rs b/tests/testsuite/cargo_remove/script/mod.rs new file mode 100644 index 00000000000..b5d533b7e41 --- /dev/null +++ b/tests/testsuite/cargo_remove/script/mod.rs @@ -0,0 +1,40 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::current_dir; +use cargo_test_support::file; +use cargo_test_support::prelude::*; +use cargo_test_support::str; +use cargo_test_support::CargoCommandExt; +use cargo_test_support::Project; + +#[cargo_test] +fn case() { + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + + let project = Project::from_template(current_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .masquerade_as_nightly_cargo(&["script"]) + .arg("-Zscript") + .arg("remove") + .arg_line("--manifest-path cargo-remove-test-fixture.rs docopt") + .current_dir(cwd) + .assert() + .failure() + .stdout_eq(str![""]) + .stderr_eq(file!["stderr.term.svg"]); + + assert_ui().subset_matches(current_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/script/out/cargo-remove-test-fixture.rs b/tests/testsuite/cargo_remove/script/out/cargo-remove-test-fixture.rs new file mode 100644 index 00000000000..99662ce110a --- /dev/null +++ b/tests/testsuite/cargo_remove/script/out/cargo-remove-test-fixture.rs @@ -0,0 +1,23 @@ +--- +edition = "2015" + +[build-dependencies] +semver = "0.1.0" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] +--- + +fn main() { +} diff --git a/tests/testsuite/cargo_remove/script/stderr.term.svg b/tests/testsuite/cargo_remove/script/stderr.term.svg new file mode 100644 index 00000000000..4165d0ac770 --- /dev/null +++ b/tests/testsuite/cargo_remove/script/stderr.term.svg @@ -0,0 +1,50 @@ + + + + + + + warning: `package.edition` is unspecified, defaulting to `[..]` + + error: Unable to parse Cargo.toml + + + + Caused by: + + Manifest not valid TOML + + + + Caused by: + + TOML parse error at line 1, column 4 + + | + + 1 | --- + + | ^ + + expected `.`, `=` + + + + + + diff --git a/tests/testsuite/cargo_remove/script_last/in/cargo-remove-test-fixture.rs b/tests/testsuite/cargo_remove/script_last/in/cargo-remove-test-fixture.rs new file mode 100644 index 00000000000..67950478399 --- /dev/null +++ b/tests/testsuite/cargo_remove/script_last/in/cargo-remove-test-fixture.rs @@ -0,0 +1,7 @@ +--- +[dependencies] +docopt = "0.6" +--- + +fn main() { +} diff --git a/tests/testsuite/cargo_remove/script_last/mod.rs b/tests/testsuite/cargo_remove/script_last/mod.rs new file mode 100644 index 00000000000..68da9cc2d78 --- /dev/null +++ b/tests/testsuite/cargo_remove/script_last/mod.rs @@ -0,0 +1,30 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::current_dir; +use cargo_test_support::file; +use cargo_test_support::prelude::*; +use cargo_test_support::str; +use cargo_test_support::CargoCommandExt; +use cargo_test_support::Project; + +#[cargo_test] +fn case() { + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + + let project = Project::from_template(current_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .masquerade_as_nightly_cargo(&["script"]) + .arg("-Zscript") + .arg("remove") + .arg_line("--manifest-path cargo-remove-test-fixture.rs docopt") + .current_dir(cwd) + .assert() + .failure() + .stdout_eq(str![""]) + .stderr_eq(file!["stderr.term.svg"]); + + assert_ui().subset_matches(current_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_remove/script_last/out/cargo-remove-test-fixture.rs b/tests/testsuite/cargo_remove/script_last/out/cargo-remove-test-fixture.rs new file mode 100644 index 00000000000..67950478399 --- /dev/null +++ b/tests/testsuite/cargo_remove/script_last/out/cargo-remove-test-fixture.rs @@ -0,0 +1,7 @@ +--- +[dependencies] +docopt = "0.6" +--- + +fn main() { +} diff --git a/tests/testsuite/cargo_remove/script_last/stderr.term.svg b/tests/testsuite/cargo_remove/script_last/stderr.term.svg new file mode 100644 index 00000000000..4165d0ac770 --- /dev/null +++ b/tests/testsuite/cargo_remove/script_last/stderr.term.svg @@ -0,0 +1,50 @@ + + + + + + + warning: `package.edition` is unspecified, defaulting to `[..]` + + error: Unable to parse Cargo.toml + + + + Caused by: + + Manifest not valid TOML + + + + Caused by: + + TOML parse error at line 1, column 4 + + | + + 1 | --- + + | ^ + + expected `.`, `=` + + + + + + From 6ebd1aec1709b7868c41f543b5beebaf8128798b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 25 Nov 2024 10:36:57 -0600 Subject: [PATCH 110/525] refactor(remove): Clarify variable names --- src/bin/cargo/commands/remove.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/bin/cargo/commands/remove.rs b/src/bin/cargo/commands/remove.rs index 833fd00c549..6bccac53d64 100644 --- a/src/bin/cargo/commands/remove.rs +++ b/src/bin/cargo/commands/remove.rs @@ -161,7 +161,7 @@ fn parse_section(args: &ArgMatches) -> DepTable { /// Clean up the workspace.dependencies, profile, patch, and replace sections of the root manifest /// by removing dependencies which no longer have a reference to them. fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { - let mut manifest: toml_edit::DocumentMut = + let mut workspace_manifest: toml_edit::DocumentMut = cargo_util::paths::read(workspace.root_manifest())?.parse()?; let mut is_modified = true; @@ -177,8 +177,8 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { let mut dependencies = members .into_iter() - .flat_map(|(manifest, unstable_features)| { - manifest + .flat_map(|(member_manifest, unstable_features)| { + member_manifest .get_sections() .into_iter() .flat_map(move |(_, table)| { @@ -190,7 +190,7 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { Dependency::from_toml( workspace.gctx(), workspace.root(), - &manifest.path, + &member_manifest.path, &unstable_features, key, item, @@ -203,7 +203,7 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { // Clean up the workspace.dependencies section and replace instances of // workspace dependencies with their definitions - if let Some(toml_edit::Item::Table(deps_table)) = manifest + if let Some(toml_edit::Item::Table(deps_table)) = workspace_manifest .get_mut("workspace") .and_then(|t| t.get_mut("dependencies")) { @@ -246,7 +246,9 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { // Example tables: // - profile.dev.package.foo // - profile.release.package."foo:2.1.0" - if let Some(toml_edit::Item::Table(profile_section_table)) = manifest.get_mut("profile") { + if let Some(toml_edit::Item::Table(profile_section_table)) = + workspace_manifest.get_mut("profile") + { profile_section_table.set_implicit(true); for (_, item) in profile_section_table.iter_mut() { @@ -280,7 +282,7 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { } // Clean up the replace section - if let Some(toml_edit::Item::Table(table)) = manifest.get_mut("replace") { + if let Some(toml_edit::Item::Table(table)) = workspace_manifest.get_mut("replace") { table.set_implicit(true); for (key, item) in table.iter_mut() { @@ -298,7 +300,7 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { if is_modified { cargo_util::paths::write_atomic( workspace.root_manifest(), - manifest.to_string().as_bytes(), + workspace_manifest.to_string().as_bytes(), )?; } @@ -340,12 +342,12 @@ fn spec_has_match( /// Removes unused patches from the manifest fn gc_unused_patches(workspace: &Workspace<'_>, resolve: &Resolve) -> CargoResult { - let mut manifest: toml_edit::DocumentMut = + let mut workspace_manifest: toml_edit::DocumentMut = cargo_util::paths::read(workspace.root_manifest())?.parse()?; let mut modified = false; // Clean up the patch section - if let Some(toml_edit::Item::Table(patch_section_table)) = manifest.get_mut("patch") { + if let Some(toml_edit::Item::Table(patch_section_table)) = workspace_manifest.get_mut("patch") { patch_section_table.set_implicit(true); for (_, item) in patch_section_table.iter_mut() { @@ -383,7 +385,10 @@ fn gc_unused_patches(workspace: &Workspace<'_>, resolve: &Resolve) -> CargoResul } if modified { - cargo_util::paths::write(workspace.root_manifest(), manifest.to_string().as_bytes())?; + cargo_util::paths::write( + workspace.root_manifest(), + workspace_manifest.to_string().as_bytes(), + )?; } Ok(modified) From 0b994144793186bd3874bf95fa453fbb4a7fce1c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 25 Nov 2024 10:40:59 -0600 Subject: [PATCH 111/525] refactor(remove): Consolidate how we mutate manifests This will make it easier to add cargo script support --- src/bin/cargo/commands/remove.rs | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/bin/cargo/commands/remove.rs b/src/bin/cargo/commands/remove.rs index 6bccac53d64..d65410618c7 100644 --- a/src/bin/cargo/commands/remove.rs +++ b/src/bin/cargo/commands/remove.rs @@ -161,8 +161,7 @@ fn parse_section(args: &ArgMatches) -> DepTable { /// Clean up the workspace.dependencies, profile, patch, and replace sections of the root manifest /// by removing dependencies which no longer have a reference to them. fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { - let mut workspace_manifest: toml_edit::DocumentMut = - cargo_util::paths::read(workspace.root_manifest())?.parse()?; + let mut workspace_manifest = LocalManifest::try_new(workspace.root_manifest())?; let mut is_modified = true; let members = workspace @@ -204,6 +203,7 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { // Clean up the workspace.dependencies section and replace instances of // workspace dependencies with their definitions if let Some(toml_edit::Item::Table(deps_table)) = workspace_manifest + .data .get_mut("workspace") .and_then(|t| t.get_mut("dependencies")) { @@ -247,7 +247,7 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { // - profile.dev.package.foo // - profile.release.package."foo:2.1.0" if let Some(toml_edit::Item::Table(profile_section_table)) = - workspace_manifest.get_mut("profile") + workspace_manifest.data.get_mut("profile") { profile_section_table.set_implicit(true); @@ -282,7 +282,7 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { } // Clean up the replace section - if let Some(toml_edit::Item::Table(table)) = workspace_manifest.get_mut("replace") { + if let Some(toml_edit::Item::Table(table)) = workspace_manifest.data.get_mut("replace") { table.set_implicit(true); for (key, item) in table.iter_mut() { @@ -298,10 +298,7 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> { } if is_modified { - cargo_util::paths::write_atomic( - workspace.root_manifest(), - workspace_manifest.to_string().as_bytes(), - )?; + workspace_manifest.write()?; } Ok(()) @@ -342,12 +339,13 @@ fn spec_has_match( /// Removes unused patches from the manifest fn gc_unused_patches(workspace: &Workspace<'_>, resolve: &Resolve) -> CargoResult { - let mut workspace_manifest: toml_edit::DocumentMut = - cargo_util::paths::read(workspace.root_manifest())?.parse()?; + let mut workspace_manifest = LocalManifest::try_new(workspace.root_manifest())?; let mut modified = false; // Clean up the patch section - if let Some(toml_edit::Item::Table(patch_section_table)) = workspace_manifest.get_mut("patch") { + if let Some(toml_edit::Item::Table(patch_section_table)) = + workspace_manifest.data.get_mut("patch") + { patch_section_table.set_implicit(true); for (_, item) in patch_section_table.iter_mut() { @@ -385,10 +383,7 @@ fn gc_unused_patches(workspace: &Workspace<'_>, resolve: &Resolve) -> CargoResul } if modified { - cargo_util::paths::write( - workspace.root_manifest(), - workspace_manifest.to_string().as_bytes(), - )?; + workspace_manifest.write()?; } Ok(modified) From 7ec1867ba04e98aae69b90ec025a3ac34fc8a333 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 22 Nov 2024 16:34:00 -0600 Subject: [PATCH 112/525] refactor(toml): Give more specific name to ScriptSource --- src/cargo/util/toml/embedded.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index 155b54cb18c..2a6d659c4f6 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -189,15 +189,15 @@ fn sanitize_name(name: &str) -> String { } #[derive(Debug)] -struct Source<'s> { +struct ScriptSource<'s> { shebang: Option<&'s str>, info: Option<&'s str>, frontmatter: Option<&'s str>, content: &'s str, } -fn split_source(input: &str) -> CargoResult> { - let mut source = Source { +fn split_source(input: &str) -> CargoResult> { + let mut source = ScriptSource { shebang: None, info: None, frontmatter: None, From e82a4beaed1f1b72f6ecba5548cafdb766128ffc Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 22 Nov 2024 16:36:22 -0600 Subject: [PATCH 113/525] refactor(toml): Move the parse fn onto ScriptSource --- src/cargo/util/toml/embedded.rs | 154 ++++++++++++++++---------------- 1 file changed, 78 insertions(+), 76 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index 2a6d659c4f6..8f7237872c3 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -21,7 +21,7 @@ pub(super) fn expand_manifest( path: &std::path::Path, gctx: &GlobalContext, ) -> CargoResult { - let source = split_source(content)?; + let source = ScriptSource::parse(content)?; if let Some(frontmatter) = source.frontmatter { match source.info { Some("cargo") | None => {} @@ -196,87 +196,89 @@ struct ScriptSource<'s> { content: &'s str, } -fn split_source(input: &str) -> CargoResult> { - let mut source = ScriptSource { - shebang: None, - info: None, - frontmatter: None, - content: input, - }; +impl<'s> ScriptSource<'s> { + fn parse(input: &'s str) -> CargoResult { + let mut source = Self { + shebang: None, + info: None, + frontmatter: None, + content: input, + }; + + // See rust-lang/rust's compiler/rustc_lexer/src/lib.rs's `strip_shebang` + // Shebang must start with `#!` literally, without any preceding whitespace. + // For simplicity we consider any line starting with `#!` a shebang, + // regardless of restrictions put on shebangs by specific platforms. + if let Some(rest) = source.content.strip_prefix("#!") { + // Ok, this is a shebang but if the next non-whitespace token is `[`, + // then it may be valid Rust code, so consider it Rust code. + if rest.trim_start().starts_with('[') { + return Ok(source); + } - // See rust-lang/rust's compiler/rustc_lexer/src/lib.rs's `strip_shebang` - // Shebang must start with `#!` literally, without any preceding whitespace. - // For simplicity we consider any line starting with `#!` a shebang, - // regardless of restrictions put on shebangs by specific platforms. - if let Some(rest) = source.content.strip_prefix("#!") { - // Ok, this is a shebang but if the next non-whitespace token is `[`, - // then it may be valid Rust code, so consider it Rust code. - if rest.trim_start().starts_with('[') { - return Ok(source); + // No other choice than to consider this a shebang. + let newline_end = source + .content + .find('\n') + .map(|pos| pos + 1) + .unwrap_or(source.content.len()); + let (shebang, content) = source.content.split_at(newline_end); + source.shebang = Some(shebang); + source.content = content; } - // No other choice than to consider this a shebang. - let newline_end = source - .content - .find('\n') - .map(|pos| pos + 1) + const FENCE_CHAR: char = '-'; + + let mut trimmed_content = source.content; + while !trimmed_content.is_empty() { + let c = trimmed_content; + let c = c.trim_start_matches([' ', '\t']); + let c = c.trim_start_matches(['\r', '\n']); + if c == trimmed_content { + break; + } + trimmed_content = c; + } + let fence_end = trimmed_content + .char_indices() + .find_map(|(i, c)| (c != FENCE_CHAR).then_some(i)) .unwrap_or(source.content.len()); - let (shebang, content) = source.content.split_at(newline_end); - source.shebang = Some(shebang); + let (fence_pattern, rest) = match fence_end { + 0 => { + return Ok(source); + } + 1 | 2 => { + anyhow::bail!( + "found {fence_end} `{FENCE_CHAR}` in rust frontmatter, expected at least 3" + ) + } + _ => trimmed_content.split_at(fence_end), + }; + let (info, content) = rest.split_once("\n").unwrap_or((rest, "")); + let info = info.trim(); + if !info.is_empty() { + source.info = Some(info); + } source.content = content; - } - const FENCE_CHAR: char = '-'; + let Some((frontmatter, content)) = source.content.split_once(fence_pattern) else { + anyhow::bail!("no closing `{fence_pattern}` found for frontmatter"); + }; + source.frontmatter = Some(frontmatter); + source.content = content; - let mut trimmed_content = source.content; - while !trimmed_content.is_empty() { - let c = trimmed_content; - let c = c.trim_start_matches([' ', '\t']); - let c = c.trim_start_matches(['\r', '\n']); - if c == trimmed_content { - break; - } - trimmed_content = c; - } - let fence_end = trimmed_content - .char_indices() - .find_map(|(i, c)| (c != FENCE_CHAR).then_some(i)) - .unwrap_or(source.content.len()); - let (fence_pattern, rest) = match fence_end { - 0 => { - return Ok(source); - } - 1 | 2 => { - anyhow::bail!( - "found {fence_end} `{FENCE_CHAR}` in rust frontmatter, expected at least 3" - ) + let (line, content) = source + .content + .split_once("\n") + .unwrap_or((source.content, "")); + let line = line.trim(); + if !line.is_empty() { + anyhow::bail!("unexpected trailing content on closing fence: `{line}`"); } - _ => trimmed_content.split_at(fence_end), - }; - let (info, content) = rest.split_once("\n").unwrap_or((rest, "")); - let info = info.trim(); - if !info.is_empty() { - source.info = Some(info); - } - source.content = content; + source.content = content; - let Some((frontmatter, content)) = source.content.split_once(fence_pattern) else { - anyhow::bail!("no closing `{fence_pattern}` found for frontmatter"); - }; - source.frontmatter = Some(frontmatter); - source.content = content; - - let (line, content) = source - .content - .split_once("\n") - .unwrap_or((source.content, "")); - let line = line.trim(); - if !line.is_empty() { - anyhow::bail!("unexpected trailing content on closing fence: `{line}`"); + Ok(source) } - source.content = content; - - Ok(source) } #[cfg(test)] @@ -291,7 +293,7 @@ mod test_expand { fn assert_source(source: &str, expected: impl IntoData) { use std::fmt::Write as _; - let actual = match split_source(source) { + let actual = match ScriptSource::parse(source) { Ok(actual) => actual, Err(err) => panic!("unexpected err: {err}"), }; @@ -497,7 +499,7 @@ content: "\nfn main() {}" #[test] fn split_too_few_dashes() { assert_err( - split_source( + ScriptSource::parse( r#"#!/usr/bin/env cargo -- [dependencies] @@ -513,7 +515,7 @@ fn main() {} #[test] fn split_mismatched_dashes() { assert_err( - split_source( + ScriptSource::parse( r#"#!/usr/bin/env cargo --- [dependencies] @@ -529,7 +531,7 @@ fn main() {} #[test] fn split_missing_close() { assert_err( - split_source( + ScriptSource::parse( r#"#!/usr/bin/env cargo --- [dependencies] From a0ccb13cb1116685d3ad06931246b74e1a2e0cac Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 22 Nov 2024 16:38:36 -0600 Subject: [PATCH 114/525] refactor(toml): Switch to using accessors with ScriptSource --- src/cargo/util/toml/embedded.rs | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index 8f7237872c3..fa603a1eed1 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -22,8 +22,8 @@ pub(super) fn expand_manifest( gctx: &GlobalContext, ) -> CargoResult { let source = ScriptSource::parse(content)?; - if let Some(frontmatter) = source.frontmatter { - match source.info { + if let Some(frontmatter) = source.frontmatter() { + match source.info() { Some("cargo") | None => {} Some(other) => { if let Some(remainder) = other.strip_prefix("cargo,") { @@ -50,7 +50,7 @@ pub(super) fn expand_manifest( ) .into_path_unlocked(); let mut hacked_source = String::new(); - if let Some(shebang) = source.shebang { + if let Some(shebang) = source.shebang() { writeln!(hacked_source, "{shebang}")?; } writeln!(hacked_source)?; // open @@ -58,7 +58,7 @@ pub(super) fn expand_manifest( writeln!(hacked_source)?; } writeln!(hacked_source)?; // close - writeln!(hacked_source, "{}", source.content)?; + writeln!(hacked_source, "{}", source.content())?; if let Some(parent) = hacked_path.parent() { cargo_util::paths::create_dir_all(parent)?; } @@ -279,6 +279,22 @@ impl<'s> ScriptSource<'s> { Ok(source) } + + fn shebang(&self) -> Option<&'s str> { + self.shebang + } + + fn info(&self) -> Option<&'s str> { + self.info + } + + fn frontmatter(&self) -> Option<&'s str> { + self.frontmatter + } + + fn content(&self) -> &'s str { + self.content + } } #[cfg(test)] @@ -299,10 +315,10 @@ mod test_expand { }; let mut rendered = String::new(); - write_optional_field(&mut rendered, "shebang", actual.shebang); - write_optional_field(&mut rendered, "info", actual.info); - write_optional_field(&mut rendered, "frontmatter", actual.frontmatter); - writeln!(&mut rendered, "content: {:?}", actual.content).unwrap(); + write_optional_field(&mut rendered, "shebang", actual.shebang()); + write_optional_field(&mut rendered, "info", actual.info()); + write_optional_field(&mut rendered, "frontmatter", actual.frontmatter()); + writeln!(&mut rendered, "content: {:?}", actual.content()).unwrap(); assert_data_eq!(rendered, expected.raw()); } From 89d1fafbfdece54e32606bdc9398d249e7dd29ba Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 22 Nov 2024 16:39:52 -0600 Subject: [PATCH 115/525] refactor(toml): Expose ScriptSource for reuse --- src/cargo/util/toml/embedded.rs | 12 ++++++------ src/cargo/util/toml/mod.rs | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index fa603a1eed1..126188588b2 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -189,7 +189,7 @@ fn sanitize_name(name: &str) -> String { } #[derive(Debug)] -struct ScriptSource<'s> { +pub struct ScriptSource<'s> { shebang: Option<&'s str>, info: Option<&'s str>, frontmatter: Option<&'s str>, @@ -197,7 +197,7 @@ struct ScriptSource<'s> { } impl<'s> ScriptSource<'s> { - fn parse(input: &'s str) -> CargoResult { + pub fn parse(input: &'s str) -> CargoResult { let mut source = Self { shebang: None, info: None, @@ -280,19 +280,19 @@ impl<'s> ScriptSource<'s> { Ok(source) } - fn shebang(&self) -> Option<&'s str> { + pub fn shebang(&self) -> Option<&'s str> { self.shebang } - fn info(&self) -> Option<&'s str> { + pub fn info(&self) -> Option<&'s str> { self.info } - fn frontmatter(&self) -> Option<&'s str> { + pub fn frontmatter(&self) -> Option<&'s str> { self.frontmatter } - fn content(&self) -> &'s str { + pub fn content(&self) -> &'s str { self.content } } diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index bd1fcf142c9..d59ba0ef4ea 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -39,6 +39,8 @@ mod targets; use self::targets::to_targets; +pub use embedded::ScriptSource; + /// See also `bin/cargo/commands/run.rs`s `is_manifest_command` pub fn is_embedded(path: &Path) -> bool { let ext = path.extension(); From 26844a33315110b913340cd6b86662200c8ed5ee Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 22 Nov 2024 20:56:54 -0600 Subject: [PATCH 116/525] feat(toml): Allow adding/removing from cargo scripts --- src/cargo/util/toml_mut/dependency.rs | 2 + src/cargo/util/toml_mut/manifest.rs | 94 ++++++++++++++++++- tests/testsuite/cargo_add/script_bare/mod.rs | 2 +- .../script_bare/out/cargo-test-fixture.rs | 5 + .../cargo_add/script_bare/stderr.term.svg | 26 ++--- .../cargo_add/script_frontmatter/mod.rs | 2 +- .../out/cargo-test-fixture.rs | 3 + .../script_frontmatter/stderr.term.svg | 28 ++---- .../testsuite/cargo_add/script_shebang/mod.rs | 2 +- .../script_shebang/out/cargo-test-fixture.rs | 4 + .../cargo_add/script_shebang/stderr.term.svg | 26 ++--- tests/testsuite/cargo_remove/script/mod.rs | 2 +- .../script/out/cargo-remove-test-fixture.rs | 1 - .../cargo_remove/script/stderr.term.svg | 28 +----- .../testsuite/cargo_remove/script_last/mod.rs | 2 +- .../out/cargo-remove-test-fixture.rs | 3 +- .../cargo_remove/script_last/stderr.term.svg | 28 +----- 17 files changed, 139 insertions(+), 119 deletions(-) diff --git a/src/cargo/util/toml_mut/dependency.rs b/src/cargo/util/toml_mut/dependency.rs index 78cc58a29b7..6d693d6dda3 100644 --- a/src/cargo/util/toml_mut/dependency.rs +++ b/src/cargo/util/toml_mut/dependency.rs @@ -1252,6 +1252,8 @@ mod tests { let mut local = LocalManifest { path: crate_root.clone(), manifest, + embedded: None, + raw: toml.to_owned(), }; assert_eq!(local.manifest.to_string(), toml); let gctx = GlobalContext::default().unwrap(); diff --git a/src/cargo/util/toml_mut/manifest.rs b/src/cargo/util/toml_mut/manifest.rs index 98033531e3a..79fd24029de 100644 --- a/src/cargo/util/toml_mut/manifest.rs +++ b/src/cargo/util/toml_mut/manifest.rs @@ -11,6 +11,7 @@ use crate::core::dependency::DepKind; use crate::core::{FeatureValue, Features, Workspace}; use crate::util::closest; use crate::util::interning::InternedString; +use crate::util::toml::{is_embedded, ScriptSource}; use crate::{CargoResult, GlobalContext}; /// Dependency table to add deps to. @@ -245,6 +246,10 @@ pub struct LocalManifest { pub path: PathBuf, /// Manifest contents. pub manifest: Manifest, + /// The raw, unparsed package file + pub raw: String, + /// Edit location for an embedded manifest, if relevant + pub embedded: Option, } impl Deref for LocalManifest { @@ -267,18 +272,56 @@ impl LocalManifest { if !path.is_absolute() { anyhow::bail!("can only edit absolute paths, got {}", path.display()); } - let data = cargo_util::paths::read(&path)?; + let raw = cargo_util::paths::read(&path)?; + let mut data = raw.clone(); + let mut embedded = None; + if is_embedded(path) { + let source = ScriptSource::parse(&data)?; + if let Some(frontmatter) = source.frontmatter() { + embedded = Some(Embedded::exists(&data, frontmatter)); + data = frontmatter.to_owned(); + } else if let Some(shebang) = source.shebang() { + embedded = Some(Embedded::after(&data, shebang)); + data = String::new(); + } else { + embedded = Some(Embedded::start()); + data = String::new(); + } + } let manifest = data.parse().context("Unable to parse Cargo.toml")?; Ok(LocalManifest { manifest, path: path.to_owned(), + raw, + embedded, }) } /// Write changes back to the file. pub fn write(&self) -> CargoResult<()> { - let s = self.manifest.data.to_string(); - let new_contents_bytes = s.as_bytes(); + let mut manifest = self.manifest.data.to_string(); + let raw = match self.embedded.as_ref() { + Some(Embedded::Implicit(start)) => { + if !manifest.ends_with("\n") { + manifest.push_str("\n"); + } + let fence = "---\n"; + let prefix = &self.raw[0..*start]; + let suffix = &self.raw[*start..]; + let empty_line = if prefix.is_empty() { "\n" } else { "" }; + format!("{prefix}{fence}{manifest}{fence}{empty_line}{suffix}") + } + Some(Embedded::Explicit(span)) => { + if !manifest.ends_with("\n") { + manifest.push_str("\n"); + } + let prefix = &self.raw[0..span.start]; + let suffix = &self.raw[span.end..]; + format!("{prefix}{manifest}{suffix}") + } + None => manifest, + }; + let new_contents_bytes = raw.as_bytes(); cargo_util::paths::write_atomic(&self.path, new_contents_bytes) } @@ -531,6 +574,51 @@ impl std::fmt::Display for LocalManifest { } } +/// Edit location for an embedded manifest +#[derive(Clone, Debug)] +pub enum Embedded { + /// Manifest is implicit + /// + /// This is the insert location for a frontmatter + Implicit(usize), + /// Manifest is explicit in a frontmatter + /// + /// This is the span of the frontmatter body + Explicit(std::ops::Range), +} + +impl Embedded { + fn start() -> Self { + Self::Implicit(0) + } + + fn after(input: &str, after: &str) -> Self { + let span = substr_span(input, after); + let end = span.end; + Self::Implicit(end) + } + + fn exists(input: &str, exists: &str) -> Self { + let span = substr_span(input, exists); + Self::Explicit(span) + } +} + +fn substr_span(haystack: &str, needle: &str) -> std::ops::Range { + let haystack_start_ptr = haystack.as_ptr(); + let haystack_end_ptr = haystack[haystack.len()..haystack.len()].as_ptr(); + + let needle_start_ptr = needle.as_ptr(); + let needle_end_ptr = needle[needle.len()..needle.len()].as_ptr(); + + assert!(needle_end_ptr < haystack_end_ptr); + assert!(haystack_start_ptr <= needle_start_ptr); + let start = needle_start_ptr as usize - haystack_start_ptr as usize; + let end = start + needle.len(); + + start..end +} + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] enum DependencyStatus { None, diff --git a/tests/testsuite/cargo_add/script_bare/mod.rs b/tests/testsuite/cargo_add/script_bare/mod.rs index 3b0f34317ab..ffa6db6c6f8 100644 --- a/tests/testsuite/cargo_add/script_bare/mod.rs +++ b/tests/testsuite/cargo_add/script_bare/mod.rs @@ -31,7 +31,7 @@ fn case() { .arg_line("--manifest-path cargo-test-fixture.rs my-package") .current_dir(cwd) .assert() - .failure() + .success() .stdout_eq(str![""]) .stderr_eq(file!["stderr.term.svg"]); diff --git a/tests/testsuite/cargo_add/script_bare/out/cargo-test-fixture.rs b/tests/testsuite/cargo_add/script_bare/out/cargo-test-fixture.rs index f328e4d9d04..caf9ce19959 100644 --- a/tests/testsuite/cargo_add/script_bare/out/cargo-test-fixture.rs +++ b/tests/testsuite/cargo_add/script_bare/out/cargo-test-fixture.rs @@ -1 +1,6 @@ +--- +[dependencies] +my-package = "99999.0.0" +--- + fn main() {} diff --git a/tests/testsuite/cargo_add/script_bare/stderr.term.svg b/tests/testsuite/cargo_add/script_bare/stderr.term.svg index f0187d20a34..b5c72375b88 100644 --- a/tests/testsuite/cargo_add/script_bare/stderr.term.svg +++ b/tests/testsuite/cargo_add/script_bare/stderr.term.svg @@ -1,8 +1,8 @@ - +
+ +MSRV: Respected as of "#, + $ver, + r#". + +
"# + ) }; } From f06d53827d0a05018aa640c3ab8759071b18a70b Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 8 Dec 2024 06:54:36 -0800 Subject: [PATCH 179/525] Add note about type and const generics --- src/doc/src/reference/semver.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/doc/src/reference/semver.md b/src/doc/src/reference/semver.md index b48fdc3469d..232f7f3ac50 100644 --- a/src/doc/src/reference/semver.md +++ b/src/doc/src/reference/semver.md @@ -1654,6 +1654,8 @@ See the [edition guide][rpit-capture-guide] and the [reference][rpit-reference] It is a minor change to capture fewer generic parameters in an RPIT. +> Note: All in-scope type and const generic parameters must be either implicitly captured (no `+ use<…>` specified) or explicitly captured (must be listed in `+ use<…>`), and thus currently it is not allowed to change what is captured of those kinds of generics. + [RPIT]: ../../reference/types/impl-trait.md#abstract-return-types [rpit-capture-guide]: ../../edition-guide/rust-2024/rpit-lifetime-capture.html [rpit-reference]: ../../reference/types/impl-trait.md#capturing From 4661f9b924762dbfbe2b74fd2bc41f428838ebe2 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sun, 8 Dec 2024 19:03:16 -0500 Subject: [PATCH 180/525] refactor: use Path::push to construct remap-path-prefix It creates paths with correct separators for different systems. --- src/cargo/core/compiler/mod.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 406280c6dbd..5dc34616d91 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -1310,10 +1310,16 @@ fn trim_paths_args( /// This remap logic aligns with rustc: /// fn sysroot_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString { - let sysroot = &build_runner.bcx.target_data.info(unit.kind).sysroot; let mut remap = OsString::from("--remap-path-prefix="); - remap.push(sysroot); - remap.push("/lib/rustlib/src/rust"); // See also `detect_sysroot_src_path()`. + remap.push({ + // See also `detect_sysroot_src_path()`. + let mut sysroot = build_runner.bcx.target_data.info(unit.kind).sysroot.clone(); + sysroot.push("lib"); + sysroot.push("rustlib"); + sysroot.push("src"); + sysroot.push("rust"); + sysroot + }); remap.push("="); remap.push("/rustc/"); if let Some(commit_hash) = build_runner.bcx.rustc().commit_hash.as_ref() { From f9ef2c547c93d42a776a41b377bf2f28eaa7fb9f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 9 Dec 2024 09:22:40 -0600 Subject: [PATCH 181/525] docs(build-rs): Backtick literal values --- crates/build-rs/src/output.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/build-rs/src/output.rs b/crates/build-rs/src/output.rs index d50f7d349db..7a9bcc4270b 100644 --- a/crates/build-rs/src/output.rs +++ b/crates/build-rs/src/output.rs @@ -171,7 +171,7 @@ pub fn rustc_link_arg_benches(flag: &str) { /// to the symbols from the given lib, and the binary should access them through /// the library target’s public API. /// -/// The optional `KIND` may be one of dylib, static, or framework. See the +/// The optional `KIND` may be one of `dylib`, `static`, or `framework`. See the /// [rustc book][-l] for more detail. /// /// [-l]: https://doc.rust-lang.org/stable/rustc/command-line-arguments.html#option-l-link-lib From 0f621016768887957ccc67eec14e042d8fe33bac Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 9 Dec 2024 10:04:21 -0600 Subject: [PATCH 182/525] perf(build-rs): Always emit :: directives Our MSRV is much higher than 1.77. Also, as time goes on, there is less incentive to drop it below 1.77, especially with the MSRV-aware resolver in 1.84 --- crates/build-rs/src/allow_use.rs | 14 -------------- crates/build-rs/src/output.rs | 12 ++---------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/crates/build-rs/src/allow_use.rs b/crates/build-rs/src/allow_use.rs index 7818e722edb..115441be853 100644 --- a/crates/build-rs/src/allow_use.rs +++ b/crates/build-rs/src/allow_use.rs @@ -1,14 +1,5 @@ use std::{process::Command, sync::OnceLock}; -fn rust_version_minor() -> u32 { - static VERSION_MINOR: OnceLock = OnceLock::new(); - *VERSION_MINOR.get_or_init(|| { - version_minor(&crate::input::cargo_pkg_rust_version().unwrap_or_default()) - // assume build-rs's MSRV if none specified for the current package - .unwrap_or_else(|| version_minor(env!("CARGO_PKG_RUST_VERSION")).unwrap()) - }) -} - fn cargo_version_minor() -> u32 { static VERSION_MINOR: OnceLock = OnceLock::new(); *VERSION_MINOR.get_or_init(|| { @@ -33,11 +24,6 @@ fn version_minor(version: &str) -> Option { Some(minor) } -pub(crate) fn double_colon_directives() -> bool { - // cargo errors on `cargo::` directives with insufficient package.rust-version - rust_version_minor() >= 77 -} - pub(crate) fn check_cfg() -> bool { // emit check-cfg if the toolchain being used supports it cargo_version_minor() >= 80 diff --git a/crates/build-rs/src/output.rs b/crates/build-rs/src/output.rs index 7a9bcc4270b..dcdafeaef24 100644 --- a/crates/build-rs/src/output.rs +++ b/crates/build-rs/src/output.rs @@ -13,11 +13,7 @@ use crate::{ use std::{ffi::OsStr, fmt::Display, fmt::Write, path::Path, str}; fn emit(directive: &str, value: impl Display) { - if allow_use::double_colon_directives() { - println!("cargo::{}={}", directive, value); - } else { - println!("cargo:{}={}", directive, value); - } + println!("cargo::{}={}", directive, value); } /// The `rerun-if-changed` instruction tells Cargo to re-run the build script if the @@ -421,9 +417,5 @@ pub fn metadata(key: &str, val: &str) { panic!("cannot emit metadata: invalid value {val:?}"); } - if allow_use::double_colon_directives() { - emit("metadata", format_args!("{}={}", key, val)); - } else { - emit(key, val); - } + emit("metadata", format_args!("{}={}", key, val)); } From 5e833bfbbb232663232cefaa85073200e8464fd4 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 9 Dec 2024 10:05:42 -0600 Subject: [PATCH 183/525] perf(build-rs): Always emit check-cfg These didn't require an MSRV bump. Worse case, some metadata will be emitted but that shouldn't impact things too negatively. --- crates/build-rs/src/allow_use.rs | 30 ----------------------------- crates/build-rs/src/lib.rs | 1 - crates/build-rs/src/output.rs | 33 +++++++++++++------------------- 3 files changed, 13 insertions(+), 51 deletions(-) delete mode 100644 crates/build-rs/src/allow_use.rs diff --git a/crates/build-rs/src/allow_use.rs b/crates/build-rs/src/allow_use.rs deleted file mode 100644 index 115441be853..00000000000 --- a/crates/build-rs/src/allow_use.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::{process::Command, sync::OnceLock}; - -fn cargo_version_minor() -> u32 { - static VERSION_MINOR: OnceLock = OnceLock::new(); - *VERSION_MINOR.get_or_init(|| { - let out = Command::new(crate::input::cargo()) - .arg("-V") - .output() - .expect("running `cargo -V` should succeed"); - assert!(out.status.success(), "running `cargo -V` should succeed"); - - // > cargo -V # example output - // cargo 1.82.0 (8f40fc59f 2024-08-21) - - let out = std::str::from_utf8(&out.stdout).expect("`cargo -V` should output valid UTF-8"); - let version = out.split(' ').nth(1).unwrap(); - version_minor(version).unwrap() - }) -} - -fn version_minor(version: &str) -> Option { - let minor = version.split('.').nth(1)?; - let minor = minor.parse().unwrap(); - Some(minor) -} - -pub(crate) fn check_cfg() -> bool { - // emit check-cfg if the toolchain being used supports it - cargo_version_minor() >= 80 -} diff --git a/crates/build-rs/src/lib.rs b/crates/build-rs/src/lib.rs index f2339e7e842..bc511f6f629 100644 --- a/crates/build-rs/src/lib.rs +++ b/crates/build-rs/src/lib.rs @@ -40,7 +40,6 @@ MSRV: Respected as of "#, }; } -mod allow_use; mod ident; pub mod input; diff --git a/crates/build-rs/src/output.rs b/crates/build-rs/src/output.rs index dcdafeaef24..573f0b2d8d8 100644 --- a/crates/build-rs/src/output.rs +++ b/crates/build-rs/src/output.rs @@ -6,10 +6,7 @@ //! //! Reference: -use crate::{ - allow_use, - ident::{is_ascii_ident, is_ident}, -}; +use crate::ident::{is_ascii_ident, is_ident}; use std::{ffi::OsStr, fmt::Display, fmt::Write, path::Path, str}; fn emit(directive: &str, value: impl Display) { @@ -309,13 +306,11 @@ pub fn rustc_check_cfgs(keys: &[&str]) { } } - if allow_use::check_cfg() { - let mut directive = keys[0].to_string(); - for key in &keys[1..] { - write!(directive, ", {key}").expect("writing to string should be infallible"); - } - emit("rustc-check-cfg", format_args!("cfg({directive})")); + let mut directive = keys[0].to_string(); + for key in &keys[1..] { + write!(directive, ", {key}").expect("writing to string should be infallible"); } + emit("rustc-check-cfg", format_args!("cfg({directive})")); } /// Add to the list of expected config names that is used when checking the @@ -339,17 +334,15 @@ pub fn rustc_check_cfg_values(key: &str, values: &[&str]) { return; } - if allow_use::check_cfg() { - let mut directive = format!("\"{}\"", values[0].escape_default()); - for value in &values[1..] { - write!(directive, ", \"{}\"", value.escape_default()) - .expect("writing to string should be infallible"); - } - emit( - "rustc-check-cfg", - format_args!("cfg({key}, values({directive}))"), - ); + let mut directive = format!("\"{}\"", values[0].escape_default()); + for value in &values[1..] { + write!(directive, ", \"{}\"", value.escape_default()) + .expect("writing to string should be infallible"); } + emit( + "rustc-check-cfg", + format_args!("cfg({key}, values({directive}))"), + ); } /// The `rustc-env` instruction tells Cargo to set the given environment variable From 260fcab3b35928cd3a5115accd2c59a5cf63f46b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 9 Dec 2024 10:10:28 -0600 Subject: [PATCH 184/525] refactor(build-rs): Clean up 'use's --- crates/build-rs/src/output.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/build-rs/src/output.rs b/crates/build-rs/src/output.rs index 573f0b2d8d8..37b32908cee 100644 --- a/crates/build-rs/src/output.rs +++ b/crates/build-rs/src/output.rs @@ -6,8 +6,11 @@ //! //! Reference: +use std::ffi::OsStr; +use std::path::Path; +use std::{fmt::Display, fmt::Write as _}; + use crate::ident::{is_ascii_ident, is_ident}; -use std::{ffi::OsStr, fmt::Display, fmt::Write, path::Path, str}; fn emit(directive: &str, value: impl Display) { println!("cargo::{}={}", directive, value); From 5fa8a686faf81e01916985901e371bde50e94b07 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 9 Dec 2024 09:30:57 -0600 Subject: [PATCH 185/525] feat(build-rs): Add the 'error' directive --- crates/build-rs/src/output.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/build-rs/src/output.rs b/crates/build-rs/src/output.rs index 37b32908cee..013bc4fc125 100644 --- a/crates/build-rs/src/output.rs +++ b/crates/build-rs/src/output.rs @@ -403,6 +403,26 @@ pub fn warning(message: &str) { emit("warning", message); } +/// The `error` instruction tells Cargo to display an error after the build script has finished +/// running, and then fail the build. +/// +///
+/// +/// Build script libraries should carefully consider if they want to use [`error`] versus +/// returning a `Result`. It may be better to return a `Result`, and allow the caller to decide if the +/// error is fatal or not. The caller can then decide whether or not to display the `Err` variant +/// using [`error`]. +/// +///
+#[doc = respected_msrv!("1.84")] +#[track_caller] +pub fn error(message: &str) { + if message.contains('\n') { + panic!("cannot emit warning: message contains newline"); + } + emit("error", message); +} + /// Metadata, used by `links` scripts. #[track_caller] pub fn metadata(key: &str, val: &str) { From 15892e003c26b97e5502c46538ef10c9e673751a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 9 Dec 2024 10:02:41 -0600 Subject: [PATCH 186/525] refactor(build-rs): Abstract `std::env::var_os` I made a dedicated `Env::is_present` function in case we want to handle `rerun-if-env-changed` differently in that case. --- crates/build-rs/src/input.rs | 95 ++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 32 deletions(-) diff --git a/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs index 6cfdd3268a6..d0c694eb185 100644 --- a/crates/build-rs/src/input.rs +++ b/crates/build-rs/src/input.rs @@ -6,11 +6,41 @@ //! //! Reference: -use std::env::var_os; use std::path::PathBuf; use crate::ident::{is_ascii_ident, is_crate_name, is_feature_name}; +/// Abstraction over environment variables +trait Env { + /// Fetches the environment variable `key`, returning `None` if the variable isn’t set or if + /// there is another error. + /// + /// It may return `None` if the environment variable’s name contains the equal sign character + /// (`=`) or the NUL character. + /// + /// Note that this function will not check if the environment variable is valid Unicode. + fn get(&self, key: &str) -> Option; + + /// Checks the environment variable `key` is present + /// + /// It may not be considered present if the environment variable’s name contains the equal sign character + /// (`=`) or the NUL character. + fn is_present(&self, key: &str) -> bool; +} + +/// Fetches environment variables from the current process +struct ProcessEnv; + +impl Env for ProcessEnv { + fn get(&self, key: &str) -> Option { + std::env::var_os(key) + } + + fn is_present(&self, key: &str) -> bool { + self.get(key).is_some() + } +} + /// Path to the `cargo` binary performing the build. #[track_caller] pub fn cargo() -> PathBuf { @@ -30,7 +60,8 @@ pub fn cargo_manifest_dir() -> PathBuf { /// The path to the manifest of your package. #[track_caller] pub fn cargo_manifest_path() -> PathBuf { - var_os("CARGO_MANIFEST_PATH") + ProcessEnv + .get("CARGO_MANIFEST_PATH") .map(to_path) .unwrap_or_else(|| { let mut path = cargo_manifest_dir(); @@ -42,7 +73,7 @@ pub fn cargo_manifest_path() -> PathBuf { /// The manifest `links` value. #[track_caller] pub fn cargo_manifest_links() -> Option { - var_os("CARGO_MANIFEST_LINKS").map(to_string) + ProcessEnv.get("CARGO_MANIFEST_LINKS").map(to_string) } /// Contains parameters needed for Cargo’s [jobserver] implementation to parallelize @@ -57,7 +88,7 @@ pub fn cargo_manifest_links() -> Option { /// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html #[track_caller] pub fn cargo_makeflags() -> Option { - var_os("CARGO_MAKEFLAGS").map(to_string) + ProcessEnv.get("CARGO_MAKEFLAGS").map(to_string) } /// For each activated feature of the package being built, this will be `true`. @@ -68,7 +99,7 @@ pub fn cargo_feature(name: &str) -> bool { } let name = name.to_uppercase().replace('-', "_"); let key = format!("CARGO_FEATURE_{name}"); - is_present(&key) + ProcessEnv.is_present(&key) } /// For each [configuration option] of the package being built, this will contain @@ -82,7 +113,7 @@ pub fn cargo_feature(name: &str) -> bool { #[track_caller] pub fn cargo_cfg(cfg: &str) -> Option> { let var = cargo_cfg_var(cfg); - var_os(&var).map(|v| to_strings(v, ',')) + ProcessEnv.get(&var).map(|v| to_strings(v, ',')) } #[track_caller] @@ -112,7 +143,7 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_clippy() -> bool { - is_present("CARGO_CFG_CLIPPY") + ProcessEnv.is_present("CARGO_CFG_CLIPPY") } /// If we are compiling with debug assertions enabled. @@ -123,25 +154,25 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_debug_assertions() -> bool { - is_present("CARGO_CFG_DEBUG_ASSERTIONS") + ProcessEnv.is_present("CARGO_CFG_DEBUG_ASSERTIONS") } #[cfg(any())] #[track_caller] pub fn cargo_cfg_doc() -> bool { - is_present("CARGO_CFG_DOC") + ProcessEnv.is_present("CARGO_CFG_DOC") } #[cfg(any())] #[track_caller] pub fn cargo_cfg_docsrs() -> bool { - is_present("CARGO_CFG_DOCSRS") + ProcessEnv.is_present("CARGO_CFG_DOCSRS") } #[cfg(any())] #[track_caller] pub fn cargo_cfg_doctest() -> bool { - is_present("CARGO_CFG_DOCTEST") + ProcessEnv.is_present("CARGO_CFG_DOCTEST") } /// The level of detail provided by derived [`Debug`] implementations. @@ -155,7 +186,7 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_miri() -> bool { - is_present("CARGO_CFG_MIRI") + ProcessEnv.is_present("CARGO_CFG_MIRI") } /// If we are compiling with overflow checks enabled. @@ -163,7 +194,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_overflow_checks() -> bool { - is_present("CARGO_CFG_OVERFLOW_CHECKS") + ProcessEnv.is_present("CARGO_CFG_OVERFLOW_CHECKS") } /// The [panic strategy](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#panic). @@ -175,7 +206,7 @@ mod cfg { /// If the crate is being compiled as a procedural macro. #[track_caller] pub fn cargo_cfg_proc_macro() -> bool { - is_present("CARGO_CFG_PROC_MACRO") + ProcessEnv.is_present("CARGO_CFG_PROC_MACRO") } /// The target relocation model. @@ -189,7 +220,7 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_rustfmt() -> bool { - is_present("CARGO_CFG_RUSTFMT") + ProcessEnv.is_present("CARGO_CFG_RUSTFMT") } /// Sanitizers enabled for the crate being compiled. @@ -197,7 +228,9 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_sanitize() -> Option> { - var_os("CARGO_CFG_SANITIZE").map(|v| to_strings(v, ',')) + ProcessEnv + .get("CARGO_CFG_SANITIZE") + .map(|v| to_strings(v, ',')) } /// If CFI sanitization is generalizing pointers. @@ -205,7 +238,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_sanitizer_cfi_generalize_pointers() -> bool { - is_present("CARGO_CFG_SANITIZER_CFI_GENERALIZE_POINTERS") + ProcessEnv.is_present("CARGO_CFG_SANITIZER_CFI_GENERALIZE_POINTERS") } /// If CFI sanitization is normalizing integers. @@ -213,7 +246,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_sanitizer_cfi_normalize_integers() -> bool { - is_present("CARGO_CFG_SANITIZER_CFI_NORMALIZE_INTEGERS") + ProcessEnv.is_present("CARGO_CFG_SANITIZER_CFI_NORMALIZE_INTEGERS") } /// Disambiguation of the [target ABI](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_abi) @@ -309,7 +342,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_target_thread_local() -> bool { - is_present("CARGO_CFG_TARGET_THREAD_LOCAL") + ProcessEnv.is_present("CARGO_CFG_TARGET_THREAD_LOCAL") } /// The [target vendor](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_vendor). @@ -321,7 +354,7 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_test() -> bool { - is_present("CARGO_CFG_TEST") + ProcessEnv.is_present("CARGO_CFG_TEST") } /// If we are compiling with UB checks enabled. @@ -329,19 +362,19 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_ub_checks() -> bool { - is_present("CARGO_CFG_UB_CHECKS") + ProcessEnv.is_present("CARGO_CFG_UB_CHECKS") } /// Set on [unix-like platforms](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#unix-and-windows). #[track_caller] pub fn cargo_cfg_unix() -> bool { - is_present("CARGO_CFG_UNIX") + ProcessEnv.is_present("CARGO_CFG_UNIX") } /// Set on [windows-like platforms](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#unix-and-windows). #[track_caller] pub fn cargo_cfg_windows() -> bool { - is_present("CARGO_CFG_WINDOWS") + ProcessEnv.is_present("CARGO_CFG_WINDOWS") } } @@ -428,7 +461,7 @@ pub fn dep_metadata(name: &str, key: &str) -> Option { let name = name.to_uppercase().replace('-', "_"); let key = key.to_uppercase().replace('-', "_"); let key = format!("DEP_{name}_{key}"); - var_os(&key).map(to_string) + ProcessEnv.get(&key).map(to_string) } /// The compiler that Cargo has resolved to use. @@ -448,7 +481,7 @@ pub fn rustdoc() -> PathBuf { /// [`build.rustc-wrapper`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustc-wrapper #[track_caller] pub fn rustc_wrapper() -> Option { - var_os("RUSTC_WRAPPER").map(to_path) + ProcessEnv.get("RUSTC_WRAPPER").map(to_path) } /// The rustc wrapper, if any, that Cargo is using for workspace members. See @@ -457,7 +490,7 @@ pub fn rustc_wrapper() -> Option { /// [`build.rustc-workspace-wrapper`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustc-workspace-wrapper #[track_caller] pub fn rustc_workspace_wrapper() -> Option { - var_os("RUSTC_WORKSPACE_WRAPPER").map(to_path) + ProcessEnv.get("RUSTC_WORKSPACE_WRAPPER").map(to_path) } /// The linker that Cargo has resolved to use for the current target, if specified. @@ -465,7 +498,7 @@ pub fn rustc_workspace_wrapper() -> Option { /// [`target.*.linker`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#targettriplelinker #[track_caller] pub fn rustc_linker() -> Option { - var_os("RUSTC_LINKER").map(to_path) + ProcessEnv.get("RUSTC_LINKER").map(to_path) } /// Extra flags that Cargo invokes rustc with. See [`build.rustflags`]. @@ -561,13 +594,11 @@ pub fn cargo_pkg_readme() -> Option { to_opt(var_or_panic("CARGO_PKG_README")).map(to_path) } -fn is_present(key: &str) -> bool { - var_os(key).is_some() -} - #[track_caller] fn var_or_panic(key: &str) -> std::ffi::OsString { - var_os(key).unwrap_or_else(|| panic!("cargo environment variable `{key}` is missing")) + ProcessEnv + .get(key) + .unwrap_or_else(|| panic!("cargo environment variable `{key}` is missing")) } fn to_path(value: std::ffi::OsString) -> PathBuf { From 8e90ce9a2427539dc63332edaf8613dacbcd1baa Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 6 Dec 2024 09:23:22 -0600 Subject: [PATCH 187/525] feat(build-script): Pass CARGO_CFG_FEATURE This may look redundant with `CARGO_FEATURE_=1` except that doesn't provide a lossless way of getting the names, e.g. for forwarding for child builds like tests that need to build examples. This also makes things more consistent as users conditionalize on features through `cfg` and this even fits with what the `CARGO_CFG_` docs say: > For each configuration option of the package being built, this > environment variable will contain the value of the configuration, where > is the name of the configuration uppercased and having - > translated to _. Boolean configurations are present if they are set, and > not present otherwise. Configurations with multiple values are joined to > a single variable with the values delimited by ,. This includes values > built-in to the compiler (which can be seen with rustc --print=cfg) and > values set by build scripts and extra flags passed to rustc (such as > those defined in RUSTFLAGS). Some examples of what these variables are: Fixes #3702 --- crates/build-rs-test-lib/build.rs | 1 + crates/build-rs/src/input.rs | 7 +++++++ crates/build-rs/src/lib.rs | 14 ++++++++++++++ src/cargo/core/compiler/custom_build.rs | 4 ++++ src/doc/src/reference/environment-variables.md | 4 ++-- tests/testsuite/build_script.rs | 5 ++++- 6 files changed, 32 insertions(+), 3 deletions(-) diff --git a/crates/build-rs-test-lib/build.rs b/crates/build-rs-test-lib/build.rs index eff04806a73..0592a374915 100644 --- a/crates/build-rs-test-lib/build.rs +++ b/crates/build-rs-test-lib/build.rs @@ -9,6 +9,7 @@ fn main() { fn smoke_test_inputs() { use build_rs::input::*; dbg!(cargo()); + dbg!(cargo_cfg_feature()); dbg!(cargo_cfg("careful")); #[cfg(feature = "unstable")] dbg!(cargo_cfg_fmt_debug()); diff --git a/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs index 112aa5fed4e..6cfdd3268a6 100644 --- a/crates/build-rs/src/input.rs +++ b/crates/build-rs/src/input.rs @@ -102,6 +102,13 @@ mod cfg { // those disabled with #[cfg(any())] don't seem meaningfully useful // but we list all cfg that are default known to check-cfg + /// Each activated feature of the package being built + #[doc = requires_msrv!("1.85")] + #[track_caller] + pub fn cargo_cfg_feature() -> Vec { + to_strings(var_or_panic(&cargo_cfg_var("target_feature")), ',') + } + #[cfg(any())] #[track_caller] pub fn cargo_cfg_clippy() -> bool { diff --git a/crates/build-rs/src/lib.rs b/crates/build-rs/src/lib.rs index bc511f6f629..6221e44d3bd 100644 --- a/crates/build-rs/src/lib.rs +++ b/crates/build-rs/src/lib.rs @@ -40,6 +40,20 @@ MSRV: Respected as of "#, }; } +macro_rules! requires_msrv { + ($ver:literal) => { + concat!( + r#"
+ +MSRV: Requires "#, + $ver, + r#". + +
"# + ) + }; +} + mod ident; pub mod input; diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index a9928ab58c6..9f7287baace 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -336,6 +336,10 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul } let mut cfg_map = HashMap::new(); + cfg_map.insert( + "feature", + unit.features.iter().map(|s| s.as_str()).collect::>(), + ); for cfg in bcx.target_data.cfg(unit.kind) { match *cfg { Cfg::Name(ref n) => { diff --git a/src/doc/src/reference/environment-variables.md b/src/doc/src/reference/environment-variables.md index c47c5d178d0..0e1dcd3435e 100644 --- a/src/doc/src/reference/environment-variables.md +++ b/src/doc/src/reference/environment-variables.md @@ -342,6 +342,7 @@ let out_dir = env::var("OUT_DIR").unwrap(); values built-in to the compiler (which can be seen with `rustc --print=cfg`) and values set by build scripts and extra flags passed to `rustc` (such as those defined in `RUSTFLAGS`). Some examples of what these variables are: + * `CARGO_CFG_FEATURE` --- Each activated feature of the package being built. * `CARGO_CFG_UNIX` --- Set on [unix-like platforms]. * `CARGO_CFG_WINDOWS` --- Set on [windows-like platforms]. * `CARGO_CFG_TARGET_FAMILY=unix,wasm` --- The [target family]. @@ -356,8 +357,7 @@ let out_dir = env::var("OUT_DIR").unwrap(); > Note that different [target triples][Target Triple] have different sets of `cfg` values, > hence variables present in one target triple might not be available in the other. > - > Some cfg values like `debug_assertions`, `test`, and Cargo features like - > `feature="foo"` are not available. + > Some cfg values like `debug_assertions` and `test` are not available. * `OUT_DIR` --- the folder in which all output and intermediate artifacts should be placed. This folder is inside the build directory for the package being built, and it is unique for the package in question. diff --git a/tests/testsuite/build_script.rs b/tests/testsuite/build_script.rs index 5725c728ea7..78af30351ca 100644 --- a/tests/testsuite/build_script.rs +++ b/tests/testsuite/build_script.rs @@ -156,7 +156,7 @@ fn custom_build_env_vars() { authors = ["wycats@example.com"] [features] - bar_feat = ["bar/foo"] + bar_feat = ["bar/foo", "bar/other-feature"] [dependencies.bar] path = "bar" @@ -176,6 +176,7 @@ fn custom_build_env_vars() { [features] foo = [] + other-feature = [] "#, ) .file("bar/src/lib.rs", "pub fn hello() {}"); @@ -213,6 +214,8 @@ fn custom_build_env_vars() { let _host = env::var("HOST").unwrap(); let _feat = env::var("CARGO_FEATURE_FOO").unwrap(); + let feat = env::var("CARGO_CFG_FEATURE").unwrap(); + assert_eq!(feat, "foo,other-feature"); let cargo = env::var("CARGO").unwrap(); if env::var_os("CHECK_CARGO_IS_RUSTC").is_some() {{ From ec73b49795f7360afc7619f4a505f6b3390c13d7 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Mon, 9 Dec 2024 14:54:36 -0500 Subject: [PATCH 188/525] chore: update auto-label to include build-rs crate --- triagebot.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/triagebot.toml b/triagebot.toml index 514fa761f22..511e43b76fa 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -55,7 +55,11 @@ trigger_files = [ ] [autolabel."A-build-scripts"] -trigger_files = ["src/cargo/core/compiler/custom_build.rs"] +trigger_files = [ + "crates/build-rs-test-lib/", + "crates/build-rs/", + "src/cargo/core/compiler/custom_build.rs", +] [autolabel."A-cache-messages"] trigger_files = ["src/cargo/util/rustc.rs"] From f8e51543af71ee6da839c2a79ab37301f00367c6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 9 Dec 2024 14:00:56 -0600 Subject: [PATCH 189/525] fix(build-rs): Correctly refer to the item in assert Follow up to #14910 --- crates/build-rs/src/output.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/build-rs/src/output.rs b/crates/build-rs/src/output.rs index 013bc4fc125..a1c047e941d 100644 --- a/crates/build-rs/src/output.rs +++ b/crates/build-rs/src/output.rs @@ -418,7 +418,7 @@ pub fn warning(message: &str) { #[track_caller] pub fn error(message: &str) { if message.contains('\n') { - panic!("cannot emit warning: message contains newline"); + panic!("cannot emit error: message contains newline"); } emit("error", message); } From 6c021e0ac815e0b30cd406a43c6d63fcc3d697ef Mon Sep 17 00:00:00 2001 From: Ed Page Date: Mon, 9 Dec 2024 10:28:52 -0600 Subject: [PATCH 190/525] fix(build-rs): Implicitly report `rerun-if-env-changed` for `input` As we abstract aware the env variables, users can't do a good job of reporting these, so we'll do it ourselves. We could make the traits public and take and explicit `env` parameter. I figured we can wait until there is a motivating use case. `build_env` does have a fancier `Env` impl where you pass it HOST and TARGET and it automatically looks up values for those with a fallback scheme. --- crates/build-rs/src/input.rs | 84 +++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs index d0c694eb185..c07264a4f76 100644 --- a/crates/build-rs/src/input.rs +++ b/crates/build-rs/src/input.rs @@ -9,6 +9,10 @@ use std::path::PathBuf; use crate::ident::{is_ascii_ident, is_crate_name, is_feature_name}; +use crate::output::rerun_if_env_changed; + +/// [`ProcessEnv`] wrapper that implicit calls [`rerun_if_env_changed`] +const ENV: RerunIfEnvChanged = RerunIfEnvChanged::new(); /// Abstraction over environment variables trait Env { @@ -41,6 +45,28 @@ impl Env for ProcessEnv { } } +/// [`Env`] wrapper that implicitly calls [`rerun_if_env_changed`] +struct RerunIfEnvChanged(E); + +impl RerunIfEnvChanged { + const fn new() -> Self { + Self(ProcessEnv) + } +} + +impl Env for RerunIfEnvChanged { + #[track_caller] + fn get(&self, key: &str) -> Option { + rerun_if_env_changed(key); + self.0.get(key) + } + + #[track_caller] + fn is_present(&self, key: &str) -> bool { + self.get(key).is_some() + } +} + /// Path to the `cargo` binary performing the build. #[track_caller] pub fn cargo() -> PathBuf { @@ -60,8 +86,7 @@ pub fn cargo_manifest_dir() -> PathBuf { /// The path to the manifest of your package. #[track_caller] pub fn cargo_manifest_path() -> PathBuf { - ProcessEnv - .get("CARGO_MANIFEST_PATH") + ENV.get("CARGO_MANIFEST_PATH") .map(to_path) .unwrap_or_else(|| { let mut path = cargo_manifest_dir(); @@ -73,7 +98,7 @@ pub fn cargo_manifest_path() -> PathBuf { /// The manifest `links` value. #[track_caller] pub fn cargo_manifest_links() -> Option { - ProcessEnv.get("CARGO_MANIFEST_LINKS").map(to_string) + ENV.get("CARGO_MANIFEST_LINKS").map(to_string) } /// Contains parameters needed for Cargo’s [jobserver] implementation to parallelize @@ -88,7 +113,7 @@ pub fn cargo_manifest_links() -> Option { /// [jobserver]: https://www.gnu.org/software/make/manual/html_node/Job-Slots.html #[track_caller] pub fn cargo_makeflags() -> Option { - ProcessEnv.get("CARGO_MAKEFLAGS").map(to_string) + ENV.get("CARGO_MAKEFLAGS").map(to_string) } /// For each activated feature of the package being built, this will be `true`. @@ -99,7 +124,7 @@ pub fn cargo_feature(name: &str) -> bool { } let name = name.to_uppercase().replace('-', "_"); let key = format!("CARGO_FEATURE_{name}"); - ProcessEnv.is_present(&key) + ENV.is_present(&key) } /// For each [configuration option] of the package being built, this will contain @@ -113,7 +138,7 @@ pub fn cargo_feature(name: &str) -> bool { #[track_caller] pub fn cargo_cfg(cfg: &str) -> Option> { let var = cargo_cfg_var(cfg); - ProcessEnv.get(&var).map(|v| to_strings(v, ',')) + ENV.get(&var).map(|v| to_strings(v, ',')) } #[track_caller] @@ -143,7 +168,7 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_clippy() -> bool { - ProcessEnv.is_present("CARGO_CFG_CLIPPY") + ENV.is_present("CARGO_CFG_CLIPPY") } /// If we are compiling with debug assertions enabled. @@ -154,25 +179,25 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_debug_assertions() -> bool { - ProcessEnv.is_present("CARGO_CFG_DEBUG_ASSERTIONS") + ENV.is_present("CARGO_CFG_DEBUG_ASSERTIONS") } #[cfg(any())] #[track_caller] pub fn cargo_cfg_doc() -> bool { - ProcessEnv.is_present("CARGO_CFG_DOC") + ENV.is_present("CARGO_CFG_DOC") } #[cfg(any())] #[track_caller] pub fn cargo_cfg_docsrs() -> bool { - ProcessEnv.is_present("CARGO_CFG_DOCSRS") + ENV.is_present("CARGO_CFG_DOCSRS") } #[cfg(any())] #[track_caller] pub fn cargo_cfg_doctest() -> bool { - ProcessEnv.is_present("CARGO_CFG_DOCTEST") + ENV.is_present("CARGO_CFG_DOCTEST") } /// The level of detail provided by derived [`Debug`] implementations. @@ -186,7 +211,7 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_miri() -> bool { - ProcessEnv.is_present("CARGO_CFG_MIRI") + ENV.is_present("CARGO_CFG_MIRI") } /// If we are compiling with overflow checks enabled. @@ -194,7 +219,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_overflow_checks() -> bool { - ProcessEnv.is_present("CARGO_CFG_OVERFLOW_CHECKS") + ENV.is_present("CARGO_CFG_OVERFLOW_CHECKS") } /// The [panic strategy](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#panic). @@ -206,7 +231,7 @@ mod cfg { /// If the crate is being compiled as a procedural macro. #[track_caller] pub fn cargo_cfg_proc_macro() -> bool { - ProcessEnv.is_present("CARGO_CFG_PROC_MACRO") + ENV.is_present("CARGO_CFG_PROC_MACRO") } /// The target relocation model. @@ -220,7 +245,7 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_rustfmt() -> bool { - ProcessEnv.is_present("CARGO_CFG_RUSTFMT") + ENV.is_present("CARGO_CFG_RUSTFMT") } /// Sanitizers enabled for the crate being compiled. @@ -228,9 +253,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_sanitize() -> Option> { - ProcessEnv - .get("CARGO_CFG_SANITIZE") - .map(|v| to_strings(v, ',')) + ENV.get("CARGO_CFG_SANITIZE").map(|v| to_strings(v, ',')) } /// If CFI sanitization is generalizing pointers. @@ -238,7 +261,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_sanitizer_cfi_generalize_pointers() -> bool { - ProcessEnv.is_present("CARGO_CFG_SANITIZER_CFI_GENERALIZE_POINTERS") + ENV.is_present("CARGO_CFG_SANITIZER_CFI_GENERALIZE_POINTERS") } /// If CFI sanitization is normalizing integers. @@ -246,7 +269,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_sanitizer_cfi_normalize_integers() -> bool { - ProcessEnv.is_present("CARGO_CFG_SANITIZER_CFI_NORMALIZE_INTEGERS") + ENV.is_present("CARGO_CFG_SANITIZER_CFI_NORMALIZE_INTEGERS") } /// Disambiguation of the [target ABI](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_abi) @@ -342,7 +365,7 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_target_thread_local() -> bool { - ProcessEnv.is_present("CARGO_CFG_TARGET_THREAD_LOCAL") + ENV.is_present("CARGO_CFG_TARGET_THREAD_LOCAL") } /// The [target vendor](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#target_vendor). @@ -354,7 +377,7 @@ mod cfg { #[cfg(any())] #[track_caller] pub fn cargo_cfg_test() -> bool { - ProcessEnv.is_present("CARGO_CFG_TEST") + ENV.is_present("CARGO_CFG_TEST") } /// If we are compiling with UB checks enabled. @@ -362,19 +385,19 @@ mod cfg { #[cfg(feature = "unstable")] #[track_caller] pub fn cargo_cfg_ub_checks() -> bool { - ProcessEnv.is_present("CARGO_CFG_UB_CHECKS") + ENV.is_present("CARGO_CFG_UB_CHECKS") } /// Set on [unix-like platforms](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#unix-and-windows). #[track_caller] pub fn cargo_cfg_unix() -> bool { - ProcessEnv.is_present("CARGO_CFG_UNIX") + ENV.is_present("CARGO_CFG_UNIX") } /// Set on [windows-like platforms](https://doc.rust-lang.org/stable/reference/conditional-compilation.html#unix-and-windows). #[track_caller] pub fn cargo_cfg_windows() -> bool { - ProcessEnv.is_present("CARGO_CFG_WINDOWS") + ENV.is_present("CARGO_CFG_WINDOWS") } } @@ -461,7 +484,7 @@ pub fn dep_metadata(name: &str, key: &str) -> Option { let name = name.to_uppercase().replace('-', "_"); let key = key.to_uppercase().replace('-', "_"); let key = format!("DEP_{name}_{key}"); - ProcessEnv.get(&key).map(to_string) + ENV.get(&key).map(to_string) } /// The compiler that Cargo has resolved to use. @@ -481,7 +504,7 @@ pub fn rustdoc() -> PathBuf { /// [`build.rustc-wrapper`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustc-wrapper #[track_caller] pub fn rustc_wrapper() -> Option { - ProcessEnv.get("RUSTC_WRAPPER").map(to_path) + ENV.get("RUSTC_WRAPPER").map(to_path) } /// The rustc wrapper, if any, that Cargo is using for workspace members. See @@ -490,7 +513,7 @@ pub fn rustc_wrapper() -> Option { /// [`build.rustc-workspace-wrapper`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#buildrustc-workspace-wrapper #[track_caller] pub fn rustc_workspace_wrapper() -> Option { - ProcessEnv.get("RUSTC_WORKSPACE_WRAPPER").map(to_path) + ENV.get("RUSTC_WORKSPACE_WRAPPER").map(to_path) } /// The linker that Cargo has resolved to use for the current target, if specified. @@ -498,7 +521,7 @@ pub fn rustc_workspace_wrapper() -> Option { /// [`target.*.linker`]: https://doc.rust-lang.org/stable/cargo/reference/config.html#targettriplelinker #[track_caller] pub fn rustc_linker() -> Option { - ProcessEnv.get("RUSTC_LINKER").map(to_path) + ENV.get("RUSTC_LINKER").map(to_path) } /// Extra flags that Cargo invokes rustc with. See [`build.rustflags`]. @@ -596,8 +619,7 @@ pub fn cargo_pkg_readme() -> Option { #[track_caller] fn var_or_panic(key: &str) -> std::ffi::OsString { - ProcessEnv - .get(key) + ENV.get(key) .unwrap_or_else(|| panic!("cargo environment variable `{key}` is missing")) } From 674e609a0ec2dc431575c48989a7bd1953ff2ab0 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Wed, 19 Jun 2024 20:07:37 -0400 Subject: [PATCH 191/525] refactor: make room for adapting rustc-stable-hasher --- src/cargo/core/compiler/build_runner/compilation_files.rs | 4 ++-- src/cargo/core/compiler/compile_kind.rs | 2 +- src/cargo/core/compiler/fingerprint/mod.rs | 2 +- src/cargo/ops/cargo_compile/mod.rs | 2 +- src/cargo/util/hex.rs | 4 ++-- src/cargo/util/rustc.rs | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cargo/core/compiler/build_runner/compilation_files.rs b/src/cargo/core/compiler/build_runner/compilation_files.rs index 80100f322ed..d9b6676d10a 100644 --- a/src/cargo/core/compiler/build_runner/compilation_files.rs +++ b/src/cargo/core/compiler/build_runner/compilation_files.rs @@ -718,8 +718,8 @@ fn compute_metadata( } } - let c_metadata = UnitHash(c_metadata_hasher.finish()); - let c_extra_filename = UnitHash(c_extra_filename_hasher.finish()); + let c_metadata = UnitHash(Hasher::finish(&c_metadata_hasher)); + let c_extra_filename = UnitHash(Hasher::finish(&c_extra_filename_hasher)); let unit_id = c_extra_filename; let c_extra_filename = use_extra_filename.then_some(c_extra_filename); diff --git a/src/cargo/core/compiler/compile_kind.rs b/src/cargo/core/compiler/compile_kind.rs index 222732ddebc..deb518afb48 100644 --- a/src/cargo/core/compiler/compile_kind.rs +++ b/src/cargo/core/compiler/compile_kind.rs @@ -195,6 +195,6 @@ impl CompileTarget { self.name.hash(&mut hasher); } } - hasher.finish() + Hasher::finish(&hasher) } } diff --git a/src/cargo/core/compiler/fingerprint/mod.rs b/src/cargo/core/compiler/fingerprint/mod.rs index 67e4aaab438..ac29b0d15d9 100644 --- a/src/cargo/core/compiler/fingerprint/mod.rs +++ b/src/cargo/core/compiler/fingerprint/mod.rs @@ -1561,7 +1561,7 @@ fn calculate_normal( local: Mutex::new(local), memoized_hash: Mutex::new(None), metadata, - config: config.finish(), + config: Hasher::finish(&config), compile_kind, rustflags: extra_flags, fs_status: FsStatus::Stale, diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index 4e011e7ec5a..c89c064c780 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -657,7 +657,7 @@ fn traverse_and_share( .collect(); // Here, we have recursively traversed this unit's dependencies, and hashed them: we can // finalize the dep hash. - let new_dep_hash = dep_hash.finish(); + let new_dep_hash = Hasher::finish(&dep_hash); // This is the key part of the sharing process: if the unit is a runtime dependency, whose // target is the same as the host, we canonicalize the compile kind to `CompileKind::Host`. diff --git a/src/cargo/util/hex.rs b/src/cargo/util/hex.rs index 2d06d9b5939..c0583e4a3af 100644 --- a/src/cargo/util/hex.rs +++ b/src/cargo/util/hex.rs @@ -10,7 +10,7 @@ pub fn to_hex(num: u64) -> String { pub fn hash_u64(hashable: H) -> u64 { let mut hasher = StableHasher::new(); hashable.hash(&mut hasher); - hasher.finish() + Hasher::finish(&hasher) } pub fn hash_u64_file(mut file: &File) -> std::io::Result { @@ -23,7 +23,7 @@ pub fn hash_u64_file(mut file: &File) -> std::io::Result { } hasher.write(&buf[..n]); } - Ok(hasher.finish()) + Ok(Hasher::finish(&hasher)) } pub fn short_hash(hashable: &H) -> String { diff --git a/src/cargo/util/rustc.rs b/src/cargo/util/rustc.rs index 19aa447edd9..66218dc8541 100644 --- a/src/cargo/util/rustc.rs +++ b/src/cargo/util/rustc.rs @@ -381,7 +381,7 @@ fn rustc_fingerprint( _ => (), } - Ok(hasher.finish()) + Ok(Hasher::finish(&hasher)) } fn process_fingerprint(cmd: &ProcessBuilder, extra_fingerprint: u64) -> u64 { @@ -391,5 +391,5 @@ fn process_fingerprint(cmd: &ProcessBuilder, extra_fingerprint: u64) -> u64 { let mut env = cmd.get_envs().iter().collect::>(); env.sort_unstable(); env.hash(&mut hasher); - hasher.finish() + Hasher::finish(&hasher) } From 2525532ff047ab7e1e64546a3c05358252cdd229 Mon Sep 17 00:00:00 2001 From: Jacob Finkelman Date: Tue, 10 Dec 2024 19:35:04 +0000 Subject: [PATCH 192/525] move ActivationsKey and SemverCompatibility --- src/cargo/core/resolver/context.rs | 35 ++--------------------------- src/cargo/core/resolver/types.rs | 36 +++++++++++++++++++++++++++++- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/cargo/core/resolver/context.rs b/src/cargo/core/resolver/context.rs index 08c289d9f8b..720bf0ce1ca 100644 --- a/src/cargo/core/resolver/context.rs +++ b/src/cargo/core/resolver/context.rs @@ -1,13 +1,12 @@ use super::dep_cache::RegistryQueryer; use super::errors::ActivateResult; -use super::types::{ConflictMap, ConflictReason, FeaturesSet, ResolveOpts}; +use super::types::{ActivationsKey, ConflictMap, ConflictReason, FeaturesSet, ResolveOpts}; use super::RequestedFeatures; -use crate::core::{Dependency, PackageId, SourceId, Summary}; +use crate::core::{Dependency, PackageId, Summary}; use crate::util::interning::InternedString; use crate::util::Graph; use anyhow::format_err; use std::collections::HashMap; -use std::num::NonZeroU64; use tracing::debug; // A `Context` is basically a bunch of local resolution information which is @@ -39,39 +38,9 @@ pub type ContextAge = usize; /// By storing this in a hash map we ensure that there is only one /// semver compatible version of each crate. /// This all so stores the `ContextAge`. -pub type ActivationsKey = (InternedString, SourceId, SemverCompatibility); - pub type Activations = im_rc::HashMap; -/// A type that represents when cargo treats two Versions as compatible. -/// Versions `a` and `b` are compatible if their left-most nonzero digit is the -/// same. -#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)] -pub enum SemverCompatibility { - Major(NonZeroU64), - Minor(NonZeroU64), - Patch(u64), -} - -impl From<&semver::Version> for SemverCompatibility { - fn from(ver: &semver::Version) -> Self { - if let Some(m) = NonZeroU64::new(ver.major) { - return SemverCompatibility::Major(m); - } - if let Some(m) = NonZeroU64::new(ver.minor) { - return SemverCompatibility::Minor(m); - } - SemverCompatibility::Patch(ver.patch) - } -} - -impl PackageId { - pub fn as_activations_key(self) -> ActivationsKey { - (self.name(), self.source_id(), self.version().into()) - } -} - impl ResolverContext { pub fn new() -> ResolverContext { ResolverContext { diff --git a/src/cargo/core/resolver/types.rs b/src/cargo/core/resolver/types.rs index 2dc5d44ca33..86a1e132c31 100644 --- a/src/cargo/core/resolver/types.rs +++ b/src/cargo/core/resolver/types.rs @@ -1,10 +1,11 @@ use super::features::{CliFeatures, RequestedFeatures}; -use crate::core::{Dependency, PackageId, Summary}; +use crate::core::{Dependency, PackageId, SourceId, Summary}; use crate::util::errors::CargoResult; use crate::util::interning::InternedString; use crate::util::GlobalContext; use std::cmp::Ordering; use std::collections::{BTreeMap, BTreeSet}; +use std::num::NonZeroU64; use std::rc::Rc; use std::time::{Duration, Instant}; @@ -163,6 +164,39 @@ impl ResolveOpts { } } +/// A key that when stord in a hash map ensures that there is only one +/// semver compatible version of each crate. +/// Find the activated version of a crate based on the name, source, and semver compatibility. +pub type ActivationsKey = (InternedString, SourceId, SemverCompatibility); + +/// A type that represents when cargo treats two Versions as compatible. +/// Versions `a` and `b` are compatible if their left-most nonzero digit is the +/// same. +#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)] +pub enum SemverCompatibility { + Major(NonZeroU64), + Minor(NonZeroU64), + Patch(u64), +} + +impl From<&semver::Version> for SemverCompatibility { + fn from(ver: &semver::Version) -> Self { + if let Some(m) = NonZeroU64::new(ver.major) { + return SemverCompatibility::Major(m); + } + if let Some(m) = NonZeroU64::new(ver.minor) { + return SemverCompatibility::Minor(m); + } + SemverCompatibility::Patch(ver.patch) + } +} + +impl PackageId { + pub fn as_activations_key(self) -> ActivationsKey { + (self.name(), self.source_id(), self.version().into()) + } +} + #[derive(Clone)] pub struct DepsFrame { pub parent: Summary, From a6992390a94166eeb5865f63866b026c68fff37f Mon Sep 17 00:00:00 2001 From: Jacob Finkelman Date: Tue, 10 Dec 2024 19:38:35 +0000 Subject: [PATCH 193/525] chagne ActivationsKey into a struct --- src/cargo/core/resolver/context.rs | 3 ++- src/cargo/core/resolver/types.rs | 11 +++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/cargo/core/resolver/context.rs b/src/cargo/core/resolver/context.rs index 720bf0ce1ca..88fb4d341ea 100644 --- a/src/cargo/core/resolver/context.rs +++ b/src/cargo/core/resolver/context.rs @@ -106,7 +106,8 @@ impl ResolverContext { // versions came from a `[patch]` source. if let Some((_, dep)) = parent { if dep.source_id() != id.source_id() { - let key = (id.name(), dep.source_id(), id.version().into()); + let key = + ActivationsKey::new(id.name(), dep.source_id(), id.version().into()); let prev = self.activations.insert(key, (summary.clone(), age)); if let Some((previous_summary, _)) = prev { return Err( diff --git a/src/cargo/core/resolver/types.rs b/src/cargo/core/resolver/types.rs index 86a1e132c31..c4597fb4e5a 100644 --- a/src/cargo/core/resolver/types.rs +++ b/src/cargo/core/resolver/types.rs @@ -167,7 +167,14 @@ impl ResolveOpts { /// A key that when stord in a hash map ensures that there is only one /// semver compatible version of each crate. /// Find the activated version of a crate based on the name, source, and semver compatibility. -pub type ActivationsKey = (InternedString, SourceId, SemverCompatibility); +#[derive(Clone, PartialEq, Eq, Debug, Ord, PartialOrd, Hash)] +pub struct ActivationsKey(InternedString, SourceId, SemverCompatibility); + +impl ActivationsKey { + pub fn new(name: InternedString, source_id: SourceId, ver: SemverCompatibility) -> ActivationsKey { + ActivationsKey(name, source_id, ver) + } +} /// A type that represents when cargo treats two Versions as compatible. /// Versions `a` and `b` are compatible if their left-most nonzero digit is the @@ -193,7 +200,7 @@ impl From<&semver::Version> for SemverCompatibility { impl PackageId { pub fn as_activations_key(self) -> ActivationsKey { - (self.name(), self.source_id(), self.version().into()) + ActivationsKey(self.name(), self.source_id(), self.version().into()) } } From f2b499847b3cbb702628bf74f48deac7d5c8186c Mon Sep 17 00:00:00 2001 From: Jacob Finkelman Date: Tue, 10 Dec 2024 19:44:00 +0000 Subject: [PATCH 194/525] reorder ActivationKey so that the source is last The source is the field the most expensive to do Eq on and the one least likely to be different. By moving it to the end calles to comparing keys that are different returns faster. --- src/cargo/core/resolver/context.rs | 2 +- src/cargo/core/resolver/types.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cargo/core/resolver/context.rs b/src/cargo/core/resolver/context.rs index 88fb4d341ea..d1bae02f8fa 100644 --- a/src/cargo/core/resolver/context.rs +++ b/src/cargo/core/resolver/context.rs @@ -107,7 +107,7 @@ impl ResolverContext { if let Some((_, dep)) = parent { if dep.source_id() != id.source_id() { let key = - ActivationsKey::new(id.name(), dep.source_id(), id.version().into()); + ActivationsKey::new(id.name(), id.version().into(), dep.source_id()); let prev = self.activations.insert(key, (summary.clone(), age)); if let Some((previous_summary, _)) = prev { return Err( diff --git a/src/cargo/core/resolver/types.rs b/src/cargo/core/resolver/types.rs index c4597fb4e5a..1758b81045f 100644 --- a/src/cargo/core/resolver/types.rs +++ b/src/cargo/core/resolver/types.rs @@ -168,11 +168,11 @@ impl ResolveOpts { /// semver compatible version of each crate. /// Find the activated version of a crate based on the name, source, and semver compatibility. #[derive(Clone, PartialEq, Eq, Debug, Ord, PartialOrd, Hash)] -pub struct ActivationsKey(InternedString, SourceId, SemverCompatibility); +pub struct ActivationsKey(InternedString, SemverCompatibility, SourceId); impl ActivationsKey { - pub fn new(name: InternedString, source_id: SourceId, ver: SemverCompatibility) -> ActivationsKey { - ActivationsKey(name, source_id, ver) + pub fn new(name: InternedString, ver: SemverCompatibility, source_id: SourceId) -> ActivationsKey { + ActivationsKey(name, ver, source_id) } } @@ -200,7 +200,7 @@ impl From<&semver::Version> for SemverCompatibility { impl PackageId { pub fn as_activations_key(self) -> ActivationsKey { - ActivationsKey(self.name(), self.source_id(), self.version().into()) + ActivationsKey(self.name(), self.version().into(), self.source_id()) } } From 91e6bf8b434c4ad5d7588d75d6a36fef8bae0c94 Mon Sep 17 00:00:00 2001 From: Jacob Finkelman Date: Tue, 10 Dec 2024 19:45:02 +0000 Subject: [PATCH 195/525] a faster hash for ActivationsKey --- src/cargo/core/resolver/types.rs | 10 +++++++++- src/cargo/util/interning.rs | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/cargo/core/resolver/types.rs b/src/cargo/core/resolver/types.rs index 1758b81045f..4cce53f3280 100644 --- a/src/cargo/core/resolver/types.rs +++ b/src/cargo/core/resolver/types.rs @@ -167,7 +167,7 @@ impl ResolveOpts { /// A key that when stord in a hash map ensures that there is only one /// semver compatible version of each crate. /// Find the activated version of a crate based on the name, source, and semver compatibility. -#[derive(Clone, PartialEq, Eq, Debug, Ord, PartialOrd, Hash)] +#[derive(Clone, PartialEq, Eq, Debug, Ord, PartialOrd)] pub struct ActivationsKey(InternedString, SemverCompatibility, SourceId); impl ActivationsKey { @@ -176,6 +176,14 @@ impl ActivationsKey { } } +impl std::hash::Hash for ActivationsKey { + fn hash(&self, state: &mut H) { + self.0.fast_hash(state); + self.1.hash(state); + // self.2.hash(state); // Packages that only differ by SourceId are rare enough to not be worth hashing + } +} + /// A type that represents when cargo treats two Versions as compatible. /// Versions `a` and `b` are compatible if their left-most nonzero digit is the /// same. diff --git a/src/cargo/util/interning.rs b/src/cargo/util/interning.rs index 595968eb654..185324d6b9a 100644 --- a/src/cargo/util/interning.rs +++ b/src/cargo/util/interning.rs @@ -90,6 +90,12 @@ impl InternedString { pub fn as_str(&self) -> &'static str { self.inner } + + /// A faster implementation of hash that is completely compatible with HashMap, + /// but does not have a stable value between runs of the program. + pub fn fast_hash(&self, state: &mut H) { + std::ptr::NonNull::from(self.inner).hash(state); + } } impl Deref for InternedString { From 7a67e88baf0f73196973cadf887bf9a31c13e95f Mon Sep 17 00:00:00 2001 From: Jacob Finkelman Date: Tue, 10 Dec 2024 19:54:25 +0000 Subject: [PATCH 196/525] cargo fmt --- src/cargo/core/resolver/types.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cargo/core/resolver/types.rs b/src/cargo/core/resolver/types.rs index 4cce53f3280..8dafd5d94fc 100644 --- a/src/cargo/core/resolver/types.rs +++ b/src/cargo/core/resolver/types.rs @@ -171,7 +171,11 @@ impl ResolveOpts { pub struct ActivationsKey(InternedString, SemverCompatibility, SourceId); impl ActivationsKey { - pub fn new(name: InternedString, ver: SemverCompatibility, source_id: SourceId) -> ActivationsKey { + pub fn new( + name: InternedString, + ver: SemverCompatibility, + source_id: SourceId, + ) -> ActivationsKey { ActivationsKey(name, ver, source_id) } } From 2cafa2876cb8f197ffb29571f34fd5e02ef3fb27 Mon Sep 17 00:00:00 2001 From: Jacob Finkelman Date: Tue, 10 Dec 2024 21:18:04 +0000 Subject: [PATCH 197/525] oddly this is fasster --- src/cargo/core/resolver/types.rs | 2 +- src/cargo/util/interning.rs | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/cargo/core/resolver/types.rs b/src/cargo/core/resolver/types.rs index 8dafd5d94fc..2248d79bbf3 100644 --- a/src/cargo/core/resolver/types.rs +++ b/src/cargo/core/resolver/types.rs @@ -182,7 +182,7 @@ impl ActivationsKey { impl std::hash::Hash for ActivationsKey { fn hash(&self, state: &mut H) { - self.0.fast_hash(state); + self.0.hash(state); self.1.hash(state); // self.2.hash(state); // Packages that only differ by SourceId are rare enough to not be worth hashing } diff --git a/src/cargo/util/interning.rs b/src/cargo/util/interning.rs index 185324d6b9a..595968eb654 100644 --- a/src/cargo/util/interning.rs +++ b/src/cargo/util/interning.rs @@ -90,12 +90,6 @@ impl InternedString { pub fn as_str(&self) -> &'static str { self.inner } - - /// A faster implementation of hash that is completely compatible with HashMap, - /// but does not have a stable value between runs of the program. - pub fn fast_hash(&self, state: &mut H) { - std::ptr::NonNull::from(self.inner).hash(state); - } } impl Deref for InternedString { From 5eb7480565adfaf79bfdf90de6a7ff5ea5dcd0eb Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Mon, 18 Nov 2024 23:29:01 -0500 Subject: [PATCH 198/525] feat(SourceId): use stable hash from rustc-stable-hash This helps `-Ztrim-paths` build a stable cross-platform path for the registry and git sources. Sources files then can be found from the same path when debugging. It also helps cache registry index all at once for all platforms, for example the use case in rust-lang/cargo#14795 (despite they should use `cargo vendor` instead IMO). Some caveats: * Newer cargo will need to re-download files for global caches (index files, git/registry sources). The old cache is still kept and used when running with older cargoes. * Windows is not really covered by the "cross-platform" hash, because path prefix components like `C:` are always there. That means hashes of some sources kind, like local registry and local path, are not going to be real cross-platform stable. There might be hash collisions if you have two registries under the same domain. This won't happen to crates.io, as the infra would have to intentionally put another registry on index.crates.io to collide. We don't consider this is an actual threat model, so we are not going to use any cryptographically secure hash algorithm like BLAKE3. See also --- Cargo.lock | 7 ++ Cargo.toml | 2 + src/cargo/core/source_id.rs | 91 +++++++++++++++---------- src/cargo/util/hasher.rs | 23 +------ tests/testsuite/global_cache_tracker.rs | 16 ++++- 5 files changed, 80 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bdba69d5f51..5518d5fcb96 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -357,6 +357,7 @@ dependencies = [ "regex", "rusqlite", "rustc-hash 2.0.0", + "rustc-stable-hash", "rustfix", "same-file", "semver", @@ -3069,6 +3070,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" +[[package]] +name = "rustc-stable-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2febf9acc5ee5e99d1ad0afcdbccc02d87aa3f857a1f01f825b80eacf8edfcd1" + [[package]] name = "rustfix" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 8e676d2984d..bafde065c09 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,7 @@ rand = "0.8.5" regex = "1.10.5" rusqlite = { version = "0.32.0", features = ["bundled"] } rustc-hash = "2.0.0" +rustc-stable-hash = "0.1.1" rustfix = { version = "0.9.0", path = "crates/rustfix" } same-file = "1.0.6" schemars = "0.8.21" @@ -194,6 +195,7 @@ rand.workspace = true regex.workspace = true rusqlite.workspace = true rustc-hash.workspace = true +rustc-stable-hash.workspace = true rustfix.workspace = true same-file.workspace = true semver.workspace = true diff --git a/src/cargo/core/source_id.rs b/src/cargo/core/source_id.rs index 663f69b5751..501c88cba72 100644 --- a/src/cargo/core/source_id.rs +++ b/src/cargo/core/source_id.rs @@ -782,70 +782,89 @@ mod tests { // Otherwise please just leave a comment in your PR as to why the hash value is // changing and why the old value can't be easily preserved. // - // The hash value depends on endianness and bit-width, so we only run this test on - // little-endian 64-bit CPUs (such as x86-64 and ARM64) where it matches the - // well-known value. + // The hash value should be stable across platforms, and doesn't depend on + // endianness and bit-width. One caveat is that absolute paths on Windows + // are inherently different than on Unix-like platforms. Unless we omit or + // strip the prefix components (e.g. `C:`), there is not way to have a true + // cross-platform stable hash for absolute paths. #[test] - #[cfg(all(target_endian = "little", target_pointer_width = "64"))] - fn test_cratesio_hash() { - let gctx = GlobalContext::default().unwrap(); - let crates_io = SourceId::crates_io(&gctx).unwrap(); - assert_eq!(crate::util::hex::short_hash(&crates_io), "1ecc6299db9ec823"); - } - - // See the comment in `test_cratesio_hash`. - // - // Only test on non-Windows as paths on Windows will get different hashes. - #[test] - #[cfg(all(target_endian = "little", target_pointer_width = "64", not(windows)))] fn test_stable_hash() { use std::hash::Hasher; use std::path::Path; + use crate::util::StableHasher; + + #[cfg(not(windows))] + let ws_root = Path::new("/tmp/ws"); + #[cfg(windows)] + let ws_root = Path::new(r"C:\\tmp\ws"); + let gen_hash = |source_id: SourceId| { - let mut hasher = std::collections::hash_map::DefaultHasher::new(); - source_id.stable_hash(Path::new("/tmp/ws"), &mut hasher); - hasher.finish() + let mut hasher = StableHasher::new(); + source_id.stable_hash(ws_root, &mut hasher); + Hasher::finish(&hasher) }; + let source_id = SourceId::crates_io(&GlobalContext::default().unwrap()).unwrap(); + assert_eq!(gen_hash(source_id), 7062945687441624357); + assert_eq!(crate::util::hex::short_hash(&source_id), "25cdd57fae9f0462"); + let url = "https://my-crates.io".into_url().unwrap(); let source_id = SourceId::for_registry(&url).unwrap(); - assert_eq!(gen_hash(source_id), 18108075011063494626); - assert_eq!(crate::util::hex::short_hash(&source_id), "fb60813d6cb8df79"); + assert_eq!(gen_hash(source_id), 8310250053664888498); + assert_eq!(crate::util::hex::short_hash(&source_id), "b2d65deb64f05373"); let url = "https://your-crates.io".into_url().unwrap(); let source_id = SourceId::for_alt_registry(&url, "alt").unwrap(); - assert_eq!(gen_hash(source_id), 12862859764592646184); - assert_eq!(crate::util::hex::short_hash(&source_id), "09c10fd0cbd74bce"); + assert_eq!(gen_hash(source_id), 14149534903000258933); + assert_eq!(crate::util::hex::short_hash(&source_id), "755952de063f5dc4"); let url = "sparse+https://my-crates.io".into_url().unwrap(); let source_id = SourceId::for_registry(&url).unwrap(); - assert_eq!(gen_hash(source_id), 8763561830438022424); - assert_eq!(crate::util::hex::short_hash(&source_id), "d1ea0d96f6f759b5"); + assert_eq!(gen_hash(source_id), 16249512552851930162); + assert_eq!(crate::util::hex::short_hash(&source_id), "327cfdbd92dd81e1"); let url = "sparse+https://your-crates.io".into_url().unwrap(); let source_id = SourceId::for_alt_registry(&url, "alt").unwrap(); - assert_eq!(gen_hash(source_id), 5159702466575482972); - assert_eq!(crate::util::hex::short_hash(&source_id), "135d23074253cb78"); + assert_eq!(gen_hash(source_id), 6156697384053352292); + assert_eq!(crate::util::hex::short_hash(&source_id), "64a713b6a6fb7055"); let url = "file:///tmp/ws/crate".into_url().unwrap(); let source_id = SourceId::for_git(&url, GitReference::DefaultBranch).unwrap(); - assert_eq!(gen_hash(source_id), 15332537265078583985); - assert_eq!(crate::util::hex::short_hash(&source_id), "73a808694abda756"); - - let path = Path::new("/tmp/ws/crate"); + assert_eq!(gen_hash(source_id), 473480029881867801); + assert_eq!(crate::util::hex::short_hash(&source_id), "199e591d94239206"); + let path = &ws_root.join("crate"); let source_id = SourceId::for_local_registry(path).unwrap(); - assert_eq!(gen_hash(source_id), 18446533307730842837); - assert_eq!(crate::util::hex::short_hash(&source_id), "52a84cc73f6fd48b"); + #[cfg(not(windows))] + { + assert_eq!(gen_hash(source_id), 11515846423845066584); + assert_eq!(crate::util::hex::short_hash(&source_id), "58d73c154f81d09f"); + } + #[cfg(windows)] + { + assert_eq!(gen_hash(source_id), 6146331155906064276); + assert_eq!(crate::util::hex::short_hash(&source_id), "946fb2239f274c55"); + } let source_id = SourceId::for_path(path).unwrap(); - assert_eq!(gen_hash(source_id), 8764714075439899829); - assert_eq!(crate::util::hex::short_hash(&source_id), "e1ddd48578620fc1"); + assert_eq!(gen_hash(source_id), 215644081443634269); + #[cfg(not(windows))] + assert_eq!(crate::util::hex::short_hash(&source_id), "64bace89c92b101f"); + #[cfg(windows)] + assert_eq!(crate::util::hex::short_hash(&source_id), "01e1e6c391813fb6"); let source_id = SourceId::for_directory(path).unwrap(); - assert_eq!(gen_hash(source_id), 17459999773908528552); - assert_eq!(crate::util::hex::short_hash(&source_id), "6568fe2c2fab5bfe"); + #[cfg(not(windows))] + { + assert_eq!(gen_hash(source_id), 6127590343904940368); + assert_eq!(crate::util::hex::short_hash(&source_id), "505191d1f3920955"); + } + #[cfg(windows)] + { + assert_eq!(gen_hash(source_id), 10423446877655960172); + assert_eq!(crate::util::hex::short_hash(&source_id), "6c8ad69db585a790"); + } } #[test] diff --git a/src/cargo/util/hasher.rs b/src/cargo/util/hasher.rs index 87586c0900f..93c88bb4bf9 100644 --- a/src/cargo/util/hasher.rs +++ b/src/cargo/util/hasher.rs @@ -1,25 +1,6 @@ -//! Implementation of a hasher that produces the same values across releases. +//! A hasher that produces the same values across releases and platforms. //! //! The hasher should be fast and have a low chance of collisions (but is not //! sufficient for cryptographic purposes). -#![allow(deprecated)] -use std::hash::{Hasher, SipHasher}; - -#[derive(Clone)] -pub struct StableHasher(SipHasher); - -impl StableHasher { - pub fn new() -> StableHasher { - StableHasher(SipHasher::new()) - } -} - -impl Hasher for StableHasher { - fn finish(&self) -> u64 { - self.0.finish() - } - fn write(&mut self, bytes: &[u8]) { - self.0.write(bytes) - } -} +pub use rustc_stable_hash::StableSipHasher128 as StableHasher; diff --git a/tests/testsuite/global_cache_tracker.rs b/tests/testsuite/global_cache_tracker.rs index dfd698f5f8b..9a20ef50c18 100644 --- a/tests/testsuite/global_cache_tracker.rs +++ b/tests/testsuite/global_cache_tracker.rs @@ -2004,7 +2004,16 @@ fn compatible_with_older_cargo() { assert_eq!(get_registry_names("src"), ["middle-1.0.0", "new-1.0.0"]); assert_eq!( get_registry_names("cache"), - ["middle-1.0.0.crate", "new-1.0.0.crate", "old-1.0.0.crate"] + // Duplicate crates from two different cache location + // because we're changing how SourceId is hashed. + // This change should be reverted once rust-lang/cargo#14917 lands. + [ + "middle-1.0.0.crate", + "middle-1.0.0.crate", + "new-1.0.0.crate", + "new-1.0.0.crate", + "old-1.0.0.crate" + ] ); // T-0 months: Current version, make sure it can read data from stable, @@ -2027,7 +2036,10 @@ fn compatible_with_older_cargo() { assert_eq!(get_registry_names("src"), ["new-1.0.0"]); assert_eq!( get_registry_names("cache"), - ["middle-1.0.0.crate", "new-1.0.0.crate"] + // Duplicate crates from two different cache location + // because we're changing how SourceId is hashed. + // This change should be reverted once rust-lang/cargo#14917 lands. + ["middle-1.0.0.crate", "new-1.0.0.crate", "new-1.0.0.crate"] ); } From b45ca32bff294922bfe3df99deae508b70be9b13 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 10:48:14 -0600 Subject: [PATCH 199/525] refactor(source): Rename AlternativeVersions to RejectedVersions Really, when we switch the req to a wildcard, it is an alternative version, so for yanked and unsupported we need a different name. --- crates/resolver-tests/src/lib.rs | 2 +- src/cargo/core/resolver/errors.rs | 2 +- src/cargo/sources/directory.rs | 2 +- src/cargo/sources/path.rs | 4 ++-- src/cargo/sources/registry/mod.rs | 4 ++-- src/cargo/sources/source.rs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/crates/resolver-tests/src/lib.rs b/crates/resolver-tests/src/lib.rs index f34dd737650..359090540bd 100644 --- a/crates/resolver-tests/src/lib.rs +++ b/crates/resolver-tests/src/lib.rs @@ -143,7 +143,7 @@ pub fn resolve_with_global_context_raw( for summary in self.list.iter() { let matched = match kind { QueryKind::Exact => dep.matches(summary), - QueryKind::AlternativeVersions => dep.matches(summary), + QueryKind::RejectedVersions => dep.matches(summary), QueryKind::AlternativeNames => true, QueryKind::Normalized => true, }; diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 74eb871c8d4..45dea512b8f 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -305,7 +305,7 @@ pub(super) fn activation_error( } else { // Maybe something is wrong with the available versions let mut version_candidates = loop { - match registry.query_vec(&new_dep, QueryKind::AlternativeVersions) { + match registry.query_vec(&new_dep, QueryKind::RejectedVersions) { Poll::Ready(Ok(candidates)) => break candidates, Poll::Ready(Err(e)) => return to_resolve_err(e), Poll::Pending => match registry.block_until_ready() { diff --git a/src/cargo/sources/directory.rs b/src/cargo/sources/directory.rs index b9c34690c52..6f676a79850 100644 --- a/src/cargo/sources/directory.rs +++ b/src/cargo/sources/directory.rs @@ -108,7 +108,7 @@ impl<'gctx> Source for DirectorySource<'gctx> { } let packages = self.packages.values().map(|p| &p.0); let matches = packages.filter(|pkg| match kind { - QueryKind::Exact | QueryKind::AlternativeVersions => dep.matches(pkg.summary()), + QueryKind::Exact | QueryKind::RejectedVersions => dep.matches(pkg.summary()), QueryKind::AlternativeNames => true, QueryKind::Normalized => dep.matches(pkg.summary()), }); diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index f47225af63c..1d4a533a3c4 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -145,7 +145,7 @@ impl<'gctx> Source for PathSource<'gctx> { self.load()?; if let Some(s) = self.package.as_ref().map(|p| p.summary()) { let matched = match kind { - QueryKind::Exact | QueryKind::AlternativeVersions => dep.matches(s), + QueryKind::Exact | QueryKind::RejectedVersions => dep.matches(s), QueryKind::AlternativeNames => true, QueryKind::Normalized => dep.matches(s), }; @@ -332,7 +332,7 @@ impl<'gctx> Source for RecursivePathSource<'gctx> { .map(|p| p.summary()) { let matched = match kind { - QueryKind::Exact | QueryKind::AlternativeVersions => dep.matches(s), + QueryKind::Exact | QueryKind::RejectedVersions => dep.matches(s), QueryKind::AlternativeNames => true, QueryKind::Normalized => dep.matches(s), }; diff --git a/src/cargo/sources/registry/mod.rs b/src/cargo/sources/registry/mod.rs index 6a1451feb0c..3ff8fad344b 100644 --- a/src/cargo/sources/registry/mod.rs +++ b/src/cargo/sources/registry/mod.rs @@ -799,7 +799,7 @@ impl<'gctx> Source for RegistrySource<'gctx> { .index .query_inner(dep.package_name(), &req, &mut *self.ops, &mut |s| { let matched = match kind { - QueryKind::Exact | QueryKind::AlternativeVersions => { + QueryKind::Exact | QueryKind::RejectedVersions => { if req.is_precise() && self.gctx.cli_unstable().unstable_options { dep.matches_prerelease(s.as_summary()) } else { @@ -816,7 +816,7 @@ impl<'gctx> Source for RegistrySource<'gctx> { // leak through if they're in a whitelist (aka if they were // previously in `Cargo.lock` match s { - s @ _ if kind == QueryKind::AlternativeVersions => callback(s), + s @ _ if kind == QueryKind::RejectedVersions => callback(s), s @ IndexSummary::Candidate(_) => callback(s), s @ IndexSummary::Yanked(_) => { if self.yanked_whitelist.contains(&s.package_id()) { diff --git a/src/cargo/sources/source.rs b/src/cargo/sources/source.rs index f4a29016bb9..045684008ab 100644 --- a/src/cargo/sources/source.rs +++ b/src/cargo/sources/source.rs @@ -184,7 +184,7 @@ pub enum QueryKind { /// /// Path/Git sources may return all dependencies that are at that URI, /// whereas an `Registry` source may return dependencies that are yanked or invalid. - AlternativeVersions, + RejectedVersions, /// A query for packages close to the given dependency requirement. /// /// Each source gets to define what `close` means for it. From 8ec9b1ef883ef3a7a53dbeb0e33fc390cf6b09c6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 11:41:30 -0600 Subject: [PATCH 200/525] refactor(resolver): Clarify which dep spec is used for alternatives --- src/cargo/core/resolver/errors.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 45dea512b8f..c29a4c6a267 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -222,11 +222,11 @@ pub(super) fn activation_error( // Maybe the user mistyped the ver_req? Like `dep="2"` when `dep="0.2"` // was meant. So we re-query the registry with `dep="*"` so we can // list a few versions that were actually found. - let mut new_dep = dep.clone(); - new_dep.set_version_req(OptVersionReq::Any); + let mut wild_dep = dep.clone(); + wild_dep.set_version_req(OptVersionReq::Any); let candidates = loop { - match registry.query_vec(&new_dep, QueryKind::Exact) { + match registry.query_vec(&wild_dep, QueryKind::Exact) { Poll::Ready(Ok(candidates)) => break candidates, Poll::Ready(Err(e)) => return to_resolve_err(e), Poll::Pending => match registry.block_until_ready() { @@ -305,7 +305,7 @@ pub(super) fn activation_error( } else { // Maybe something is wrong with the available versions let mut version_candidates = loop { - match registry.query_vec(&new_dep, QueryKind::RejectedVersions) { + match registry.query_vec(&wild_dep, QueryKind::RejectedVersions) { Poll::Ready(Ok(candidates)) => break candidates, Poll::Ready(Err(e)) => return to_resolve_err(e), Poll::Pending => match registry.block_until_ready() { @@ -319,7 +319,7 @@ pub(super) fn activation_error( // Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing` // was meant. So we try asking the registry for a `fuzzy` search for suggestions. let name_candidates = loop { - match registry.query_vec(&new_dep, QueryKind::AlternativeNames) { + match registry.query_vec(&wild_dep, QueryKind::AlternativeNames) { Poll::Ready(Ok(candidates)) => break candidates, Poll::Ready(Err(e)) => return to_resolve_err(e), Poll::Pending => match registry.block_until_ready() { @@ -336,7 +336,7 @@ pub(super) fn activation_error( name_candidates.dedup_by(|a, b| a.name() == b.name()); let mut name_candidates: Vec<_> = name_candidates .iter() - .filter_map(|n| Some((edit_distance(&*new_dep.package_name(), &*n.name(), 3)?, n))) + .filter_map(|n| Some((edit_distance(&*wild_dep.package_name(), &*n.name(), 3)?, n))) .collect(); name_candidates.sort_by_key(|o| o.0); From 3aab14575f2740719ae6328c447ff4ce986b4823 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 11:39:47 -0600 Subject: [PATCH 201/525] fix(resolver): Don't report all versions as rejected When I copy/pasted the alternative-names code, I didn't notice that the wildcard dependency was used. This does not have a test yet because overly-constrictive dep specs have higher precedence atm (which I plan to also fix). --- src/cargo/core/resolver/errors.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index c29a4c6a267..a44485adaa9 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -305,7 +305,7 @@ pub(super) fn activation_error( } else { // Maybe something is wrong with the available versions let mut version_candidates = loop { - match registry.query_vec(&wild_dep, QueryKind::RejectedVersions) { + match registry.query_vec(&dep, QueryKind::RejectedVersions) { Poll::Ready(Ok(candidates)) => break candidates, Poll::Ready(Err(e)) => return to_resolve_err(e), Poll::Pending => match registry.block_until_ready() { From d7cd78f0c562016950473927caf17564d63038af Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 11:59:08 -0600 Subject: [PATCH 202/525] refactor(resolver): Pull out alternative look up logic --- src/cargo/core/resolver/errors.rs | 168 +++++++++++++++++++----------- 1 file changed, 106 insertions(+), 62 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index a44485adaa9..df1979821f2 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -6,6 +6,7 @@ use crate::core::{Dependency, PackageId, Registry, Summary}; use crate::sources::source::QueryKind; use crate::sources::IndexSummary; use crate::util::edit_distance::edit_distance; +use crate::util::errors::CargoResult; use crate::util::{GlobalContext, OptVersionReq, VersionExt}; use anyhow::Error; @@ -218,29 +219,11 @@ pub(super) fn activation_error( // We didn't actually find any candidates, so we need to // give an error message that nothing was found. - // - // Maybe the user mistyped the ver_req? Like `dep="2"` when `dep="0.2"` - // was meant. So we re-query the registry with `dep="*"` so we can - // list a few versions that were actually found. - let mut wild_dep = dep.clone(); - wild_dep.set_version_req(OptVersionReq::Any); - - let candidates = loop { - match registry.query_vec(&wild_dep, QueryKind::Exact) { - Poll::Ready(Ok(candidates)) => break candidates, - Poll::Ready(Err(e)) => return to_resolve_err(e), - Poll::Pending => match registry.block_until_ready() { - Ok(()) => continue, - Err(e) => return to_resolve_err(e), - }, - } - }; - - let mut candidates: Vec<_> = candidates.into_iter().map(|s| s.into_summary()).collect(); - - candidates.sort_unstable_by(|a, b| b.version().cmp(a.version())); - - let mut msg = if !candidates.is_empty() { + let mut msg = if let Some(candidates) = alt_versions(registry, dep) { + let candidates = match candidates { + Ok(c) => c, + Err(e) => return to_resolve_err(e), + }; let versions = { let mut versions = candidates .iter() @@ -303,45 +286,12 @@ pub(super) fn activation_error( msg } else { - // Maybe something is wrong with the available versions - let mut version_candidates = loop { - match registry.query_vec(&dep, QueryKind::RejectedVersions) { - Poll::Ready(Ok(candidates)) => break candidates, - Poll::Ready(Err(e)) => return to_resolve_err(e), - Poll::Pending => match registry.block_until_ready() { - Ok(()) => continue, - Err(e) => return to_resolve_err(e), - }, - } - }; - version_candidates.sort_unstable_by_key(|a| a.as_summary().version().clone()); - - // Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing` - // was meant. So we try asking the registry for a `fuzzy` search for suggestions. - let name_candidates = loop { - match registry.query_vec(&wild_dep, QueryKind::AlternativeNames) { - Poll::Ready(Ok(candidates)) => break candidates, - Poll::Ready(Err(e)) => return to_resolve_err(e), - Poll::Pending => match registry.block_until_ready() { - Ok(()) => continue, - Err(e) => return to_resolve_err(e), - }, - } - }; - let mut name_candidates: Vec<_> = name_candidates - .into_iter() - .map(|s| s.into_summary()) - .collect(); - name_candidates.sort_unstable_by_key(|a| a.name()); - name_candidates.dedup_by(|a, b| a.name() == b.name()); - let mut name_candidates: Vec<_> = name_candidates - .iter() - .filter_map(|n| Some((edit_distance(&*wild_dep.package_name(), &*n.name(), 3)?, n))) - .collect(); - name_candidates.sort_by_key(|o| o.0); - let mut msg = String::new(); - if !version_candidates.is_empty() { + if let Some(version_candidates) = rejected_versions(registry, dep) { + let version_candidates = match version_candidates { + Ok(c) => c, + Err(e) => return to_resolve_err(e), + }; let _ = writeln!( &mut msg, "no matching versions for `{}` found", @@ -382,7 +332,11 @@ pub(super) fn activation_error( } } } - } else if !name_candidates.is_empty() { + } else if let Some(name_candidates) = alt_names(registry, dep) { + let name_candidates = match name_candidates { + Ok(c) => c, + Err(e) => return to_resolve_err(e), + }; let _ = writeln!(&mut msg, "no matching package found",); let _ = writeln!(&mut msg, "searched package name: `{}`", dep.package_name()); let mut names = name_candidates @@ -442,6 +396,96 @@ pub(super) fn activation_error( to_resolve_err(anyhow::format_err!("{}", msg)) } +// Maybe the user mistyped the ver_req? Like `dep="2"` when `dep="0.2"` +// was meant. So we re-query the registry with `dep="*"` so we can +// list a few versions that were actually found. +fn alt_versions( + registry: &mut dyn Registry, + dep: &Dependency, +) -> Option>> { + let mut wild_dep = dep.clone(); + wild_dep.set_version_req(OptVersionReq::Any); + + let candidates = loop { + match registry.query_vec(&wild_dep, QueryKind::Exact) { + Poll::Ready(Ok(candidates)) => break candidates, + Poll::Ready(Err(e)) => return Some(Err(e)), + Poll::Pending => match registry.block_until_ready() { + Ok(()) => continue, + Err(e) => return Some(Err(e)), + }, + } + }; + let mut candidates: Vec<_> = candidates.into_iter().map(|s| s.into_summary()).collect(); + candidates.sort_unstable_by(|a, b| b.version().cmp(a.version())); + if candidates.is_empty() { + None + } else { + Some(Ok(candidates)) + } +} + +/// Maybe something is wrong with the available versions +fn rejected_versions( + registry: &mut dyn Registry, + dep: &Dependency, +) -> Option>> { + let mut version_candidates = loop { + match registry.query_vec(&dep, QueryKind::RejectedVersions) { + Poll::Ready(Ok(candidates)) => break candidates, + Poll::Ready(Err(e)) => return Some(Err(e)), + Poll::Pending => match registry.block_until_ready() { + Ok(()) => continue, + Err(e) => return Some(Err(e)), + }, + } + }; + version_candidates.sort_unstable_by_key(|a| a.as_summary().version().clone()); + if version_candidates.is_empty() { + None + } else { + Some(Ok(version_candidates)) + } +} + +/// Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing` +/// was meant. So we try asking the registry for a `fuzzy` search for suggestions. +fn alt_names( + registry: &mut dyn Registry, + dep: &Dependency, +) -> Option>> { + let mut wild_dep = dep.clone(); + wild_dep.set_version_req(OptVersionReq::Any); + + let name_candidates = loop { + match registry.query_vec(&wild_dep, QueryKind::AlternativeNames) { + Poll::Ready(Ok(candidates)) => break candidates, + Poll::Ready(Err(e)) => return Some(Err(e)), + Poll::Pending => match registry.block_until_ready() { + Ok(()) => continue, + Err(e) => return Some(Err(e)), + }, + } + }; + let mut name_candidates: Vec<_> = name_candidates + .into_iter() + .map(|s| s.into_summary()) + .collect(); + name_candidates.sort_unstable_by_key(|a| a.name()); + name_candidates.dedup_by(|a, b| a.name() == b.name()); + let mut name_candidates: Vec<_> = name_candidates + .into_iter() + .filter_map(|n| Some((edit_distance(&*wild_dep.package_name(), &*n.name(), 3)?, n))) + .collect(); + name_candidates.sort_by_key(|o| o.0); + + if name_candidates.is_empty() { + None + } else { + Some(Ok(name_candidates)) + } +} + /// Returns String representation of dependency chain for a particular `pkgid` /// within given context. pub(super) fn describe_path_in_context(cx: &ResolverContext, id: &PackageId) -> String { From ba4f982b03796c9605987aae5c261602227b50da Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 12:34:10 -0600 Subject: [PATCH 203/525] refactor(resolver): Share the buffer for errors --- src/cargo/core/resolver/errors.rs | 56 ++++++++++++++++++------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index df1979821f2..c55ede94b98 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -219,7 +219,8 @@ pub(super) fn activation_error( // We didn't actually find any candidates, so we need to // give an error message that nothing was found. - let mut msg = if let Some(candidates) = alt_versions(registry, dep) { + let mut msg = String::new(); + if let Some(candidates) = alt_versions(registry, dep) { let candidates = match candidates { Ok(c) => c, Err(e) => return to_resolve_err(e), @@ -244,49 +245,57 @@ pub(super) fn activation_error( .map(|v| format!(" (locked to {})", v)) .unwrap_or_default(); - let mut msg = format!( - "failed to select a version for the requirement `{} = \"{}\"`{}\n\ - candidate versions found which didn't match: {}\n\ - location searched: {}\n", + let _ = writeln!( + &mut msg, + "failed to select a version for the requirement `{} = \"{}\"`{}", dep.package_name(), dep.version_req(), locked_version, - versions, - registry.describe_source(dep.source_id()), ); - msg.push_str("required by "); - msg.push_str(&describe_path_in_context( - resolver_ctx, - &parent.package_id(), - )); + let _ = writeln!( + &mut msg, + "candidate versions found which didn't match: {versions}", + ); + let _ = writeln!( + &mut msg, + "location searched: {}", + registry.describe_source(dep.source_id()) + ); + let _ = write!( + &mut msg, + "required by {}", + describe_path_in_context(resolver_ctx, &parent.package_id()), + ); // If we have a pre-release candidate, then that may be what our user is looking for if let Some(pre) = candidates.iter().find(|c| c.version().is_prerelease()) { - msg.push_str("\nif you are looking for the prerelease package it needs to be specified explicitly"); - msg.push_str(&format!( + let _ = write!(&mut msg, "\nif you are looking for the prerelease package it needs to be specified explicitly"); + let _ = write!( + &mut msg, "\n {} = {{ version = \"{}\" }}", pre.name(), pre.version() - )); + ); } // If we have a path dependency with a locked version, then this may // indicate that we updated a sub-package and forgot to run `cargo // update`. In this case try to print a helpful error! if dep.source_id().is_path() && dep.version_req().is_locked() { - msg.push_str( + let _ = write!( + &mut msg, "\nconsider running `cargo update` to update \ a path dependency's locked version", ); } if registry.is_replaced(dep.source_id()) { - msg.push_str("\nperhaps a crate was updated and forgotten to be re-vendored?"); + let _ = write!( + &mut msg, + "\nperhaps a crate was updated and forgotten to be re-vendored?" + ); } - - msg } else { - let mut msg = String::new(); if let Some(version_candidates) = rejected_versions(registry, dep) { let version_candidates = match version_candidates { Ok(c) => c, @@ -378,13 +387,12 @@ pub(super) fn activation_error( "required by {}", describe_path_in_context(resolver_ctx, &parent.package_id()), ); - - msg - }; + } if let Some(gctx) = gctx { if gctx.offline() { - msg.push_str( + let _ = write!( + &mut msg, "\nAs a reminder, you're using offline mode (--offline) \ which can sometimes cause surprising resolution failures, \ if this error is too confusing you may wish to retry \ From 41d5e465bf93e7863d573787cb9b76875bcf11e4 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 12:38:33 -0600 Subject: [PATCH 204/525] refactor(resolver): Track error message and hint separately --- src/cargo/core/resolver/errors.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index c55ede94b98..bbaae66cef4 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -220,6 +220,7 @@ pub(super) fn activation_error( // We didn't actually find any candidates, so we need to // give an error message that nothing was found. let mut msg = String::new(); + let mut hints = String::new(); if let Some(candidates) = alt_versions(registry, dep) { let candidates = match candidates { Ok(c) => c, @@ -269,9 +270,9 @@ pub(super) fn activation_error( // If we have a pre-release candidate, then that may be what our user is looking for if let Some(pre) = candidates.iter().find(|c| c.version().is_prerelease()) { - let _ = write!(&mut msg, "\nif you are looking for the prerelease package it needs to be specified explicitly"); + let _ = write!(&mut hints, "\nif you are looking for the prerelease package it needs to be specified explicitly"); let _ = write!( - &mut msg, + &mut hints, "\n {} = {{ version = \"{}\" }}", pre.name(), pre.version() @@ -283,7 +284,7 @@ pub(super) fn activation_error( // update`. In this case try to print a helpful error! if dep.source_id().is_path() && dep.version_req().is_locked() { let _ = write!( - &mut msg, + &mut hints, "\nconsider running `cargo update` to update \ a path dependency's locked version", ); @@ -291,7 +292,7 @@ pub(super) fn activation_error( if registry.is_replaced(dep.source_id()) { let _ = write!( - &mut msg, + &mut hints, "\nperhaps a crate was updated and forgotten to be re-vendored?" ); } @@ -392,7 +393,7 @@ pub(super) fn activation_error( if let Some(gctx) = gctx { if gctx.offline() { let _ = write!( - &mut msg, + &mut hints, "\nAs a reminder, you're using offline mode (--offline) \ which can sometimes cause surprising resolution failures, \ if this error is too confusing you may wish to retry \ @@ -401,7 +402,7 @@ pub(super) fn activation_error( } } - to_resolve_err(anyhow::format_err!("{}", msg)) + to_resolve_err(anyhow::format_err!("{msg}{hints}")) } // Maybe the user mistyped the ver_req? Like `dep="2"` when `dep="0.2"` From aecffa819ab3067b8ca45526ac5532e092b9cc11 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 12:42:31 -0600 Subject: [PATCH 205/525] fix(resolver): Avoid an empty location in errors --- src/cargo/core/resolver/errors.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index bbaae66cef4..eaf672c2a56 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -257,11 +257,12 @@ pub(super) fn activation_error( &mut msg, "candidate versions found which didn't match: {versions}", ); - let _ = writeln!( - &mut msg, - "location searched: {}", - registry.describe_source(dep.source_id()) - ); + let mut location_searched_msg = registry.describe_source(dep.source_id()); + if location_searched_msg.is_empty() { + location_searched_msg = format!("{}", dep.source_id()); + } + + let _ = writeln!(&mut msg, "location searched: {}", location_searched_msg); let _ = write!( &mut msg, "required by {}", From f93ebac243d2c31f99a1d43d4b47774b9a925dbb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 12:45:30 -0600 Subject: [PATCH 206/525] refactor(resolver): Share context across messages --- src/cargo/core/resolver/errors.rs | 32 ++++++++++--------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index eaf672c2a56..357dbfca8fa 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -257,17 +257,6 @@ pub(super) fn activation_error( &mut msg, "candidate versions found which didn't match: {versions}", ); - let mut location_searched_msg = registry.describe_source(dep.source_id()); - if location_searched_msg.is_empty() { - location_searched_msg = format!("{}", dep.source_id()); - } - - let _ = writeln!(&mut msg, "location searched: {}", location_searched_msg); - let _ = write!( - &mut msg, - "required by {}", - describe_path_in_context(resolver_ctx, &parent.package_id()), - ); // If we have a pre-release candidate, then that may be what our user is looking for if let Some(pre) = candidates.iter().find(|c| c.version().is_prerelease()) { @@ -377,19 +366,18 @@ pub(super) fn activation_error( dep.package_name() ); } + } - let mut location_searched_msg = registry.describe_source(dep.source_id()); - if location_searched_msg.is_empty() { - location_searched_msg = format!("{}", dep.source_id()); - } - - let _ = writeln!(&mut msg, "location searched: {}", location_searched_msg); - let _ = write!( - &mut msg, - "required by {}", - describe_path_in_context(resolver_ctx, &parent.package_id()), - ); + let mut location_searched_msg = registry.describe_source(dep.source_id()); + if location_searched_msg.is_empty() { + location_searched_msg = format!("{}", dep.source_id()); } + let _ = writeln!(&mut msg, "location searched: {}", location_searched_msg); + let _ = write!( + &mut msg, + "required by {}", + describe_path_in_context(resolver_ctx, &parent.package_id()), + ); if let Some(gctx) = gctx { if gctx.offline() { From 784dfbe130316df99635ae48128749d088d30dbb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 13:01:41 -0600 Subject: [PATCH 207/525] refactor(resolver): Flatten the error cases --- src/cargo/core/resolver/errors.rs | 128 +++++++++++++++--------------- 1 file changed, 63 insertions(+), 65 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 357dbfca8fa..1b770b05256 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -286,71 +286,70 @@ pub(super) fn activation_error( "\nperhaps a crate was updated and forgotten to be re-vendored?" ); } - } else { - if let Some(version_candidates) = rejected_versions(registry, dep) { - let version_candidates = match version_candidates { - Ok(c) => c, - Err(e) => return to_resolve_err(e), - }; - let _ = writeln!( - &mut msg, - "no matching versions for `{}` found", - dep.package_name() - ); - for candidate in version_candidates { - match candidate { - IndexSummary::Candidate(summary) => { - // HACK: If this was a real candidate, we wouldn't hit this case. - // so it must be a patch which get normalized to being a candidate - let _ = - writeln!(&mut msg, " version {} is unavailable", summary.version()); - } - IndexSummary::Yanked(summary) => { - let _ = writeln!(&mut msg, " version {} is yanked", summary.version()); - } - IndexSummary::Offline(summary) => { - let _ = writeln!(&mut msg, " version {} is not cached", summary.version()); - } - IndexSummary::Unsupported(summary, schema_version) => { - if let Some(rust_version) = summary.rust_version() { - // HACK: technically its unsupported and we shouldn't make assumptions - // about the entry but this is limited and for diagnostics purposes - let _ = writeln!( - &mut msg, - " version {} requires cargo {}", - summary.version(), - rust_version - ); - } else { - let _ = writeln!( + } else if let Some(version_candidates) = rejected_versions(registry, dep) { + let version_candidates = match version_candidates { + Ok(c) => c, + Err(e) => return to_resolve_err(e), + }; + let _ = writeln!( + &mut msg, + "no matching versions for `{}` found", + dep.package_name() + ); + for candidate in version_candidates { + match candidate { + IndexSummary::Candidate(summary) => { + // HACK: If this was a real candidate, we wouldn't hit this case. + // so it must be a patch which get normalized to being a candidate + let _ = writeln!(&mut msg, " version {} is unavailable", summary.version()); + } + IndexSummary::Yanked(summary) => { + let _ = writeln!(&mut msg, " version {} is yanked", summary.version()); + } + IndexSummary::Offline(summary) => { + let _ = writeln!(&mut msg, " version {} is not cached", summary.version()); + } + IndexSummary::Unsupported(summary, schema_version) => { + if let Some(rust_version) = summary.rust_version() { + // HACK: technically its unsupported and we shouldn't make assumptions + // about the entry but this is limited and for diagnostics purposes + let _ = writeln!( + &mut msg, + " version {} requires cargo {}", + summary.version(), + rust_version + ); + } else { + let _ = writeln!( &mut msg, " version {} requires a Cargo version that supports index version {}", summary.version(), schema_version ); - } } } } - } else if let Some(name_candidates) = alt_names(registry, dep) { - let name_candidates = match name_candidates { - Ok(c) => c, - Err(e) => return to_resolve_err(e), - }; - let _ = writeln!(&mut msg, "no matching package found",); - let _ = writeln!(&mut msg, "searched package name: `{}`", dep.package_name()); - let mut names = name_candidates - .iter() - .take(3) - .map(|c| c.1.name().as_str()) - .collect::>(); - - if name_candidates.len() > 3 { - names.push("..."); - } - // Vertically align first suggestion with missing crate name - // so a typo jumps out at you. - let suggestions = names + } + } else if let Some(name_candidates) = alt_names(registry, dep) { + let name_candidates = match name_candidates { + Ok(c) => c, + Err(e) => return to_resolve_err(e), + }; + let _ = writeln!(&mut msg, "no matching package found",); + let _ = writeln!(&mut msg, "searched package name: `{}`", dep.package_name()); + let mut names = name_candidates + .iter() + .take(3) + .map(|c| c.1.name().as_str()) + .collect::>(); + + if name_candidates.len() > 3 { + names.push("..."); + } + // Vertically align first suggestion with missing crate name + // so a typo jumps out at you. + let suggestions = + names .iter() .enumerate() .fold(String::default(), |acc, (i, el)| match i { @@ -358,14 +357,13 @@ pub(super) fn activation_error( i if names.len() - 1 == i && name_candidates.len() <= 3 => acc + " or " + el, _ => acc + ", " + el, }); - let _ = writeln!(&mut msg, "perhaps you meant: {suggestions}"); - } else { - let _ = writeln!( - &mut msg, - "no matching package named `{}` found", - dep.package_name() - ); - } + let _ = writeln!(&mut msg, "perhaps you meant: {suggestions}"); + } else { + let _ = writeln!( + &mut msg, + "no matching package named `{}` found", + dep.package_name() + ); } let mut location_searched_msg = registry.describe_source(dep.source_id()); From 28b5cb15ee279c68141f8fe44e703ef2503c132f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 13:08:42 -0600 Subject: [PATCH 208/525] fix(resolver): In errors, show rejected versions over alt versions The user likely intended what they said and we should tell them why it didn't work. Rejected versions also imply alt versions below them. Fixes #4260 --- src/cargo/core/resolver/errors.rs | 90 +++++++++++++++---------------- tests/testsuite/artifact_dep.rs | 3 +- tests/testsuite/registry.rs | 10 ++-- 3 files changed, 50 insertions(+), 53 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 1b770b05256..8c6fc97a2bd 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -221,7 +221,51 @@ pub(super) fn activation_error( // give an error message that nothing was found. let mut msg = String::new(); let mut hints = String::new(); - if let Some(candidates) = alt_versions(registry, dep) { + if let Some(version_candidates) = rejected_versions(registry, dep) { + let version_candidates = match version_candidates { + Ok(c) => c, + Err(e) => return to_resolve_err(e), + }; + let _ = writeln!( + &mut msg, + "no matching versions for `{}` found", + dep.package_name() + ); + for candidate in version_candidates { + match candidate { + IndexSummary::Candidate(summary) => { + // HACK: If this was a real candidate, we wouldn't hit this case. + // so it must be a patch which get normalized to being a candidate + let _ = writeln!(&mut msg, " version {} is unavailable", summary.version()); + } + IndexSummary::Yanked(summary) => { + let _ = writeln!(&mut msg, " version {} is yanked", summary.version()); + } + IndexSummary::Offline(summary) => { + let _ = writeln!(&mut msg, " version {} is not cached", summary.version()); + } + IndexSummary::Unsupported(summary, schema_version) => { + if let Some(rust_version) = summary.rust_version() { + // HACK: technically its unsupported and we shouldn't make assumptions + // about the entry but this is limited and for diagnostics purposes + let _ = writeln!( + &mut msg, + " version {} requires cargo {}", + summary.version(), + rust_version + ); + } else { + let _ = writeln!( + &mut msg, + " version {} requires a Cargo version that supports index version {}", + summary.version(), + schema_version + ); + } + } + } + } + } else if let Some(candidates) = alt_versions(registry, dep) { let candidates = match candidates { Ok(c) => c, Err(e) => return to_resolve_err(e), @@ -286,50 +330,6 @@ pub(super) fn activation_error( "\nperhaps a crate was updated and forgotten to be re-vendored?" ); } - } else if let Some(version_candidates) = rejected_versions(registry, dep) { - let version_candidates = match version_candidates { - Ok(c) => c, - Err(e) => return to_resolve_err(e), - }; - let _ = writeln!( - &mut msg, - "no matching versions for `{}` found", - dep.package_name() - ); - for candidate in version_candidates { - match candidate { - IndexSummary::Candidate(summary) => { - // HACK: If this was a real candidate, we wouldn't hit this case. - // so it must be a patch which get normalized to being a candidate - let _ = writeln!(&mut msg, " version {} is unavailable", summary.version()); - } - IndexSummary::Yanked(summary) => { - let _ = writeln!(&mut msg, " version {} is yanked", summary.version()); - } - IndexSummary::Offline(summary) => { - let _ = writeln!(&mut msg, " version {} is not cached", summary.version()); - } - IndexSummary::Unsupported(summary, schema_version) => { - if let Some(rust_version) = summary.rust_version() { - // HACK: technically its unsupported and we shouldn't make assumptions - // about the entry but this is limited and for diagnostics purposes - let _ = writeln!( - &mut msg, - " version {} requires cargo {}", - summary.version(), - rust_version - ); - } else { - let _ = writeln!( - &mut msg, - " version {} requires a Cargo version that supports index version {}", - summary.version(), - schema_version - ); - } - } - } - } } else if let Some(name_candidates) = alt_names(registry, dep) { let name_candidates = match name_candidates { Ok(c) => c, diff --git a/tests/testsuite/artifact_dep.rs b/tests/testsuite/artifact_dep.rs index 28536e4cd41..1912d61e859 100644 --- a/tests/testsuite/artifact_dep.rs +++ b/tests/testsuite/artifact_dep.rs @@ -1752,10 +1752,9 @@ foo v0.1.0 ([ROOT]/foo) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `bar = "^1.0"` (locked to 1.0.1) -candidate versions found which didn't match: 1.0.0 + version 1.0.1 requires a Cargo version that supports index version 3 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` -perhaps a crate was updated and forgotten to be re-vendored? "#]]) .run(); diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 2c3da8261a2..03d02e6b2d6 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -862,12 +862,11 @@ fn relying_on_a_yank_is_bad_http() { let _server = setup_http(); relying_on_a_yank_is_bad(str![[r#" [UPDATING] `dummy-registry` index -[ERROR] failed to select a version for the requirement `baz = "=0.0.2"` -candidate versions found which didn't match: 0.0.1 +[ERROR] no matching versions for `baz` found + version 0.0.2 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `bar v0.0.1` ... which satisfies dependency `bar = "*"` of package `foo v0.0.1 ([ROOT]/foo)` -perhaps a crate was updated and forgotten to be re-vendored? "#]]); } @@ -876,12 +875,11 @@ perhaps a crate was updated and forgotten to be re-vendored? fn relying_on_a_yank_is_bad_git() { relying_on_a_yank_is_bad(str![[r#" [UPDATING] `dummy-registry` index -[ERROR] failed to select a version for the requirement `baz = "=0.0.2"` -candidate versions found which didn't match: 0.0.1 +[ERROR] no matching versions for `baz` found + version 0.0.2 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `bar v0.0.1` ... which satisfies dependency `bar = "*"` of package `foo v0.0.1 ([ROOT]/foo)` -perhaps a crate was updated and forgotten to be re-vendored? "#]]); } From 9d2115362963fa3a3a7b23d9c5e6f4c740885fd0 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 13:11:08 -0600 Subject: [PATCH 209/525] test(resolver): Verify wildcard dep isn't used to find rejected versions --- tests/testsuite/registry.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 03d02e6b2d6..4744cf90f98 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -3202,6 +3202,7 @@ fn ignores_unknown_index_version(expected: impl IntoData) { #[cargo_test] fn unknown_index_version_error() { + Package::new("bar", "0.0.1").publish(); // If the version field is not understood, it is ignored. Package::new("bar", "1.0.1") .schema_version(u32::MAX) @@ -3238,6 +3239,7 @@ required by package `foo v0.1.0 ([ROOT]/foo)` #[cargo_test] fn unknown_index_version_with_msrv_error() { + Package::new("bar", "0.0.1").publish(); // If the version field is not understood, it is ignored. Package::new("bar", "1.0.1") .schema_version(u32::MAX) From ccc00ced4d170ad7942cacd1571cae86db178e70 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 13:18:42 -0600 Subject: [PATCH 210/525] fix(resolver): Include dep spec in rejected versions error --- src/cargo/core/resolver/errors.rs | 12 ++++++++++-- tests/testsuite/registry.rs | 16 ++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 8c6fc97a2bd..592f3ae7371 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -226,10 +226,18 @@ pub(super) fn activation_error( Ok(c) => c, Err(e) => return to_resolve_err(e), }; + + let locked_version = dep + .version_req() + .locked_version() + .map(|v| format!(" (locked to {})", v)) + .unwrap_or_default(); let _ = writeln!( &mut msg, - "no matching versions for `{}` found", - dep.package_name() + "failed to select a version for the requirement `{} = \"{}\"`{}", + dep.package_name(), + dep.version_req(), + locked_version ); for candidate in version_candidates { match candidate { diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 4744cf90f98..69f41e643a2 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -862,7 +862,7 @@ fn relying_on_a_yank_is_bad_http() { let _server = setup_http(); relying_on_a_yank_is_bad(str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching versions for `baz` found +[ERROR] failed to select a version for the requirement `baz = "=0.0.2"` version 0.0.2 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `bar v0.0.1` @@ -875,7 +875,7 @@ required by package `bar v0.0.1` fn relying_on_a_yank_is_bad_git() { relying_on_a_yank_is_bad(str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching versions for `baz` found +[ERROR] failed to select a version for the requirement `baz = "=0.0.2"` version 0.0.2 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `bar v0.0.1` @@ -922,7 +922,7 @@ fn yanks_in_lockfiles_are_ok_http() { "#]], str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching versions for `bar` found +[ERROR] failed to select a version for the requirement `bar = "*"` version 0.0.1 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` @@ -940,7 +940,7 @@ fn yanks_in_lockfiles_are_ok_git() { "#]], str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching versions for `bar` found +[ERROR] failed to select a version for the requirement `bar = "*"` version 0.0.1 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` @@ -993,7 +993,7 @@ fn yanks_in_lockfiles_are_ok_for_other_update_http() { "#]], str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching versions for `bar` found +[ERROR] failed to select a version for the requirement `bar = "*"` version 0.0.1 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` @@ -1017,7 +1017,7 @@ fn yanks_in_lockfiles_are_ok_for_other_update_git() { "#]], str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching versions for `bar` found +[ERROR] failed to select a version for the requirement `bar = "*"` version 0.0.1 is yanked location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.0.1 ([ROOT]/foo)` @@ -3228,7 +3228,7 @@ fn unknown_index_version_error() { .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching versions for `bar` found +[ERROR] failed to select a version for the requirement `bar = "^1.0"` version 1.0.1 requires a Cargo version that supports index version 4294967295 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` @@ -3266,7 +3266,7 @@ fn unknown_index_version_with_msrv_error() { .with_status(101) .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index -[ERROR] no matching versions for `bar` found +[ERROR] failed to select a version for the requirement `bar = "^1.0"` version 1.0.1 requires cargo 1.2345 location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` From ab7469e3b8fea6d7e7febe5d278d570254142e78 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 11 Dec 2024 16:53:01 -0600 Subject: [PATCH 211/525] fix(script): Don't override the release profile This has been around since #12224 and isn't in the RFC, so its being removed. This is part of #12207. --- src/cargo/util/toml/embedded.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/cargo/util/toml/embedded.rs b/src/cargo/util/toml/embedded.rs index 126188588b2..686fcb77a22 100644 --- a/src/cargo/util/toml/embedded.rs +++ b/src/cargo/util/toml/embedded.rs @@ -141,19 +141,6 @@ fn expand_manifest_( toml::Value::Array(vec![toml::Value::Table(bin)]), ); - let release = manifest - .entry("profile".to_owned()) - .or_insert_with(|| toml::Value::Table(Default::default())) - .as_table_mut() - .ok_or_else(|| anyhow::format_err!("`profile` must be a table"))? - .entry("release".to_owned()) - .or_insert_with(|| toml::Value::Table(Default::default())) - .as_table_mut() - .ok_or_else(|| anyhow::format_err!("`profile.release` must be a table"))?; - release - .entry("strip".to_owned()) - .or_insert_with(|| toml::Value::Boolean(true)); - Ok(manifest) } @@ -588,9 +575,6 @@ build = false edition = "2024" name = "test-" -[profile.release] -strip = true - [workspace] "#]] @@ -626,9 +610,6 @@ build = false edition = "2024" name = "test-" -[profile.release] -strip = true - [workspace] "#]] From 71f81734657aab2e1fcfda8232c4ab4ca22f7f5f Mon Sep 17 00:00:00 2001 From: Aatif Syed Date: Wed, 11 Dec 2024 23:12:35 +0000 Subject: [PATCH 212/525] fix: emit_serialized_unit_graph uses the configured shell --- src/cargo/core/compiler/unit_graph.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/cargo/core/compiler/unit_graph.rs b/src/cargo/core/compiler/unit_graph.rs index 14bab76d65d..553996bcdc0 100644 --- a/src/cargo/core/compiler/unit_graph.rs +++ b/src/cargo/core/compiler/unit_graph.rs @@ -10,7 +10,6 @@ use crate::util::interning::InternedString; use crate::util::CargoResult; use crate::GlobalContext; use std::collections::HashMap; -use std::io::Write; /// The dependency graph of Units. pub type UnitGraph = HashMap>; @@ -121,15 +120,10 @@ pub fn emit_serialized_unit_graph( } }) .collect(); - let s = SerializedUnitGraph { + + gctx.shell().print_json(&SerializedUnitGraph { version: VERSION, units: ser_units, roots, - }; - - let stdout = std::io::stdout(); - let mut lock = stdout.lock(); - serde_json::to_writer(&mut lock, &s)?; - drop(writeln!(lock)); - Ok(()) + }) } From 6c5d34cfd5e8182f2b9c3ca89ee131c4a05e1a2c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 12 Dec 2024 09:16:22 -0600 Subject: [PATCH 213/525] test(registry): Ensure cache doesn't prevent schema errors --- tests/testsuite/registry.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 69f41e643a2..386e6a77ebd 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -3233,6 +3233,17 @@ fn unknown_index_version_error() { location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `foo v0.1.0 ([ROOT]/foo)` +"#]]) + .run(); + p.cargo("generate-lockfile") + .with_status(101) + .with_stderr_data(str![[r#" +[UPDATING] `dummy-registry` index +[ERROR] failed to select a version for the requirement `bar = "^1.0"` + version 1.0.1 requires a Cargo version that supports index version 4294967295 +location searched: `dummy-registry` index (which is replacing registry `crates-io`) +required by package `foo v0.1.0 ([ROOT]/foo)` + "#]]) .run(); } From 9a8ca867e256ebd790824a96e41bebe03341738a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 12 Dec 2024 11:50:43 -0600 Subject: [PATCH 214/525] chore: Bump cargo-test-support's version --- Cargo.lock | 2 +- Cargo.toml | 2 +- crates/cargo-test-support/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5518d5fcb96..1004313ef91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -456,7 +456,7 @@ version = "0.4.0" [[package]] name = "cargo-test-support" -version = "0.6.1" +version = "0.7.0" dependencies = [ "anstream", "anstyle", diff --git a/Cargo.toml b/Cargo.toml index bafde065c09..00dbda420cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,7 +33,7 @@ cargo-credential-macos-keychain = { version = "0.4.7", path = "credential/cargo- cargo-credential-wincred = { version = "0.4.7", path = "credential/cargo-credential-wincred" } cargo-platform = { path = "crates/cargo-platform", version = "0.2.0" } cargo-test-macro = { version = "0.4.0", path = "crates/cargo-test-macro" } -cargo-test-support = { version = "0.6.0", path = "crates/cargo-test-support" } +cargo-test-support = { version = "0.7.0", path = "crates/cargo-test-support" } cargo-util = { version = "0.2.14", path = "crates/cargo-util" } cargo-util-schemas = { version = "0.7.0", path = "crates/cargo-util-schemas" } cargo_metadata = "0.19.0" diff --git a/crates/cargo-test-support/Cargo.toml b/crates/cargo-test-support/Cargo.toml index aeba691ba20..8c23a2180a1 100644 --- a/crates/cargo-test-support/Cargo.toml +++ b/crates/cargo-test-support/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-test-support" -version = "0.6.1" +version = "0.7.0" edition.workspace = true rust-version = "1.83" # MSRV:1 license.workspace = true From 4f68299e27abe618c18c724aaf55178250171129 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 12 Dec 2024 09:38:07 -0600 Subject: [PATCH 215/525] fix(test)!: Clarify purpose of Package::invalid_index_line --- crates/cargo-test-support/src/registry.rs | 10 +++++----- tests/testsuite/registry.rs | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/cargo-test-support/src/registry.rs b/crates/cargo-test-support/src/registry.rs index 207cf74a6ec..9ea69e473dc 100644 --- a/crates/cargo-test-support/src/registry.rs +++ b/crates/cargo-test-support/src/registry.rs @@ -570,7 +570,7 @@ pub struct Package { features: FeatureMap, local: bool, alternative: bool, - invalid_json: bool, + invalid_index_line: bool, edition: Option, resolver: Option, proc_macro: bool, @@ -1251,7 +1251,7 @@ impl Package { features: BTreeMap::new(), local: false, alternative: false, - invalid_json: false, + invalid_index_line: false, edition: None, resolver: None, proc_macro: false, @@ -1422,8 +1422,8 @@ impl Package { /// Causes the JSON line emitted in the index to be invalid, presumably /// causing Cargo to skip over this version. - pub fn invalid_json(&mut self, invalid: bool) -> &mut Package { - self.invalid_json = invalid; + pub fn invalid_index_line(&mut self, invalid: bool) -> &mut Package { + self.invalid_index_line = invalid; self } @@ -1496,7 +1496,7 @@ impl Package { let c = t!(fs::read(&self.archive_dst())); cksum(&c) }; - let name = if self.invalid_json { + let name = if self.invalid_index_line { serde_json::json!(1) } else { serde_json::json!(self.name) diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 386e6a77ebd..7a1e8f0bef0 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -2916,7 +2916,9 @@ fn ignore_invalid_json_lines_git() { fn ignore_invalid_json_lines() { Package::new("foo", "0.1.0").publish(); - Package::new("foo", "0.1.1").invalid_json(true).publish(); + Package::new("foo", "0.1.1") + .invalid_index_line(true) + .publish(); Package::new("foo", "0.2.0").publish(); let p = project() From 20870cea8174370383e1cc6dbf96f7c5c0de9e9e Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 12 Dec 2024 09:45:25 -0600 Subject: [PATCH 216/525] feat(test): Allow custom index lines --- crates/cargo-test-support/src/registry.rs | 42 +++++++++++++++-------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/crates/cargo-test-support/src/registry.rs b/crates/cargo-test-support/src/registry.rs index 9ea69e473dc..c547921ceb9 100644 --- a/crates/cargo-test-support/src/registry.rs +++ b/crates/cargo-test-support/src/registry.rs @@ -571,6 +571,7 @@ pub struct Package { local: bool, alternative: bool, invalid_index_line: bool, + index_line: Option, edition: Option, resolver: Option, proc_macro: bool, @@ -1252,6 +1253,7 @@ impl Package { local: false, alternative: false, invalid_index_line: false, + index_line: None, edition: None, resolver: None, proc_macro: false, @@ -1427,6 +1429,14 @@ impl Package { self } + /// Override the auto-generated index line + /// + /// This can give more control over error cases than [`Package::invalid_index_line`] + pub fn index_line(&mut self, line: &str) -> &mut Package { + self.index_line = Some(line.to_owned()); + self + } + pub fn links(&mut self, links: &str) -> &mut Package { self.links = Some(links.to_string()); self @@ -1496,22 +1506,26 @@ impl Package { let c = t!(fs::read(&self.archive_dst())); cksum(&c) }; - let name = if self.invalid_index_line { - serde_json::json!(1) + let line = if let Some(line) = self.index_line.clone() { + line } else { - serde_json::json!(self.name) + let name = if self.invalid_index_line { + serde_json::json!(1) + } else { + serde_json::json!(self.name) + }; + create_index_line( + name, + &self.vers, + deps, + &cksum, + self.features.clone(), + self.yanked, + self.links.clone(), + self.rust_version.as_deref(), + self.v, + ) }; - let line = create_index_line( - name, - &self.vers, - deps, - &cksum, - self.features.clone(), - self.yanked, - self.links.clone(), - self.rust_version.as_deref(), - self.v, - ); let registry_path = if self.alternative { alt_registry_path() From 60f757321e8d46187f56230fef2e9596b9aeebbe Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 12 Dec 2024 10:11:52 -0600 Subject: [PATCH 217/525] test(registry): Check error on bad index line --- tests/testsuite/registry.rs | 91 +++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 7a1e8f0bef0..15580035f5a 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -2942,6 +2942,97 @@ fn ignore_invalid_json_lines() { p.cargo("check").run(); } +#[cargo_test] +fn invalid_json_lines_error() { + Package::new("foo", "0.1.0") + .rust_version("1.0") + .schema_version(2) + .publish(); + Package::new("foo", "0.1.1") + // Bad name field, too corrupt to use + .invalid_index_line(true) + .publish(); + Package::new("foo", "0.1.2") + // Bad version field, too corrupt to use + .index_line( + r#"{"cksum":"7ca5fc2301ad96ade45356faf53225aea36437d99930bbfa951155c01faecf79","deps":[],"features":{},"links":null,"name":"foo","vers":"bad","yanked":false,"rust_version":"1.2345","v":1000000000}"#, + ) + .publish(); + Package::new("foo", "0.1.3") + // Bad field, report rust version + .index_line( + r#"{"cksum":"7ca5fc2301ad96ade45356faf53225aea36437d99930bbfa951155c01faecf79","deps":[],"features":"bad","links":null,"name":"foo","vers":"0.1.3","yanked":false,"rust_version":"1.2345","v":1000000000}"#, + ) + .publish(); + Package::new("foo", "0.1.4") + // Bad field, report schema + .index_line( + r#"{"cksum":"7ca5fc2301ad96ade45356faf53225aea36437d99930bbfa951155c01faecf79","deps":[],"features":"bad","links":null,"name":"foo","vers":"0.1.4","yanked":false,"v":1000000000}"#, + ) + .publish(); + Package::new("foo", "0.1.5") + // Bad field, report error + .index_line( + r#"{"cksum":"7ca5fc2301ad96ade45356faf53225aea36437d99930bbfa951155c01faecf79","deps":[],"features":"bad","links":null,"name":"foo","vers":"0.1.5","yanked":false}"#, + ) + .publish(); + Package::new("foo", "0.1.6") + // Bad field with bad rust version, report schema + .index_line( + r#"{"cksum":"7ca5fc2301ad96ade45356faf53225aea36437d99930bbfa951155c01faecf79","deps":[],"features":"bad","links":null,"name":"foo","vers":"0.1.6","yanked":false,"rust_version":"bad","v":1000000000}"#, + ) + .publish(); + Package::new("foo", "0.1.7") + // Bad field with bad rust version and schema, report error + .index_line( + r#"{"cksum":"7ca5fc2301ad96ade45356faf53225aea36437d99930bbfa951155c01faecf79","deps":[],"features":"bad","links":null,"name":"foo","vers":"0.1.7","yanked":false,"rust_version":"bad","v":"bad"}"#, + ) + .publish(); + Package::new("foo", "0.2.0").publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "a" + version = "0.5.0" + edition = "2015" + authors = [] + + [dependencies] + foo = "0.1.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("generate-lockfile") + .with_status(101) + .with_stderr_data(str![[r#" +[UPDATING] `dummy-registry` index +[ERROR] failed to select a version for the requirement `foo = "^0.1.1"` +candidate versions found which didn't match: 0.2.0, 0.1.0 +location searched: `dummy-registry` index (which is replacing registry `crates-io`) +required by package `a v0.5.0 ([ROOT]/foo)` +perhaps a crate was updated and forgotten to be re-vendored? + +"#]]) + .run(); + p.cargo("generate-lockfile") + .with_status(101) + .with_stderr_data(str![[r#" +[UPDATING] `dummy-registry` index +[ERROR] failed to select a version for the requirement `foo = "^0.1.1"` +candidate versions found which didn't match: 0.2.0, 0.1.0 +location searched: `dummy-registry` index (which is replacing registry `crates-io`) +required by package `a v0.5.0 ([ROOT]/foo)` +perhaps a crate was updated and forgotten to be re-vendored? + +"#]]) + .run(); +} + #[cargo_test] fn readonly_registry_still_works_http() { let _server = setup_http(); From ac9c4e3af176a55e0a28e9bafd302dbf30a0de79 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 12 Dec 2024 10:24:39 -0600 Subject: [PATCH 218/525] refactor(registry): Pull out index to Summary conversion --- src/cargo/sources/registry/index/mod.rs | 62 ++++++++++++++----------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/src/cargo/sources/registry/index/mod.rs b/src/cargo/sources/registry/index/mod.rs index b2a23139b41..0e7e42886f5 100644 --- a/src/cargo/sources/registry/index/mod.rs +++ b/src/cargo/sources/registry/index/mod.rs @@ -259,8 +259,36 @@ pub struct IndexPackage<'a> { pub v: Option, } +impl IndexPackage<'_> { + fn to_summary(&self, source_id: SourceId) -> CargoResult { + // ****CAUTION**** Please be extremely careful with returning errors, see + // `IndexSummary::parse` for details + let pkgid = PackageId::new(self.name.into(), self.vers.clone(), source_id); + let deps = self + .deps + .iter() + .map(|dep| dep.clone().into_dep(source_id)) + .collect::>>()?; + let mut features = self.features.clone(); + if let Some(features2) = &self.features2 { + for (name, values) in features2 { + features.entry(*name).or_default().extend(values); + } + } + let mut summary = Summary::new( + pkgid, + deps, + &features, + self.links, + self.rust_version.clone(), + )?; + summary.set_checksum(self.cksum.clone()); + Ok(summary) + } +} + /// A dependency as encoded in the [`IndexPackage`] index JSON. -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Clone)] pub struct RegistryDependency<'a> { /// Name of the dependency. If the dependency is renamed, the original /// would be stored in [`RegistryDependency::package`]. @@ -706,32 +734,10 @@ impl IndexSummary { // between different versions that understand the index differently. // Make sure to consider the INDEX_V_MAX and CURRENT_CACHE_VERSION // values carefully when making changes here. - let IndexPackage { - name, - vers, - cksum, - deps, - mut features, - features2, - yanked, - links, - rust_version, - v, - } = serde_json::from_slice(line)?; - let v = v.unwrap_or(1); - tracing::trace!("json parsed registry {}/{}", name, vers); - let pkgid = PackageId::new(name.into(), vers.clone(), source_id); - let deps = deps - .into_iter() - .map(|dep| dep.into_dep(source_id)) - .collect::>>()?; - if let Some(features2) = features2 { - for (name, values) in features2 { - features.entry(name).or_default().extend(values); - } - } - let mut summary = Summary::new(pkgid, deps, &features, links, rust_version)?; - summary.set_checksum(cksum); + let index: IndexPackage<'_> = serde_json::from_slice(line)?; + let v = index.v.unwrap_or(1); + tracing::trace!("json parsed registry {}/{}", index.name, index.vers); + let summary = index.to_summary(source_id)?; let v_max = if bindeps { INDEX_V_MAX + 1 @@ -741,7 +747,7 @@ impl IndexSummary { if v_max < v { Ok(IndexSummary::Unsupported(summary, v)) - } else if yanked.unwrap_or(false) { + } else if index.yanked.unwrap_or(false) { Ok(IndexSummary::Yanked(summary)) } else { Ok(IndexSummary::Candidate(summary)) From 933ba6e1cd4b835a91077f106c69ff2d396c3754 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 12 Dec 2024 10:48:24 -0600 Subject: [PATCH 219/525] refactor(registry): Reduce places we list all IndexSummary variants --- src/cargo/sources/registry/index/mod.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/cargo/sources/registry/index/mod.rs b/src/cargo/sources/registry/index/mod.rs index 0e7e42886f5..94515d22665 100644 --- a/src/cargo/sources/registry/index/mod.rs +++ b/src/cargo/sources/registry/index/mod.rs @@ -169,12 +169,7 @@ impl IndexSummary { /// Extract the package id from any variant pub fn package_id(&self) -> PackageId { - match self { - IndexSummary::Candidate(sum) - | IndexSummary::Yanked(sum) - | IndexSummary::Offline(sum) - | IndexSummary::Unsupported(sum, _) => sum.package_id(), - } + self.as_summary().package_id() } /// Returns `true` if the index summary is [`Yanked`]. From 1d54a2b4c123f08f42dfe307aa15ead890681fe0 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 12 Dec 2024 10:52:27 -0600 Subject: [PATCH 220/525] fix(resolver): Report invalid index entries While #14897 reported packages with an unsupported index schema version, that only worked if the changes in the schema version did not cause errors in deserializing `IndexPackage` or in generating a `Summary`. This extends that change by recoverying on error with a more lax, incomplete parse of `IndexPackage` which should always generate a valid `Summary`. To help with a buggy Index, we also will report as many as we can. This does not provide a way to report to users or log on cache reads if the index entry is not at least `{"name": "", "vers": ""}`. As a side effect, the index cache will include more "invalid" index entries. That should be ok as we will ignore the invalid entry in the cache when loading it. Ignoring of invalid entries dates back to #6880 when the index cache was introduced. Fixes #10623 Fixes #14894 --- src/cargo/core/resolver/errors.rs | 7 +++ src/cargo/sources/registry/index/mod.rs | 66 +++++++++++++++++++++++-- src/cargo/sources/registry/mod.rs | 3 ++ tests/testsuite/registry.rs | 14 ++++-- 4 files changed, 82 insertions(+), 8 deletions(-) diff --git a/src/cargo/core/resolver/errors.rs b/src/cargo/core/resolver/errors.rs index 592f3ae7371..8a46a5d1130 100644 --- a/src/cargo/core/resolver/errors.rs +++ b/src/cargo/core/resolver/errors.rs @@ -271,6 +271,13 @@ pub(super) fn activation_error( ); } } + IndexSummary::Invalid(summary) => { + let _ = writeln!( + &mut msg, + " version {}'s index entry is invalid", + summary.version() + ); + } } } } else if let Some(candidates) = alt_versions(registry, dep) { diff --git a/src/cargo/sources/registry/index/mod.rs b/src/cargo/sources/registry/index/mod.rs index 94515d22665..87d5b0f9555 100644 --- a/src/cargo/sources/registry/index/mod.rs +++ b/src/cargo/sources/registry/index/mod.rs @@ -135,6 +135,8 @@ pub enum IndexSummary { Offline(Summary), /// From a newer schema version and is likely incomplete or inaccurate Unsupported(Summary, u32), + /// An error was encountered despite being a supported schema version + Invalid(Summary), } impl IndexSummary { @@ -144,7 +146,8 @@ impl IndexSummary { IndexSummary::Candidate(sum) | IndexSummary::Yanked(sum) | IndexSummary::Offline(sum) - | IndexSummary::Unsupported(sum, _) => sum, + | IndexSummary::Unsupported(sum, _) + | IndexSummary::Invalid(sum) => sum, } } @@ -154,7 +157,8 @@ impl IndexSummary { IndexSummary::Candidate(sum) | IndexSummary::Yanked(sum) | IndexSummary::Offline(sum) - | IndexSummary::Unsupported(sum, _) => sum, + | IndexSummary::Unsupported(sum, _) + | IndexSummary::Invalid(sum) => sum, } } @@ -164,6 +168,7 @@ impl IndexSummary { IndexSummary::Yanked(s) => IndexSummary::Yanked(f(s)), IndexSummary::Offline(s) => IndexSummary::Offline(f(s)), IndexSummary::Unsupported(s, v) => IndexSummary::Unsupported(f(s), v.clone()), + IndexSummary::Invalid(s) => IndexSummary::Invalid(f(s)), } } @@ -282,6 +287,22 @@ impl IndexPackage<'_> { } } +#[derive(Deserialize, Serialize)] +struct IndexPackageMinimum { + name: InternedString, + vers: Version, +} + +#[derive(Deserialize, Serialize, Default)] +struct IndexPackageRustVersion { + rust_version: Option, +} + +#[derive(Deserialize, Serialize, Default)] +struct IndexPackageV { + v: Option, +} + /// A dependency as encoded in the [`IndexPackage`] index JSON. #[derive(Deserialize, Serialize, Clone)] pub struct RegistryDependency<'a> { @@ -729,10 +750,45 @@ impl IndexSummary { // between different versions that understand the index differently. // Make sure to consider the INDEX_V_MAX and CURRENT_CACHE_VERSION // values carefully when making changes here. - let index: IndexPackage<'_> = serde_json::from_slice(line)?; + let index_summary = (|| { + let index = serde_json::from_slice::>(line)?; + let summary = index.to_summary(source_id)?; + Ok((index, summary)) + })(); + let (index, summary, valid) = match index_summary { + Ok((index, summary)) => (index, summary, true), + Err(err) => { + let Ok(IndexPackageMinimum { name, vers }) = + serde_json::from_slice::(line) + else { + // If we can't recover, prefer the original error + return Err(err); + }; + tracing::info!( + "recoverying from failed parse of registry package {name}@{vers}: {err}" + ); + let IndexPackageRustVersion { rust_version } = + serde_json::from_slice::(line).unwrap_or_default(); + let IndexPackageV { v } = + serde_json::from_slice::(line).unwrap_or_default(); + let index = IndexPackage { + name, + vers, + rust_version, + v, + deps: Default::default(), + features: Default::default(), + features2: Default::default(), + cksum: Default::default(), + yanked: Default::default(), + links: Default::default(), + }; + let summary = index.to_summary(source_id)?; + (index, summary, false) + } + }; let v = index.v.unwrap_or(1); tracing::trace!("json parsed registry {}/{}", index.name, index.vers); - let summary = index.to_summary(source_id)?; let v_max = if bindeps { INDEX_V_MAX + 1 @@ -742,6 +798,8 @@ impl IndexSummary { if v_max < v { Ok(IndexSummary::Unsupported(summary, v)) + } else if !valid { + Ok(IndexSummary::Invalid(summary)) } else if index.yanked.unwrap_or(false) { Ok(IndexSummary::Yanked(summary)) } else { diff --git a/src/cargo/sources/registry/mod.rs b/src/cargo/sources/registry/mod.rs index 3ff8fad344b..bf10f81fc20 100644 --- a/src/cargo/sources/registry/mod.rs +++ b/src/cargo/sources/registry/mod.rs @@ -834,6 +834,9 @@ impl<'gctx> Source for RegistrySource<'gctx> { summary.version() ); } + IndexSummary::Invalid(summary) => { + tracing::debug!("invalid ({} {})", summary.name(), summary.version()); + } IndexSummary::Offline(summary) => { tracing::debug!("offline ({} {})", summary.name(), summary.version()); } diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 15580035f5a..8498713701c 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -3012,10 +3012,13 @@ fn invalid_json_lines_error() { .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `foo = "^0.1.1"` -candidate versions found which didn't match: 0.2.0, 0.1.0 + version 0.1.3 requires cargo 1.2345 + version 0.1.4 requires a Cargo version that supports index version 1000000000 + version 0.1.5's index entry is invalid + version 0.1.6 requires a Cargo version that supports index version 1000000000 + version 0.1.7's index entry is invalid location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `a v0.5.0 ([ROOT]/foo)` -perhaps a crate was updated and forgotten to be re-vendored? "#]]) .run(); @@ -3024,10 +3027,13 @@ perhaps a crate was updated and forgotten to be re-vendored? .with_stderr_data(str![[r#" [UPDATING] `dummy-registry` index [ERROR] failed to select a version for the requirement `foo = "^0.1.1"` -candidate versions found which didn't match: 0.2.0, 0.1.0 + version 0.1.3 requires cargo 1.2345 + version 0.1.4 requires a Cargo version that supports index version 1000000000 + version 0.1.5's index entry is invalid + version 0.1.6 requires a Cargo version that supports index version 1000000000 + version 0.1.7's index entry is invalid location searched: `dummy-registry` index (which is replacing registry `crates-io`) required by package `a v0.5.0 ([ROOT]/foo)` -perhaps a crate was updated and forgotten to be re-vendored? "#]]) .run(); From 1c0f6cfdcc1b25c3d52020fc2ab5f8917e3e66dd Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 13 Dec 2024 04:25:02 +0900 Subject: [PATCH 221/525] test(tree): new incomplete test for `cargo tree --depth workspace` --- tests/testsuite/tree.rs | 58 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/testsuite/tree.rs b/tests/testsuite/tree.rs index 60374bb59dd..2e81f55ce0e 100644 --- a/tests/testsuite/tree.rs +++ b/tests/testsuite/tree.rs @@ -1846,6 +1846,64 @@ c v1.0.0 .run(); } +#[cargo_test] +fn depth_workspace() { + Package::new("somedep", "1.0.0").publish(); + Package::new("otherdep", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b", "c"] + "#, + ) + .file("a/Cargo.toml", &basic_manifest("a", "1.0.0")) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + + [dependencies] + c = { path = "../c" } + somedep = "1" + "#, + ) + .file("b/src/lib.rs", "") + .file( + "c/Cargo.toml", + r#" + [package] + name = "c" + version = "0.1.0" + + [dependencies] + somedep = "1" + otherdep = "1" + "#, + ) + .file("c/src/lib.rs", "") + .build(); + + p.cargo("tree") + .with_stdout_data(str![[r#" +a v1.0.0 ([ROOT]/foo/a) + +b v0.1.0 ([ROOT]/foo/b) +├── c v0.1.0 ([ROOT]/foo/c) +│ ├── otherdep v1.0.0 +│ └── somedep v1.0.0 +└── somedep v1.0.0 + +c v0.1.0 ([ROOT]/foo/c) (*) + +"#]]) + .run(); +} + #[cargo_test] fn prune() { let p = make_simple_proj(); From fc00223fca7bb7e927aeda2ff1810187ac0d7c4c Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 13 Dec 2024 04:26:50 +0900 Subject: [PATCH 222/525] refactor(tree): replace previous `depth: u32` opts with new type `DisplayDepth` refactor(tree): replace previous `depth: u32` opts with new type `DisplayDepth` --- src/bin/cargo/commands/tree.rs | 10 ++++++-- src/cargo/ops/tree/mod.rs | 43 ++++++++++++++++++++++++++++------ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/bin/cargo/commands/tree.rs b/src/bin/cargo/commands/tree.rs index b0f35370ebc..98b21db6c73 100644 --- a/src/bin/cargo/commands/tree.rs +++ b/src/bin/cargo/commands/tree.rs @@ -2,7 +2,7 @@ use crate::cli; use crate::command_prelude::*; use anyhow::{bail, format_err}; use cargo::core::dependency::DepKind; -use cargo::ops::tree::{self, EdgeKind}; +use cargo::ops::tree::{self, DisplayDepth, EdgeKind}; use cargo::ops::Packages; use cargo::util::print_available_packages; use cargo::util::CargoResult; @@ -162,6 +162,12 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let pkgs_to_prune = args._values_of("prune"); + let display_depth = args + ._value_of("depth") + .map(|s| s.parse::()) + .transpose()? + .unwrap_or(DisplayDepth::MaxDisplayDepth(u32::MAX)); + let packages = args.packages_from_flags()?; let mut invert = args .get_many::("invert") @@ -222,7 +228,7 @@ subtree of the package given to -p.\n\ duplicates: args.flag("duplicates"), format: args.get_one::("format").cloned().unwrap(), graph_features, - max_display_depth: args.value_of_u32("depth")?.unwrap_or(u32::MAX), + display_depth, no_proc_macro, }; diff --git a/src/cargo/ops/tree/mod.rs b/src/cargo/ops/tree/mod.rs index 0940f0ebfad..4ffb235c1d8 100644 --- a/src/cargo/ops/tree/mod.rs +++ b/src/cargo/ops/tree/mod.rs @@ -43,8 +43,9 @@ pub struct TreeOptions { pub format: String, /// Includes features in the tree as separate nodes. pub graph_features: bool, - /// Maximum display depth of the dependency tree. - pub max_display_depth: u32, + /// Display depth of the dependency tree. + /// If non-negative integer, display dependencies with that amount of max depth. + pub display_depth: DisplayDepth, /// Excludes proc-macro dependencies. pub no_proc_macro: bool, } @@ -86,6 +87,30 @@ impl FromStr for Prefix { } } +#[derive(Clone, Copy)] +pub enum DisplayDepth { + MaxDisplayDepth(u32), +} + +impl FromStr for DisplayDepth { + type Err = clap::Error; + + fn from_str(s: &str) -> Result { + match s { + s => s.parse().map(Self::MaxDisplayDepth).map_err(|_| { + clap::Error::raw( + clap::error::ErrorKind::ValueValidation, + format!( + "supported values for --depth are non-negative integers, \ + but `{}` is unknown", + s + ), + ) + }), + } + } +} + struct Symbols { down: &'static str, tee: &'static str, @@ -250,7 +275,7 @@ fn print( pkgs_to_prune, opts.prefix, opts.no_dedupe, - opts.max_display_depth, + opts.display_depth, &mut visited_deps, &mut levels_continue, &mut print_stack, @@ -270,7 +295,7 @@ fn print_node<'a>( pkgs_to_prune: &[PackageIdSpec], prefix: Prefix, no_dedupe: bool, - max_display_depth: u32, + display_depth: DisplayDepth, visited_deps: &mut HashSet, levels_continue: &mut Vec, print_stack: &mut Vec, @@ -329,7 +354,7 @@ fn print_node<'a>( pkgs_to_prune, prefix, no_dedupe, - max_display_depth, + display_depth, visited_deps, levels_continue, print_stack, @@ -349,7 +374,7 @@ fn print_dependencies<'a>( pkgs_to_prune: &[PackageIdSpec], prefix: Prefix, no_dedupe: bool, - max_display_depth: u32, + display_depth: DisplayDepth, visited_deps: &mut HashSet, levels_continue: &mut Vec, print_stack: &mut Vec, @@ -378,6 +403,10 @@ fn print_dependencies<'a>( } } + let max_display_depth = match display_depth { + DisplayDepth::MaxDisplayDepth(max) => max, + }; + // Current level exceeds maximum display depth. Skip. if levels_continue.len() + 1 > max_display_depth as usize { return; @@ -407,7 +436,7 @@ fn print_dependencies<'a>( pkgs_to_prune, prefix, no_dedupe, - max_display_depth, + display_depth, visited_deps, levels_continue, print_stack, From b729060b149c43896924b910b6e55eac8a3c77b9 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 13 Dec 2024 04:33:49 +0900 Subject: [PATCH 223/525] feat(workspace): new function `is_member_id` to check whether given package_id belongs to ws --- src/cargo/core/workspace.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index a2345cf676b..a81c6b9304e 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -591,6 +591,11 @@ impl<'gctx> Workspace<'gctx> { self.member_ids.contains(&pkg.package_id()) } + /// Returns true if the given package_id is a member of the workspace. + pub fn is_member_id(&self, package_id: PackageId) -> bool { + self.member_ids.contains(&package_id) + } + pub fn is_ephemeral(&self) -> bool { self.is_ephemeral } From 65931bcbf614b212b742e985f217cf94ab1f02ef Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 13 Dec 2024 05:56:20 +0900 Subject: [PATCH 224/525] refactor(tree): change signatures of tree printing functions to receive `&Workspace` --- src/cargo/ops/tree/mod.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/cargo/ops/tree/mod.rs b/src/cargo/ops/tree/mod.rs index 4ffb235c1d8..60d7974c49c 100644 --- a/src/cargo/ops/tree/mod.rs +++ b/src/cargo/ops/tree/mod.rs @@ -6,7 +6,7 @@ use crate::core::dependency::DepKind; use crate::core::resolver::{features::CliFeatures, ForceAllTargets, HasDevUnits}; use crate::core::{Package, PackageId, PackageIdSpec, PackageIdSpecQuery, Workspace}; use crate::ops::{self, Packages}; -use crate::util::{CargoResult, GlobalContext}; +use crate::util::CargoResult; use crate::{drop_print, drop_println}; use anyhow::Context as _; use graph::Graph; @@ -228,14 +228,14 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<() try to use option `--target all` first, and then narrow your search scope accordingly.", )?; } else { - print(ws.gctx(), opts, root_indexes, &pkgs_to_prune, &graph)?; + print(ws, opts, root_indexes, &pkgs_to_prune, &graph)?; } Ok(()) } /// Prints a tree for each given root. fn print( - gctx: &GlobalContext, + ws: &Workspace<'_>, opts: &TreeOptions, roots: Vec, pkgs_to_prune: &[PackageIdSpec], @@ -244,7 +244,7 @@ fn print( let format = Pattern::new(&opts.format) .with_context(|| format!("tree format `{}` not valid", opts.format))?; - let symbols = if gctx.shell().out_unicode() { + let symbols = if ws.gctx().shell().out_unicode() { &UTF8_SYMBOLS } else { &ASCII_SYMBOLS @@ -256,7 +256,7 @@ fn print( for (i, root_index) in roots.into_iter().enumerate() { if i != 0 { - drop_println!(gctx); + drop_println!(ws.gctx()); } // A stack of bools used to determine where | symbols should appear @@ -267,7 +267,7 @@ fn print( let mut print_stack = vec![]; print_node( - gctx, + ws, graph, root_index, &format, @@ -287,7 +287,7 @@ fn print( /// Prints a package and all of its dependencies. fn print_node<'a>( - gctx: &GlobalContext, + ws: &Workspace<'_>, graph: &'a Graph<'_>, node_index: usize, format: &Pattern, @@ -303,12 +303,12 @@ fn print_node<'a>( let new = no_dedupe || visited_deps.insert(node_index); match prefix { - Prefix::Depth => drop_print!(gctx, "{}", levels_continue.len()), + Prefix::Depth => drop_print!(ws.gctx(), "{}", levels_continue.len()), Prefix::Indent => { if let Some((last_continues, rest)) = levels_continue.split_last() { for continues in rest { let c = if *continues { symbols.down } else { " " }; - drop_print!(gctx, "{} ", c); + drop_print!(ws.gctx(), "{} ", c); } let c = if *last_continues { @@ -316,7 +316,7 @@ fn print_node<'a>( } else { symbols.ell }; - drop_print!(gctx, "{0}{1}{1} ", c, symbols.right); + drop_print!(ws.gctx(), "{0}{1}{1} ", c, symbols.right); } } Prefix::None => {} @@ -332,7 +332,7 @@ fn print_node<'a>( } else { " (*)" }; - drop_println!(gctx, "{}{}", format.display(graph, node_index), star); + drop_println!(ws.gctx(), "{}{}", format.display(graph, node_index), star); if !new || in_cycle { return; @@ -346,7 +346,7 @@ fn print_node<'a>( EdgeKind::Feature, ] { print_dependencies( - gctx, + ws, graph, node_index, format, @@ -366,7 +366,7 @@ fn print_node<'a>( /// Prints all the dependencies of a package for the given dependency kind. fn print_dependencies<'a>( - gctx: &GlobalContext, + ws: &Workspace<'_>, graph: &'a Graph<'_>, node_index: usize, format: &Pattern, @@ -396,10 +396,10 @@ fn print_dependencies<'a>( if let Some(name) = name { for continues in &**levels_continue { let c = if *continues { symbols.down } else { " " }; - drop_print!(gctx, "{} ", c); + drop_print!(ws.gctx(), "{} ", c); } - drop_println!(gctx, "{}", name); + drop_println!(ws.gctx(), "{}", name); } } @@ -428,7 +428,7 @@ fn print_dependencies<'a>( while let Some(dependency) = it.next() { levels_continue.push(it.peek().is_some()); print_node( - gctx, + ws, graph, *dependency, format, From f7442319e7f1ea17b0c98136424cc83bb289c190 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Fri, 13 Dec 2024 05:57:12 +0900 Subject: [PATCH 225/525] feat(tree): implement filtering logic for `--depth workspace` flag --- src/cargo/ops/tree/mod.rs | 13 ++++++++++--- src/doc/man/cargo-tree.md | 3 +++ src/doc/man/generated_txt/cargo-tree.txt | 3 +++ src/doc/src/commands/cargo-tree.md | 4 +++- src/etc/man/cargo-tree.1 | 3 +++ tests/testsuite/tree.rs | 7 ++----- 6 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/cargo/ops/tree/mod.rs b/src/cargo/ops/tree/mod.rs index 60d7974c49c..e0579696f61 100644 --- a/src/cargo/ops/tree/mod.rs +++ b/src/cargo/ops/tree/mod.rs @@ -45,6 +45,7 @@ pub struct TreeOptions { pub graph_features: bool, /// Display depth of the dependency tree. /// If non-negative integer, display dependencies with that amount of max depth. + /// If `workspace`, display dependencies from current workspace only. pub display_depth: DisplayDepth, /// Excludes proc-macro dependencies. pub no_proc_macro: bool, @@ -90,6 +91,7 @@ impl FromStr for Prefix { #[derive(Clone, Copy)] pub enum DisplayDepth { MaxDisplayDepth(u32), + Workspace, } impl FromStr for DisplayDepth { @@ -97,11 +99,12 @@ impl FromStr for DisplayDepth { fn from_str(s: &str) -> Result { match s { + "workspace" => Ok(Self::Workspace), s => s.parse().map(Self::MaxDisplayDepth).map_err(|_| { clap::Error::raw( clap::error::ErrorKind::ValueValidation, format!( - "supported values for --depth are non-negative integers, \ + "supported values for --depth are non-negative integers and `workspace`, \ but `{}` is unknown", s ), @@ -403,8 +406,9 @@ fn print_dependencies<'a>( } } - let max_display_depth = match display_depth { - DisplayDepth::MaxDisplayDepth(max) => max, + let (max_display_depth, filter_non_workspace_member) = match display_depth { + DisplayDepth::MaxDisplayDepth(max) => (max, false), + DisplayDepth::Workspace => (u32::MAX, true), }; // Current level exceeds maximum display depth. Skip. @@ -418,6 +422,9 @@ fn print_dependencies<'a>( // Filter out packages to prune. match graph.node(**dep) { Node::Package { package_id, .. } => { + if filter_non_workspace_member && !ws.is_member_id(*package_id) { + return false; + } !pkgs_to_prune.iter().any(|spec| spec.matches(*package_id)) } _ => true, diff --git a/src/doc/man/cargo-tree.md b/src/doc/man/cargo-tree.md index ce99b761e7d..fc3521c9939 100644 --- a/src/doc/man/cargo-tree.md +++ b/src/doc/man/cargo-tree.md @@ -96,6 +96,9 @@ Prune the given package from the display of the dependency tree. {{#option "`--depth` _depth_" }} Maximum display depth of the dependency tree. A depth of 1 displays the direct dependencies, for example. + +If the given value is `workspace`, only shows the dependencies that are member +of the current workspace, instead. {{/option}} {{#option "`--no-dedupe`" }} diff --git a/src/doc/man/generated_txt/cargo-tree.txt b/src/doc/man/generated_txt/cargo-tree.txt index 1af730aff53..0fc542f429f 100644 --- a/src/doc/man/generated_txt/cargo-tree.txt +++ b/src/doc/man/generated_txt/cargo-tree.txt @@ -85,6 +85,9 @@ OPTIONS Maximum display depth of the dependency tree. A depth of 1 displays the direct dependencies, for example. + If the given value is workspace, only shows the dependencies that + are member of the current workspace, instead. + --no-dedupe Do not de-duplicate repeated dependencies. Usually, when a package has already displayed its dependencies, further occurrences will not diff --git a/src/doc/src/commands/cargo-tree.md b/src/doc/src/commands/cargo-tree.md index 154939a28ef..9418eaa9d1c 100644 --- a/src/doc/src/commands/cargo-tree.md +++ b/src/doc/src/commands/cargo-tree.md @@ -91,7 +91,9 @@ subtree of the package given to -p.
--depth depth
Maximum display depth of the dependency tree. A depth of 1 displays the direct -dependencies, for example.
+dependencies, for example.

+

If the given value is workspace, only shows the dependencies that are member +of the current workspace, instead.

--no-dedupe
diff --git a/src/etc/man/cargo-tree.1 b/src/etc/man/cargo-tree.1 index c951460b028..25045b3f92d 100644 --- a/src/etc/man/cargo-tree.1 +++ b/src/etc/man/cargo-tree.1 @@ -93,6 +93,9 @@ Prune the given package from the display of the dependency tree. .RS 4 Maximum display depth of the dependency tree. A depth of 1 displays the direct dependencies, for example. +.sp +If the given value is \fBworkspace\fR, only shows the dependencies that are member +of the current workspace, instead. .RE .sp \fB\-\-no\-dedupe\fR diff --git a/tests/testsuite/tree.rs b/tests/testsuite/tree.rs index 2e81f55ce0e..762cc4d9652 100644 --- a/tests/testsuite/tree.rs +++ b/tests/testsuite/tree.rs @@ -1888,15 +1888,12 @@ fn depth_workspace() { .file("c/src/lib.rs", "") .build(); - p.cargo("tree") + p.cargo("tree --depth workspace") .with_stdout_data(str![[r#" a v1.0.0 ([ROOT]/foo/a) b v0.1.0 ([ROOT]/foo/b) -├── c v0.1.0 ([ROOT]/foo/c) -│ ├── otherdep v1.0.0 -│ └── somedep v1.0.0 -└── somedep v1.0.0 +└── c v0.1.0 ([ROOT]/foo/c) c v0.1.0 ([ROOT]/foo/c) (*) From 4e5af28150543b388334de720d1b93c1fb712031 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 13 Dec 2024 10:31:53 -0600 Subject: [PATCH 226/525] refactor(schema): Group TomlManifest fields to clarify requires_package I found a bug in the manifest parser and figured this would help make it more obvious. Since I was already changing the order, I figure I'm make things a little more logical (user-facing first, implementtion details later) --- crates/cargo-util-schemas/src/manifest/mod.rs | 20 +++++++------ src/cargo/util/toml/mod.rs | 26 ++++++++--------- tests/testsuite/features_namespaced.rs | 16 +++++------ tests/testsuite/publish.rs | 28 +++++++++---------- tests/testsuite/weak_dep_features.rs | 8 +++--- 5 files changed, 50 insertions(+), 48 deletions(-) diff --git a/crates/cargo-util-schemas/src/manifest/mod.rs b/crates/cargo-util-schemas/src/manifest/mod.rs index efb71d8ceac..bfb23495c3e 100644 --- a/crates/cargo-util-schemas/src/manifest/mod.rs +++ b/crates/cargo-util-schemas/src/manifest/mod.rs @@ -35,11 +35,13 @@ use crate::schema::TomlValueWrapper; #[serde(rename_all = "kebab-case")] #[cfg_attr(feature = "unstable-schema", derive(schemars::JsonSchema))] pub struct TomlManifest { - // when adding new fields, be sure to check whether `requires_package` should disallow them pub cargo_features: Option>, + + // Update `requires_package` when adding new package-specific fields pub package: Option>, pub project: Option>, - pub profile: Option, + pub badges: Option>>, + pub features: Option>>, pub lib: Option, pub bin: Option>, pub example: Option>, @@ -52,14 +54,14 @@ pub struct TomlManifest { pub build_dependencies: Option>, #[serde(rename = "build_dependencies")] pub build_dependencies2: Option>, - pub features: Option>>, pub target: Option>, - pub replace: Option>, - pub patch: Option>>, - pub workspace: Option, - pub badges: Option>>, pub lints: Option, + pub workspace: Option, + pub profile: Option, + pub patch: Option>>, + pub replace: Option>, + /// Report unused keys (see also nested `_unused_keys`) /// Note: this is populated by the caller, rather than automatically #[serde(skip)] @@ -69,6 +71,8 @@ pub struct TomlManifest { impl TomlManifest { pub fn requires_package(&self) -> impl Iterator { [ + self.badges.as_ref().map(|_| "badges"), + self.features.as_ref().map(|_| "features"), self.lib.as_ref().map(|_| "lib"), self.bin.as_ref().map(|_| "bin"), self.example.as_ref().map(|_| "example"), @@ -79,9 +83,7 @@ impl TomlManifest { self.build_dependencies() .as_ref() .map(|_| "build-dependencies"), - self.features.as_ref().map(|_| "features"), self.target.as_ref().map(|_| "target"), - self.badges.as_ref().map(|_| "badges"), self.lints.as_ref().map(|_| "lints"), ] .into_iter() diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index ab96cdd03b0..0a52c4f0c78 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -279,7 +279,8 @@ fn normalize_toml( cargo_features: original_toml.cargo_features.clone(), package: None, project: None, - profile: original_toml.profile.clone(), + badges: None, + features: None, lib: None, bin: None, example: None, @@ -290,13 +291,12 @@ fn normalize_toml( dev_dependencies2: None, build_dependencies: None, build_dependencies2: None, - features: None, target: None, - replace: original_toml.replace.clone(), - patch: None, - workspace: original_toml.workspace.clone(), - badges: None, lints: None, + workspace: original_toml.workspace.clone(), + profile: original_toml.profile.clone(), + patch: None, + replace: original_toml.replace.clone(), _unused_keys: Default::default(), }; @@ -2820,9 +2820,11 @@ fn prepare_toml_for_publish( let all = |_d: &manifest::TomlDependency| true; let mut manifest = manifest::TomlManifest { + cargo_features: me.cargo_features.clone(), package: Some(package), project: None, - profile: me.profile.clone(), + badges: me.badges.clone(), + features: me.features.clone(), lib, bin, example, @@ -2837,7 +2839,6 @@ fn prepare_toml_for_publish( dev_dependencies2: None, build_dependencies: map_deps(gctx, me.build_dependencies(), all)?, build_dependencies2: None, - features: me.features.clone(), target: match me.target.as_ref().map(|target_map| { target_map .iter() @@ -2863,12 +2864,11 @@ fn prepare_toml_for_publish( Some(Err(e)) => return Err(e), None => None, }, - replace: None, - patch: None, - workspace: None, - badges: me.badges.clone(), - cargo_features: me.cargo_features.clone(), lints: me.lints.clone(), + workspace: None, + profile: me.profile.clone(), + patch: None, + replace: None, _unused_keys: Default::default(), }; strip_features(&mut manifest); diff --git a/tests/testsuite/features_namespaced.rs b/tests/testsuite/features_namespaced.rs index 796a31edc70..d04625a0705 100644 --- a/tests/testsuite/features_namespaced.rs +++ b/tests/testsuite/features_namespaced.rs @@ -1006,6 +1006,9 @@ homepage = "https://example.com/" readme = false license = "MIT" +[features] +feat = ["opt-dep1"] + [lib] name = "foo" path = "src/lib.rs" @@ -1018,9 +1021,6 @@ optional = true version = "1.0" optional = true -[features] -feat = ["opt-dep1"] - "##]], )], ); @@ -1143,6 +1143,11 @@ homepage = "https://example.com/" readme = false license = "MIT" +[features] +feat1 = [] +feat2 = ["dep:bar"] +feat3 = ["feat2"] + [lib] name = "foo" path = "src/lib.rs" @@ -1151,11 +1156,6 @@ path = "src/lib.rs" version = "1.0" optional = true -[features] -feat1 = [] -feat2 = ["dep:bar"] -feat3 = ["feat2"] - "##]], )], ); diff --git a/tests/testsuite/publish.rs b/tests/testsuite/publish.rs index 51ab6cfdaf7..4bcb9194416 100644 --- a/tests/testsuite/publish.rs +++ b/tests/testsuite/publish.rs @@ -2015,6 +2015,20 @@ readme = false license = "MIT" repository = "foo" +[features] +foo_feature = [ + "normal-only/cat", + "build-only/cat", + "normal-and-dev/cat", + "target-normal-only/cat", + "target-build-only/cat", + "target-normal-and-dev/cat", + "optional-dep-feature/cat", + "dep:optional-namespaced", + "optional-renamed-dep-feature10/cat", + "dep:optional-renamed-namespaced10", +] + [[bin]] name = "foo" path = "src/main.rs" @@ -2057,20 +2071,6 @@ features = ["cat"] version = "1.0" features = ["cat"] -[features] -foo_feature = [ - "normal-only/cat", - "build-only/cat", - "normal-and-dev/cat", - "target-normal-only/cat", - "target-build-only/cat", - "target-normal-and-dev/cat", - "optional-dep-feature/cat", - "dep:optional-namespaced", - "optional-renamed-dep-feature10/cat", - "dep:optional-renamed-namespaced10", -] - [target."cfg(unix)".dependencies.target-normal-and-dev] version = "1.0" features = ["cat"] diff --git a/tests/testsuite/weak_dep_features.rs b/tests/testsuite/weak_dep_features.rs index b0997414cfe..583f67c8b53 100644 --- a/tests/testsuite/weak_dep_features.rs +++ b/tests/testsuite/weak_dep_features.rs @@ -650,6 +650,10 @@ homepage = "https://example.com/" readme = false license = "MIT" +[features] +feat1 = [] +feat2 = ["bar?/feat"] + [lib] name = "foo" path = "src/lib.rs" @@ -658,10 +662,6 @@ path = "src/lib.rs" version = "1.0" optional = true -[features] -feat1 = [] -feat2 = ["bar?/feat"] - "##]], )], ); From aef2f6d1ae6f01f0b659717943b1bc30ac5ca3a8 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 13 Dec 2024 10:48:31 -0600 Subject: [PATCH 227/525] test(patch): Ensure the patch actuall gets built --- tests/testsuite/patch.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/testsuite/patch.rs b/tests/testsuite/patch.rs index c919e1184ec..825dbea5937 100644 --- a/tests/testsuite/patch.rs +++ b/tests/testsuite/patch.rs @@ -1479,8 +1479,17 @@ fn patch_in_virtual() { .file("foo/src/lib.rs", r#""#) .build(); - p.cargo("check").run(); - p.cargo("check") + p.cargo("check -p foo") + .with_stderr_data(str![[r#" +[UPDATING] `dummy-registry` index +[LOCKING] 1 package to latest compatible version +[CHECKING] bar v0.1.0 ([ROOT]/foo/bar) +[CHECKING] foo v0.1.0 ([ROOT]/foo/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + p.cargo("check -p foo") .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s From cb30ab61d15f1233cb2c961bb048cdd079b2358c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 13 Dec 2024 10:50:55 -0600 Subject: [PATCH 228/525] refactor(toml): Initialize fields before everything else --- src/cargo/util/toml/mod.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 0a52c4f0c78..81155915191 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -275,6 +275,15 @@ fn normalize_toml( warnings: &mut Vec, errors: &mut Vec, ) -> CargoResult { + let package_root = manifest_file.parent().unwrap(); + + let inherit_cell: LazyCell = LazyCell::new(); + let inherit = || { + inherit_cell + .try_borrow_with(|| load_inheritable_fields(gctx, manifest_file, &workspace_config)) + }; + let workspace_root = || inherit().map(|fields| fields.ws_root().as_path()); + let mut normalized_toml = manifest::TomlManifest { cargo_features: original_toml.cargo_features.clone(), package: None, @@ -300,15 +309,6 @@ fn normalize_toml( _unused_keys: Default::default(), }; - let package_root = manifest_file.parent().unwrap(); - - let inherit_cell: LazyCell = LazyCell::new(); - let inherit = || { - inherit_cell - .try_borrow_with(|| load_inheritable_fields(gctx, manifest_file, &workspace_config)) - }; - let workspace_root = || inherit().map(|fields| fields.ws_root().as_path()); - if let Some(original_package) = original_toml.package() { let package_name = &original_package.name; From e83dc0405ba0f14c166c4a0954fb119b961001da Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 13 Dec 2024 10:55:20 -0600 Subject: [PATCH 229/525] test(patch): Speed up path-bases test --- tests/testsuite/patch.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/testsuite/patch.rs b/tests/testsuite/patch.rs index 825dbea5937..e3e14b90587 100644 --- a/tests/testsuite/patch.rs +++ b/tests/testsuite/patch.rs @@ -3078,7 +3078,12 @@ fn patch_with_base() { .file("src/lib.rs", "use bar::hello as _;") .build(); - p.cargo("build -v") + p.cargo("tree") .masquerade_as_nightly_cargo(&["path-bases"]) + .with_stdout_data(str![[r#" +foo v0.5.0 ([ROOT]/foo) +└── bar v0.5.0 ([ROOT]/bar) + +"#]]) .run(); } From 2d23b94a7f5f86bdf74fd5da2617e82ffad1f222 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 13 Dec 2024 11:00:04 -0600 Subject: [PATCH 230/525] test(base): Verify bases in patches in virtual manifests --- tests/testsuite/patch.rs | 69 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/tests/testsuite/patch.rs b/tests/testsuite/patch.rs index e3e14b90587..eb5efd4c33b 100644 --- a/tests/testsuite/patch.rs +++ b/tests/testsuite/patch.rs @@ -3038,7 +3038,7 @@ foo v0.0.0 ([ROOT]/foo) } #[cargo_test] -fn patch_with_base() { +fn patch_in_real_with_base() { let bar = project() .at("bar") .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) @@ -3087,3 +3087,70 @@ foo v0.5.0 ([ROOT]/foo) "#]]) .run(); } + +#[cargo_test] +fn patch_in_virtual_with_base() { + let bar = project() + .at("bar") + .file("Cargo.toml", &basic_manifest("bar", "0.5.0")) + .file("src/lib.rs", "pub fn hello() {}") + .build(); + Package::new("bar", "0.5.0").publish(); + + let p = project() + .file( + ".cargo/config.toml", + &format!( + r#" + [path-bases] + test = '{}' + "#, + bar.root().parent().unwrap().display() + ), + ) + .file( + "Cargo.toml", + r#" + cargo-features = ["path-bases"] + + [workspace] + members = ["foo"] + + [patch.crates-io] + bar = { base = 'test', path = 'bar' } + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.5.0" + authors = ["wycats@example.com"] + edition = "2018" + + [dependencies] + bar = "0.5.0" + "#, + ) + .file("foo/src/lib.rs", "use bar::hello as _;") + .build(); + + p.cargo("tree") + .masquerade_as_nightly_cargo(&["path-bases"]) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] failed to load source for dependency `bar` + +Caused by: + Unable to update [ROOT]/foo/bar + +Caused by: + failed to read `[ROOT]/foo/bar/Cargo.toml` + +Caused by: + [NOT_FOUND] + +"#]]) + .run(); +} From 5b8b2ac248dca8727c9a10cb8fd6376d9e4f0051 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 13 Dec 2024 11:00:57 -0600 Subject: [PATCH 231/525] fix(base): Support bases in patches in virtual manifests This bug has been there since #14360 --- src/cargo/util/toml/mod.rs | 22 ++++++++++------------ tests/testsuite/patch.rs | 15 +++------------ 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 81155915191..bc80097b705 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -304,7 +304,12 @@ fn normalize_toml( lints: None, workspace: original_toml.workspace.clone(), profile: original_toml.profile.clone(), - patch: None, + patch: normalize_patch( + gctx, + original_toml.patch.as_ref(), + &workspace_root, + features, + )?, replace: original_toml.replace.clone(), _unused_keys: Default::default(), }; @@ -483,13 +488,6 @@ fn normalize_toml( } normalized_toml.target = (!normalized_target.is_empty()).then_some(normalized_target); - normalized_toml.patch = normalize_patch( - gctx, - original_toml.patch.as_ref(), - &workspace_root, - features, - )?; - let normalized_lints = original_toml .lints .clone() @@ -1733,14 +1731,14 @@ fn to_virtual_manifest( root, }; ( - replace(&original_toml, &mut manifest_ctx)?, - patch(&original_toml, &mut manifest_ctx)?, + replace(&normalized_toml, &mut manifest_ctx)?, + patch(&normalized_toml, &mut manifest_ctx)?, ) }; - if let Some(profiles) = &original_toml.profile { + if let Some(profiles) = &normalized_toml.profile { validate_profiles(profiles, gctx.cli_unstable(), &features, warnings)?; } - let resolve_behavior = original_toml + let resolve_behavior = normalized_toml .workspace .as_ref() .and_then(|ws| ws.resolver.as_deref()) diff --git a/tests/testsuite/patch.rs b/tests/testsuite/patch.rs index eb5efd4c33b..ff46b324091 100644 --- a/tests/testsuite/patch.rs +++ b/tests/testsuite/patch.rs @@ -3138,18 +3138,9 @@ fn patch_in_virtual_with_base() { p.cargo("tree") .masquerade_as_nightly_cargo(&["path-bases"]) - .with_status(101) - .with_stderr_data(str![[r#" -[ERROR] failed to load source for dependency `bar` - -Caused by: - Unable to update [ROOT]/foo/bar - -Caused by: - failed to read `[ROOT]/foo/bar/Cargo.toml` - -Caused by: - [NOT_FOUND] + .with_stdout_data(str![[r#" +foo v0.5.0 ([ROOT]/foo/foo) +└── bar v0.5.0 ([ROOT]/bar) "#]]) .run(); From 80409f701657b2c0996751f1b36c19868e7c5f18 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 13 Dec 2024 22:35:12 -0500 Subject: [PATCH 232/525] test(build-std): dont require rustup It checks rustup existence even when test is skipped for other reasons --- tests/testsuite/standard_lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testsuite/standard_lib.rs b/tests/testsuite/standard_lib.rs index 16645c17bce..9fadc4fb02f 100644 --- a/tests/testsuite/standard_lib.rs +++ b/tests/testsuite/standard_lib.rs @@ -389,7 +389,7 @@ fn check_core() { .run(); } -#[cargo_test(build_std_mock, requires = "rustup")] +#[cargo_test(build_std_mock)] fn build_std_with_no_arg_for_core_only_target() { let has_rustup_aarch64_unknown_none = std::process::Command::new("rustup") .args(["target", "list", "--installed"]) From 014378f8c07bec557b6c6608e5baf0761a12d504 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 15 Dec 2024 14:20:56 -0800 Subject: [PATCH 233/525] Clarify status of `home_dir` Users should be using the standard library `home_dir` instead of this crate. --- crates/home/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/home/README.md b/crates/home/README.md index 23523f9bdb1..47f408a1af4 100644 --- a/crates/home/README.md +++ b/crates/home/README.md @@ -7,13 +7,19 @@ This provides the definition of `home_dir` used by Cargo and rustup, as well functions to find the correct value of `CARGO_HOME` and `RUSTUP_HOME`. -The definition of `home_dir` provided by the standard library is +The definition of [`home_dir`] provided by the standard library is incorrect because it considers the `HOME` environment variable on Windows. This causes surprising situations where a Rust program will behave differently depending on whether it is run under a Unix emulation environment like Cygwin or MinGW. Neither Cargo nor rustup use the standard library's definition - they use the definition here. +**Note:** This has been fixed in Rust 1.85 to no longer use the `HOME` +environment variable on Windows. If you are still using this crate for the +purpose of getting a home directory, you are strongly encouraged to switch to +using the standard library's [`home_dir`] instead. It is planned to have the +deprecation notice removed in 1.86. + This crate further provides two functions, `cargo_home` and `rustup_home`, which are the canonical way to determine the location that Cargo and rustup store their data. @@ -25,6 +31,7 @@ See [rust-lang/rust#43321]. > crate may make major changes to its APIs or be deprecated without warning. [rust-lang/rust#43321]: https://github.com/rust-lang/rust/issues/43321 +[`home_dir`]: https://doc.rust-lang.org/nightly/std/env/fn.home_dir.html ## License From 20ec18a3ff7fffbcac015269def9de660e81d187 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 15 Dec 2024 14:22:53 -0800 Subject: [PATCH 234/525] Update home changelog --- crates/home/CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/crates/home/CHANGELOG.md b/crates/home/CHANGELOG.md index 5b1a2f8ea44..f761b83ab11 100644 --- a/crates/home/CHANGELOG.md +++ b/crates/home/CHANGELOG.md @@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.5.11 - 2024-12-16 + +Note: 0.5.10 was not published. + +- Updated package metadata. + [#13184](https://github.com/rust-lang/cargo/pull/13184) +- Updated minimum Rust version to 1.81. + [#13266](https://github.com/rust-lang/cargo/pull/13266) + [#13324](https://github.com/rust-lang/cargo/pull/13324) + [#14871](https://github.com/rust-lang/cargo/pull/14871) +- Updated windows-sys to 0.59. + [#14335](https://github.com/rust-lang/cargo/pull/14335) +- Clarified support level of this crate (not intended for external use). + [#14600](https://github.com/rust-lang/cargo/pull/14600) +- Docs cleanup. + [#14823]() +- Add notice that this crate should not be used, and to use the standard library's `home_dir` instead. + [#14939](https://github.com/rust-lang/cargo/pull/14939) + ## 0.5.9 - 2023-12-15 - Replace SHGetFolderPathW with SHGetKnownFolderPath From f18cbda5ab9c8aeda15ac40ce2aa4cab1ea1fc66 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sun, 15 Dec 2024 14:27:45 -0800 Subject: [PATCH 235/525] Limit release trigger to 0.* tags --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fd35c9b86e5..0b31b6a63fe 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ name: Release on: push: tags: - - "**" + - "0.*" # Prevent multiple releases from starting at the same time. concurrency: From a9e442a97931582af5d8cd1f6203619d595b7c21 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 17 Dec 2024 08:35:45 -0600 Subject: [PATCH 236/525] docs(unstable): Correct stabilization version for MSRV-resolver --- src/doc/src/reference/unstable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index 7dcf79dc7b1..c871dabdd21 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -359,7 +359,7 @@ This was stabilized in 1.79 in [#13608](https://github.com/rust-lang/cargo/pull/ ### MSRV-aware resolver -This was stabilized in 1.83 in [#14639](https://github.com/rust-lang/cargo/pull/14639). +This was stabilized in 1.84 in [#14639](https://github.com/rust-lang/cargo/pull/14639). ### Convert `incompatible_toolchain` error into a lint From 3d066ac1eab0ac51aee7d5f3b27d092e914c48ed Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 17 Dec 2024 08:46:13 -0600 Subject: [PATCH 237/525] docs(unstable): Fix a typo --- src/doc/src/reference/unstable.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index c871dabdd21..3eba4e45804 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -373,7 +373,7 @@ Unimplemented Unimplemented -### Update `cargp new` template to set `package.rust-version = "toolchain"` +### Update `cargo new` template to set `package.rust-version = "toolchain"` Unimplemented From 9ca1485f518fea567bfc873f08712a460e7a274a Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 17 Dec 2024 12:35:29 -0500 Subject: [PATCH 238/525] docs: fix wrong changelog PR link --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ad04a50957..d428822a984 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -563,12 +563,12 @@ - ❗️ cargo-package: Disallow `package.license-file` and `package.readme` pointing to non-existent files during packaging. + [#13921](https://github.com/rust-lang/cargo/pull/13921) - ❗️ cargo-package: generated `.cargo_vcs_info.json` is always included, even when `--allow-dirty` is passed. [#13960](https://github.com/rust-lang/cargo/pull/13960) - ❗️ Disallow passing `--release`/`--debug` flag along with the `--profile` flag. [#13971](https://github.com/rust-lang/cargo/pull/13971) - [#13921](https://github.com/rust-lang/cargo/pull/13921) - ❗️ Remove `lib.plugin` key support in Cargo.toml. Rust plugin support has been deprecated for four years and was removed in 1.75.0. [#13902](https://github.com/rust-lang/cargo/pull/13902) From ca5961462055f52882a3ef44fd531158c06779df Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sun, 15 Dec 2024 19:01:30 -0500 Subject: [PATCH 239/525] test(build-std): Isolate output test to avoid spurious [BLOCKING] messages from concurrent runs 47c2095b1dd580a91e42cb6197b58a318526b8c4 didn't really fix the flakiness. build-std tests use the users `CARGO_HOME` for downloading registry dependencies of the standard library. This reduces disk needs of the tests, speeds up the tests, and reduces the number of network requests that could fail. However, this means all of the tests access the same locks for the package cache. In one test, we assert on the output and a `[BLOCKING]` message can show up, depending on test execution time from concurrent test runs. We are going to hack around this by having the one test that asserts on test output to use the standard `cargo-test-support` `CARGO_HOME`, rather than the users `CARGO_HOME`. There will then only be one process accessing the lock and no `[BLOCKING]` messages. --- tests/build-std/main.rs | 49 +++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/tests/build-std/main.rs b/tests/build-std/main.rs index f8d5e748ac5..32771a01a19 100644 --- a/tests/build-std/main.rs +++ b/tests/build-std/main.rs @@ -25,9 +25,11 @@ use cargo_test_support::{basic_manifest, paths, project, rustc_host, str, Execs} use std::env; use std::path::Path; -fn enable_build_std(e: &mut Execs, arg: Option<&str>) { - e.env_remove("CARGO_HOME"); - e.env_remove("HOME"); +fn enable_build_std(e: &mut Execs, arg: Option<&str>, isolated: bool) { + if !isolated { + e.env_remove("CARGO_HOME"); + e.env_remove("HOME"); + } // And finally actually enable `build-std` for now let arg = match arg { @@ -40,19 +42,41 @@ fn enable_build_std(e: &mut Execs, arg: Option<&str>) { // Helper methods used in the tests below trait BuildStd: Sized { + /// Set `-Zbuild-std` args and will download dependencies of the standard + /// library in users's `CARGO_HOME` (`~/.cargo/`) instead of isolated + /// environment `cargo-test-support` usually provides. + /// + /// The environment is not isolated is to avoid excessive network requests + /// and downloads. A side effect is `[BLOCKING]` will show up in stderr, + /// as a sign of package cahce lock contention when running other build-std + /// tests concurrently. fn build_std(&mut self) -> &mut Self; + + /// Like [`BuildStd::build_std`] and is able to specify what crates to build. fn build_std_arg(&mut self, arg: &str) -> &mut Self; + + /// Like [`BuildStd::build_std`] but use an isolated `CARGO_HOME` environment + /// to avoid package cache lock contention. + /// + /// Don't use this unless you really need to assert the full stderr + /// and avoid any `[BLOCKING]` message. + fn build_std_isolated(&mut self) -> &mut Self; fn target_host(&mut self) -> &mut Self; } impl BuildStd for Execs { fn build_std(&mut self) -> &mut Self { - enable_build_std(self, None); + enable_build_std(self, None, false); self } fn build_std_arg(&mut self, arg: &str) -> &mut Self { - enable_build_std(self, Some(arg)); + enable_build_std(self, Some(arg), false); + self + } + + fn build_std_isolated(&mut self) -> &mut Self { + enable_build_std(self, None, true); self } @@ -107,9 +131,12 @@ fn basic() { ) .build(); - p.cargo("check").build_std().target_host().run(); + // HACK: use an isolated the isolated CARGO_HOME environment (`build_std_isolated`) + // to avoid `[BLOCKING]` messages (from lock contention with other tests) + // from getting in this test's asserts + p.cargo("check").build_std_isolated().target_host().run(); p.cargo("build") - .build_std() + .build_std_isolated() .target_host() // Importantly, this should not say [UPDATING] // There have been multiple bugs where every build triggers and update. @@ -120,7 +147,7 @@ fn basic() { "#]]) .run(); p.cargo("run") - .build_std() + .build_std_isolated() .target_host() .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -129,7 +156,7 @@ fn basic() { "#]]) .run(); p.cargo("test") - .build_std() + .build_std_isolated() .target_host() .with_stderr_data(str![[r#" [COMPILING] rustc-std-workspace-std [..] @@ -379,13 +406,11 @@ fn test_proc_macro() { .file("src/lib.rs", "") .build(); - // Download dependencies first, - // so we can compare `cargo test` output without any wildcard - p.cargo("fetch").build_std().run(); p.cargo("test --lib") .env_remove(cargo_util::paths::dylib_path_envvar()) .build_std() .with_stderr_data(str![[r#" +... [COMPILING] foo v0.0.0 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] unittests src/lib.rs (target/debug/deps/foo-[HASH]) From cbdbfe4ecd6a83c65aba72800abe2d5549058090 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Mon, 16 Dec 2024 23:09:05 -0500 Subject: [PATCH 240/525] refactor(build-std): extract support core only detection This is a preparation of reuse for subsequent fix. --- src/cargo/core/compiler/build_context/target_info.rs | 10 ++++++++++ src/cargo/core/compiler/standard_lib.rs | 8 ++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs index e03c76d6bbf..1844073ae45 100644 --- a/src/cargo/core/compiler/build_context/target_info.rs +++ b/src/cargo/core/compiler/build_context/target_info.rs @@ -619,6 +619,16 @@ impl TargetInfo { .iter() .any(|sup| sup.as_str() == split.as_str()) } + + /// Checks if a target maybe support std. + /// + /// If no explictly stated in target spec json, we treat it as "maybe support". + /// + /// This is only useful for `-Zbuild-std` to determine the default set of + /// crates it is going to build. + pub fn maybe_support_std(&self) -> bool { + matches!(self.supports_std, Some(true) | None) + } } /// Takes rustc output (using specialized command line args), and calculates the file prefix and diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 5e9fe6af494..c135a5d6d73 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -115,10 +115,10 @@ pub fn generate_std_roots( ) -> CargoResult>> { // Generate a map of Units for each kind requested. let mut ret = HashMap::new(); - let (core_only, maybe_std): (Vec<&CompileKind>, Vec<_>) = kinds.iter().partition(|kind| - // Only include targets that explicitly don't support std - target_data.info(**kind).supports_std == Some(false)); - for (default_crate, kinds) in [("core", core_only), ("std", maybe_std)] { + let (maybe_std, maybe_core): (Vec<&CompileKind>, Vec<_>) = kinds + .iter() + .partition(|kind| target_data.info(**kind).maybe_support_std()); + for (default_crate, kinds) in [("core", maybe_core), ("std", maybe_std)] { if kinds.is_empty() { continue; } From f004691aa41449fa06b764683385558edee51428 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sun, 15 Dec 2024 15:23:41 -0500 Subject: [PATCH 241/525] test(build-std): resolve too less deps This failed because since 125e873dffc4b68b263c5decd88750ec10fd441e [`std_resolve`][1] only includes `sysroot` as primary package. When any custom Cargo feature is provided via `-Zbuild-std-feature`, the default feature set `panic-unwind` would be gone, so no `panic_unwind` crate presents in `std_resolve`. When then calling [`std_resolve.query`][2] with the default set of crates from [`std_crates`][3], which automatically includes `panic_unwind` when `std` presents, it'll result in spec not found because `panic_unwind` was not in `std_resolve` anyway. [1]: https://github.com/rust-lang/cargo/blob/addcc8ca715bc7fe20df66afd6efbf3c77ef43f8/src/cargo/core/compiler/standard_lib.rs#L96 [2]: https://github.com/rust-lang/cargo/blob/addcc8ca715bc7fe20df66afd6efbf3c77ef43f8/src/cargo/core/compiler/standard_lib.rs#L158 [3]: https://github.com/rust-lang/cargo/blob/addcc8ca715bc7fe20df66afd6efbf3c77ef43f8/src/cargo/core/compiler/standard_lib.rs#L156 See rust-lang/cargo#14935 --- tests/build-std/main.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/build-std/main.rs b/tests/build-std/main.rs index 32771a01a19..800495da340 100644 --- a/tests/build-std/main.rs +++ b/tests/build-std/main.rs @@ -418,3 +418,30 @@ fn test_proc_macro() { "#]]) .run(); } + +#[cargo_test(build_std_real)] +fn test_panic_abort() { + // See rust-lang/cargo#14935 + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + edition = "2021" + "#, + ) + .file("src/lib.rs", "#![no_std]") + .build(); + + p.cargo("check") + .build_std_arg("std,panic_abort") + .env("RUSTFLAGS", "-C panic=abort") + .arg("-Zbuild-std-features=panic_immediate_abort") + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] package ID specification `panic_unwind` did not match any packages + +"#]]) + .run(); +} From 0149bca5cc8cbdc98089103971fb6530fd037f95 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Mon, 16 Dec 2024 23:23:36 -0500 Subject: [PATCH 242/525] fix(build-std): behavior revert of 125e873dffc4b68b This is kinda a revert of 125e873dffc4b68b263c5decd88750ec10fd441e in terms of the behavior. After this, now `std_resolve` is always resolved by the same set of packages that Cargo will use to generate the unit graph, (technically the same set of crates + `sysroot`), by sharing the same set of primary packages via `std_crates` functions. --- src/cargo/core/compiler/standard_lib.rs | 23 +++++++++++++++++++---- src/cargo/ops/cargo_compile/mod.rs | 11 ++++++++--- src/cargo/ops/cargo_fetch.rs | 10 ++++++++-- tests/build-std/main.rs | 5 ----- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index c135a5d6d73..18253b5d465 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -44,10 +44,14 @@ fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[Unit]) - } /// Resolve the standard library dependencies. +/// +/// * `crates` is the arg value from `-Zbuild-std`. pub fn resolve_std<'gctx>( ws: &Workspace<'gctx>, target_data: &mut RustcTargetData<'gctx>, build_config: &BuildConfig, + crates: &[String], + kinds: &[CompileKind], ) -> CargoResult<(PackageSet<'gctx>, Resolve, ResolvedFeatures)> { if build_config.build_plan { ws.gctx() @@ -65,10 +69,21 @@ pub fn resolve_std<'gctx>( // `[dev-dependencies]`. No need for us to generate a `Resolve` which has // those included because we'll never use them anyway. std_ws.set_require_optional_deps(false); - // `sysroot` + the default feature set below should give us a good default - // Resolve, which includes `libtest` as well. - let specs = Packages::Packages(vec!["sysroot".into()]); - let specs = specs.to_package_id_specs(&std_ws)?; + let specs = { + // If there is anything looks like needing std, resolve with it. + // If not, we assume only `core` maye be needed, as `core the most fundamental crate. + // + // This may need a UI overhaul if `build-std` wants to fully support multi-targets. + let maybe_std = kinds + .iter() + .any(|kind| target_data.info(*kind).maybe_support_std()); + let mut crates = std_crates(crates, if maybe_std { "std" } else { "core" }, &[]); + // `sysroot` is not in the default set because it is optional, but it needs + // to be part of the resolve in case we do need it or `libtest`. + crates.insert("sysroot"); + let specs = Packages::Packages(crates.into_iter().map(Into::into).collect()); + specs.to_package_id_specs(&std_ws)? + }; let features = match &gctx.cli_unstable().build_std_features { Some(list) => list.clone(), None => vec![ diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index c89c064c780..04520d64340 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -289,9 +289,14 @@ pub fn create_bcx<'a, 'gctx>( resolved_features, } = resolve; - let std_resolve_features = if gctx.cli_unstable().build_std.is_some() { - let (std_package_set, std_resolve, std_features) = - standard_lib::resolve_std(ws, &mut target_data, &build_config)?; + let std_resolve_features = if let Some(crates) = &gctx.cli_unstable().build_std { + let (std_package_set, std_resolve, std_features) = standard_lib::resolve_std( + ws, + &mut target_data, + &build_config, + crates, + &build_config.requested_kinds, + )?; pkg_set.add_set(std_package_set); Some((std_resolve, std_features)) } else { diff --git a/src/cargo/ops/cargo_fetch.rs b/src/cargo/ops/cargo_fetch.rs index b1e596f6485..4ff89491d86 100644 --- a/src/cargo/ops/cargo_fetch.rs +++ b/src/cargo/ops/cargo_fetch.rs @@ -64,8 +64,14 @@ pub fn fetch<'a>( } // If -Zbuild-std was passed, download dependencies for the standard library. - if gctx.cli_unstable().build_std.is_some() { - let (std_package_set, _, _) = standard_lib::resolve_std(ws, &mut data, &build_config)?; + if let Some(crates) = &gctx.cli_unstable().build_std { + let (std_package_set, _, _) = standard_lib::resolve_std( + ws, + &mut data, + &build_config, + crates, + &build_config.requested_kinds, + )?; packages.add_set(std_package_set); } diff --git a/tests/build-std/main.rs b/tests/build-std/main.rs index 800495da340..976fc7e141b 100644 --- a/tests/build-std/main.rs +++ b/tests/build-std/main.rs @@ -438,10 +438,5 @@ fn test_panic_abort() { .build_std_arg("std,panic_abort") .env("RUSTFLAGS", "-C panic=abort") .arg("-Zbuild-std-features=panic_immediate_abort") - .with_status(101) - .with_stderr_data(str![[r#" -[ERROR] package ID specification `panic_unwind` did not match any packages - -"#]]) .run(); } From a2e63e6799aac8fa0974fa388731b30daded5ad9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 17 Dec 2024 20:25:51 +0100 Subject: [PATCH 243/525] Do not hash absolute sysroot path into stdlib crates metadata. stdlib crates get a SourceId which has an absolute path pointing into the sysroot. This makes the metadata hash change depending on where you've installed Rust. This is causing problems because the different hashes snowball into the optimizer making different decisions which ends up changing the binary size. (Some context: at work we're working with embedded devices with little flash storage so it often happens that a binary builds locally and then fails to fit in flash in CI, just because CI has installed rustc to a different path. Improving binary size is *not* a goal of this PR, after the fix the size will be whatever, but at least it won't change based on the rustc path anymore) Overview of the fix: - For libstd crates, the metadata hash now contains the path relative to the sysroot, instead of the absolute path. - The absolute path is still hashed into the fingerprint (not the metadata) so moving the rustc installation triggers a rebuild. This ensures stdlib crates are rebuilt when upgrading nightly versions. - The rustc version is still hashed into the metadata as usual, so upgrading Rust releases (not nightly versions) does cause a metadata change. Repro of the bug: ``` $ git clone https://github.com/embassy-rs/embassy --branch cargo-nondet-repro $ cd embassy/ $ cd examples/nrf52840 $ RUSTUP_HOME=~/.rustup1 cargo build --release --bin wifi_esp_hosted .... Finished `release` profile [optimized + debuginfo] target(s) in 13.33s $ llvm-size target/thumbv7em-none-eabi/release/wifi_esp_hosted text data bss dec hex filename 114500 80 48116 162696 27b88 target/thumbv7em-none-eabi/release/wifi_esp_hosted $ RUSTUP_HOME=~/.rustup2 cargo build --release --bin wifi_esp_hosted .... Finished `release` profile [optimized + debuginfo] target(s) in 9.64s $ llvm-size target/humbv7em-none-eabi/release/wifi_esp_hosted text data bss dec hex filename 114272 80 48116 162468 27aa4 target/thumbv7em-none-eabi/release/wifi_esp_hosted ``` --- .../core/compiler/build_runner/compilation_files.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/cargo/core/compiler/build_runner/compilation_files.rs b/src/cargo/core/compiler/build_runner/compilation_files.rs index d9b6676d10a..8c7ddff7f89 100644 --- a/src/cargo/core/compiler/build_runner/compilation_files.rs +++ b/src/cargo/core/compiler/build_runner/compilation_files.rs @@ -604,11 +604,20 @@ fn compute_metadata( METADATA_VERSION.hash(&mut shared_hasher); + let ws_root = if unit.is_std { + // SourceId for stdlib crates is an absolute path inside the sysroot. + // Pass the sysroot as workspace root so that we hash a relative path. + // This avoids the metadata hash changing depending on where the user installed rustc. + &bcx.target_data.get_info(unit.kind).unwrap().sysroot + } else { + bcx.ws.root() + }; + // Unique metadata per (name, source, version) triple. This'll allow us // to pull crates from anywhere without worrying about conflicts. unit.pkg .package_id() - .stable_hash(bcx.ws.root()) + .stable_hash(ws_root) .hash(&mut shared_hasher); // Also mix in enabled features to our metadata. This'll ensure that From 1d74d955a75a47a2e28c90f65cbe27565ed3dc96 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 6 Dec 2024 09:22:22 -0500 Subject: [PATCH 244/525] fix(cargo-rustc): stabilize higher precedence trailing flags This was always enabled on nightly since 1.83-nightly (2024-09). We have no feedback since then, so assume it is a low-impact change. This stabilization is targeted at 1.85 (2025-02-20) --- src/cargo/core/compiler/mod.rs | 39 +++------------------------- tests/testsuite/rustc.rs | 47 ++++++---------------------------- tests/testsuite/rustdoc.rs | 8 +++--- 3 files changed, 16 insertions(+), 78 deletions(-) diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 5dc34616d91..93859e914b9 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -696,10 +696,8 @@ fn prepare_rustc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResult base.inherit_jobserver(&build_runner.jobserver); build_deps_args(&mut base, build_runner, unit)?; add_cap_lints(build_runner.bcx, unit, &mut base); - if cargo_rustc_higher_args_precedence(build_runner) { - if let Some(args) = build_runner.bcx.extra_args_for(unit) { - base.args(args); - } + if let Some(args) = build_runner.bcx.extra_args_for(unit) { + base.args(args); } base.args(&unit.rustflags); if build_runner.bcx.gctx.cli_unstable().binary_dep_depinfo { @@ -754,12 +752,6 @@ fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResu rustdoc.args(unit.pkg.manifest().lint_rustflags()); - if !cargo_rustc_higher_args_precedence(build_runner) { - if let Some(args) = build_runner.bcx.extra_args_for(unit) { - rustdoc.args(args); - } - } - let metadata = build_runner.metadata_for_doc_units[unit]; rustdoc .arg("-C") @@ -800,10 +792,8 @@ fn prepare_rustdoc(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoResu rustdoc::add_output_format(build_runner, unit, &mut rustdoc)?; - if cargo_rustc_higher_args_precedence(build_runner) { - if let Some(args) = build_runner.bcx.extra_args_for(unit) { - rustdoc.args(args); - } + if let Some(args) = build_runner.bcx.extra_args_for(unit) { + rustdoc.args(args); } rustdoc.args(&unit.rustdocflags); @@ -1107,11 +1097,6 @@ fn build_base_args( cmd.args(unit.pkg.manifest().lint_rustflags()); cmd.args(&profile_rustflags); - if !cargo_rustc_higher_args_precedence(build_runner) { - if let Some(args) = build_runner.bcx.extra_args_for(unit) { - cmd.args(args); - } - } // `-C overflow-checks` is implied by the setting of `-C debug-assertions`, // so we only need to provide `-C overflow-checks` if it differs from @@ -2007,19 +1992,3 @@ fn scrape_output_path(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> CargoR .outputs(unit) .map(|outputs| outputs[0].path.clone()) } - -/// Provides a way to change the precedence of `cargo rustc -- `. -/// -/// This is intended to be a short-live function. -/// -/// See -fn cargo_rustc_higher_args_precedence(build_runner: &BuildRunner<'_, '_>) -> bool { - build_runner.bcx.gctx.nightly_features_allowed - && build_runner - .bcx - .gctx - .get_env("__CARGO_RUSTC_ORIG_ARGS_PRIO") - .ok() - .as_deref() - != Some("1") -} diff --git a/tests/testsuite/rustc.rs b/tests/testsuite/rustc.rs index 2041c006706..34d00b7833f 100644 --- a/tests/testsuite/rustc.rs +++ b/tests/testsuite/rustc.rs @@ -28,7 +28,7 @@ fn lib() { p.cargo("rustc --lib -v -- -C debug-assertions=off") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) -[RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=2 [..]-C debug-assertions=off[..]-C metadata=[..] [..]--out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps` +[RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=2 [..]-C metadata=[..] [..]--out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps[..]-C debug-assertions=off[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) @@ -46,7 +46,7 @@ fn build_main_and_allow_unstable_options() { .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=2 [..]-C metadata=[..] --out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps` -[RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link[..]-C debuginfo=2 [..]-C debug-assertions[..]-C metadata=[..] --out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps --extern foo=[ROOT]/foo/target/debug/deps/libfoo-[HASH].rlib` +[RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]--crate-type bin --emit=[..]link[..]-C debuginfo=2 [..]-C metadata=[..] --out-dir [ROOT]/foo/target/debug/deps -L dependency=[ROOT]/foo/target/debug/deps --extern foo=[ROOT]/foo/target/debug/deps/libfoo-[HASH].rlib[..]-C debug-assertions[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) @@ -83,7 +83,7 @@ fn build_with_args_to_one_of_multiple_binaries() { .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=2 [..]-C metadata=[..] --out-dir [..]` -[RUNNING] `rustc --crate-name bar --edition=2015 src/bin/bar.rs [..]--crate-type bin --emit=[..]link[..]-C debuginfo=2 [..]-C debug-assertions [..]` +[RUNNING] `rustc --crate-name bar --edition=2015 src/bin/bar.rs [..]--crate-type bin --emit=[..]link[..]-C debuginfo=2 [..]-C debug-assertions[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) @@ -358,7 +358,7 @@ fn build_with_args_to_one_of_multiple_tests() { .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc --crate-name foo --edition=2015 src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C debuginfo=2 [..]-C metadata=[..] --out-dir [..]` -[RUNNING] `rustc --crate-name bar --edition=2015 tests/bar.rs [..]--emit=[..]link[..]-C debuginfo=2 [..]-C debug-assertions --test[..]` +[RUNNING] `rustc --crate-name bar --edition=2015 tests/bar.rs [..]--emit=[..]link[..]-C debuginfo=2 [..]--test[..]-C debug-assertions[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) @@ -395,7 +395,7 @@ fn build_foo_with_bar_dependency() { [COMPILING] bar v0.1.0 ([ROOT]/bar) [RUNNING] `rustc --crate-name bar [..] -C debuginfo=2[..]` [COMPILING] foo v0.0.1 ([ROOT]/foo) -[RUNNING] `rustc --crate-name foo [..] -C debuginfo=2 [..]-C debug-assertions [..]` +[RUNNING] `rustc --crate-name foo [..] -C debuginfo=2 [..]-C debug-assertions[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) @@ -430,7 +430,7 @@ fn build_only_bar_dependency() { .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [COMPILING] bar v0.1.0 ([ROOT]/bar) -[RUNNING] `rustc --crate-name bar [..]--crate-type lib [..] -C debug-assertions [..]` +[RUNNING] `rustc --crate-name bar [..]--crate-type lib [..] -C debug-assertions[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) @@ -599,7 +599,7 @@ fn rustc_fingerprint() { p.cargo("rustc -v -- -C debug-assertions") .with_stderr_data(str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) -[RUNNING] `rustc --crate-name foo [..]-C debug-assertions [..]` +[RUNNING] `rustc --crate-name foo [..]-C debug-assertions[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) @@ -657,7 +657,7 @@ fn rustc_test_with_implicit_bin() { .with_stderr_data( str![[r#" [COMPILING] foo v0.5.0 ([ROOT]/foo) -[RUNNING] `rustc --crate-name test1 --edition=2015 tests/test1.rs [..] --cfg foo [..]` +[RUNNING] `rustc --crate-name test1 --edition=2015 tests/test1.rs [..] --cfg foo[..]` [RUNNING] `rustc --crate-name foo --edition=2015 src/main.rs [..]` ... [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -816,7 +816,6 @@ fn precedence() { p.cargo("rustc --release -v -- --cfg cargo_rustc -C strip=symbols") .env("RUSTFLAGS", "--cfg from_rustflags") - .masquerade_as_nightly_cargo(&["cargo-rustc-precedence"]) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.0 ([ROOT]/foo) [RUNNING] `rustc [..]-C strip=debuginfo [..]--cfg cargo_rustc -C strip=symbols --cfg from_rustflags` @@ -824,34 +823,4 @@ fn precedence() { "#]]) .run(); - - // Ensure the short-live env var to work - p.cargo("clean").run(); - p.cargo("rustc --release -v -- --cfg cargo_rustc -C strip=symbols") - .env("RUSTFLAGS", "--cfg from_rustflags") - .env("__CARGO_RUSTC_ORIG_ARGS_PRIO", "1") - .masquerade_as_nightly_cargo(&["cargo-rustc-precedence"]) - .with_stderr_data( - str![[r#" -[COMPILING] foo v0.0.0 ([ROOT]/foo) -[RUNNING] `rustc [..]--cfg cargo_rustc -C strip=symbols [..]-C strip=debuginfo [..]--cfg from_rustflags` -[FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s - -"#]] - ) - .run(); - - // Ensure non-nightly to work as before - p.cargo("clean").run(); - p.cargo("rustc --release -v -- --cfg cargo_rustc -C strip=symbols") - .env("RUSTFLAGS", "--cfg from_rustflags") - .with_stderr_data( - str![[r#" -[COMPILING] foo v0.0.0 ([ROOT]/foo) -[RUNNING] `rustc [..]--cfg cargo_rustc -C strip=symbols [..]-C strip=debuginfo [..]--cfg from_rustflags` -[FINISHED] `release` profile [optimized] target(s) in [ELAPSED]s - -"#]] - ) - .run(); } diff --git a/tests/testsuite/rustdoc.rs b/tests/testsuite/rustdoc.rs index 3a7977cb8cc..a502dd27222 100644 --- a/tests/testsuite/rustdoc.rs +++ b/tests/testsuite/rustdoc.rs @@ -106,7 +106,7 @@ fn rustdoc_args() { p.cargo("rustdoc -v -- --cfg=foo") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) -[RUNNING] `rustdoc [..] --crate-name foo src/lib.rs -o [ROOT]/foo/target/doc [..] --cfg=foo -C metadata=[..] -L dependency=[ROOT]/foo/target/debug/deps [..]` +[RUNNING] `rustdoc [..] --crate-name foo src/lib.rs -o [ROOT]/foo/target/doc [..]-C metadata=[..] -L dependency=[ROOT]/foo/target/debug/deps [..]--cfg=foo[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html @@ -159,7 +159,7 @@ fn rustdoc_foo_with_bar_dependency() { [CHECKING] bar v0.0.1 ([ROOT]/bar) [RUNNING] `rustc [..] [ROOT]/bar/src/lib.rs [..]` [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) -[RUNNING] `rustdoc [..] --crate-name foo src/lib.rs -o [ROOT]/foo/target/doc [..] --cfg=foo -C metadata=[..] -L dependency=[ROOT]/foo/target/debug/deps --extern [..]` +[RUNNING] `rustdoc [..] --crate-name foo src/lib.rs -o [ROOT]/foo/target/doc [..]-C metadata=[..] -L dependency=[ROOT]/foo/target/debug/deps --extern [..]--cfg=foo[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html @@ -195,7 +195,7 @@ fn rustdoc_only_bar_dependency() { .with_stderr_data(str![[r#" [LOCKING] 1 package to latest compatible version [DOCUMENTING] bar v0.0.1 ([ROOT]/bar) -[RUNNING] `rustdoc [..] --crate-name bar [ROOT]/bar/src/lib.rs -o [ROOT]/foo/target/doc [..] --cfg=foo -C metadata=[..] -L dependency=[ROOT]/foo/target/debug/deps [..]` +[RUNNING] `rustdoc [..] --crate-name bar [ROOT]/bar/src/lib.rs -o [ROOT]/foo/target/doc [..]-C metadata=[..] -L dependency=[ROOT]/foo/target/debug/deps [..]--cfg=foo[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/bar/index.html @@ -213,7 +213,7 @@ fn rustdoc_same_name_documents_lib() { p.cargo("rustdoc -v -- --cfg=foo") .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) -[RUNNING] `rustdoc [..] --crate-name foo src/lib.rs -o [ROOT]/foo/target/doc [..] --cfg=foo -C metadata=[..] -L dependency=[ROOT]/foo/target/debug/deps [..]` +[RUNNING] `rustdoc [..] --crate-name foo src/lib.rs -o [ROOT]/foo/target/doc [..]-C metadata=[..] -L dependency=[ROOT]/foo/target/debug/deps [..]--cfg=foo[..]` [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [GENERATED] [ROOT]/foo/target/doc/foo/index.html From 43e1527e2f87b97e8ef5806d9b8f6766db35a412 Mon Sep 17 00:00:00 2001 From: Rustin170506 Date: Wed, 18 Dec 2024 21:56:58 +0800 Subject: [PATCH 245/525] docs: add missing argument to Rustup Cargo workaround Signed-off-by: Rustin170506 --- src/doc/contrib/src/process/working-on-cargo.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/doc/contrib/src/process/working-on-cargo.md b/src/doc/contrib/src/process/working-on-cargo.md index fa583ff1134..d3069702e6b 100644 --- a/src/doc/contrib/src/process/working-on-cargo.md +++ b/src/doc/contrib/src/process/working-on-cargo.md @@ -72,7 +72,8 @@ the directory for each compilation, this can cause different calls to `rustc` to use different versions. There are a few workarounds: * Don't use rustup overrides. -* Use `rustup run target/debug/cargo` to execute `cargo`. +* Use `rustup run target/debug/cargo` to specify the toolchain(rustc) to use. + For example, `rustup run nightly target/debug/cargo`. * Set the `RUSTC` environment variable to a specific `rustc` executable (not the rustup wrapper). * Create a [custom toolchain]. This is a bit of a hack, but you can create a From 48e8a718ecceb42c6648a2c7ce7efa8d2c37161b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 18 Dec 2024 10:58:44 -0600 Subject: [PATCH 246/525] test(git): Collect tests that use the same body --- tests/testsuite/git_shallow.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/testsuite/git_shallow.rs b/tests/testsuite/git_shallow.rs index 6a45621e63b..df4be11498d 100644 --- a/tests/testsuite/git_shallow.rs +++ b/tests/testsuite/git_shallow.rs @@ -14,6 +14,11 @@ fn gitoxide_clones_shallow_two_revs_same_deps() { perform_two_revs_same_deps(true) } +#[cargo_test] +fn two_revs_same_deps() { + perform_two_revs_same_deps(false) +} + fn perform_two_revs_same_deps(shallow: bool) { let bar = git::new("meta-dep", |project| { project @@ -106,11 +111,6 @@ fn perform_two_revs_same_deps(shallow: bool) { foo.process(&foo.bin("foo")).run(); } -#[cargo_test] -fn two_revs_same_deps() { - perform_two_revs_same_deps(false) -} - #[cargo_test] fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_with_git2_fetch( ) -> anyhow::Result<()> { From 89b89bde65eb0c687e19dba575ade0c9cc9f0f3a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 18 Dec 2024 11:04:47 -0600 Subject: [PATCH 247/525] test(git): Clarify test parameter --- tests/testsuite/git_shallow.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/testsuite/git_shallow.rs b/tests/testsuite/git_shallow.rs index df4be11498d..e58ce784ce3 100644 --- a/tests/testsuite/git_shallow.rs +++ b/tests/testsuite/git_shallow.rs @@ -11,15 +11,15 @@ enum RepoMode { #[cargo_test] fn gitoxide_clones_shallow_two_revs_same_deps() { - perform_two_revs_same_deps(true) + perform_two_revs_same_deps(RepoMode::Shallow) } #[cargo_test] fn two_revs_same_deps() { - perform_two_revs_same_deps(false) + perform_two_revs_same_deps(RepoMode::Complete) } -fn perform_two_revs_same_deps(shallow: bool) { +fn perform_two_revs_same_deps(mode: RepoMode) { let bar = git::new("meta-dep", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.0.0")) @@ -97,10 +97,9 @@ fn perform_two_revs_same_deps(shallow: bool) { ) .build(); - let args = if shallow { - "build -v -Zgitoxide=fetch -Zgit=shallow-deps" - } else { - "build -v" + let args = match mode { + RepoMode::Complete => "build -v", + RepoMode::Shallow => "build -v -Zgitoxide=fetch -Zgit=shallow-deps", }; foo.cargo(args) .masquerade_as_nightly_cargo(&[ From 4cc7ff5ab594ff8c3834b8dc277d190f0a938066 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 18 Dec 2024 11:22:59 -0600 Subject: [PATCH 248/525] test(git): Fix masquerade feature list for shallow fetch --- tests/testsuite/git_shallow.rs | 62 ++++++++++------------------------ 1 file changed, 17 insertions(+), 45 deletions(-) diff --git a/tests/testsuite/git_shallow.rs b/tests/testsuite/git_shallow.rs index e58ce784ce3..f456584192d 100644 --- a/tests/testsuite/git_shallow.rs +++ b/tests/testsuite/git_shallow.rs @@ -102,9 +102,7 @@ fn perform_two_revs_same_deps(mode: RepoMode) { RepoMode::Shallow => "build -v -Zgitoxide=fetch -Zgit=shallow-deps", }; foo.cargo(args) - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); assert!(foo.bin("foo").is_file()); foo.process(&foo.bin("foo")).run(); @@ -131,9 +129,7 @@ fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_with_git2_fetch( p.cargo("fetch") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); let shallow_repo = gix::open_opts(find_index(), gix::open::Options::isolated())?; @@ -214,9 +210,7 @@ fn gitoxide_clones_git_dependency_with_shallow_protocol_and_git2_is_used_for_fol p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-deps") - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); let db_clone = gix::open_opts( @@ -352,9 +346,7 @@ fn gitoxide_shallow_clone_followed_by_non_shallow_update() -> anyhow::Result<()> p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-deps") - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); let shallow_db_clone = gix::open_opts( @@ -398,7 +390,7 @@ fn gitoxide_shallow_clone_followed_by_non_shallow_update() -> anyhow::Result<()> p.cargo("update") .arg("-Zgitoxide=fetch") // shallow-deps is omitted intentionally - .masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch"]) .run(); let db_clone = gix::open_opts( @@ -471,9 +463,7 @@ fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_fetch_maintains_ p.cargo("fetch") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); let repo = gix::open_opts(find_index(), gix::open::Options::isolated())?; @@ -491,9 +481,7 @@ fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_fetch_maintains_ p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") // NOTE: the flag needs to be consistent or else a different index is created - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); assert_eq!( @@ -511,9 +499,7 @@ fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_fetch_maintains_ p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); assert_eq!( @@ -550,7 +536,7 @@ fn gitoxide_clones_registry_without_shallow_protocol_and_follow_up_fetch_uses_sh .build(); p.cargo("fetch") .arg("-Zgitoxide=fetch") - .masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch"]) .run(); let repo = gix::open_opts(find_index(), gix::open::Options::isolated())?; @@ -568,9 +554,7 @@ fn gitoxide_clones_registry_without_shallow_protocol_and_follow_up_fetch_uses_sh p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); let shallow_repo = gix::open_opts( @@ -592,9 +576,7 @@ fn gitoxide_clones_registry_without_shallow_protocol_and_follow_up_fetch_uses_sh p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); assert_eq!( @@ -610,7 +592,7 @@ fn gitoxide_clones_registry_without_shallow_protocol_and_follow_up_fetch_uses_sh p.cargo("update") .arg("-Zgitoxide=fetch") - .masquerade_as_nightly_cargo(&["unstable features must be available for -Z gitoxide"]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch"]) .run(); assert_eq!( @@ -662,9 +644,7 @@ fn gitoxide_git_dependencies_switch_from_branch_to_rev() -> anyhow::Result<()> { p.cargo("check") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-deps") - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); let db_clone = gix::open_opts( @@ -695,9 +675,7 @@ fn gitoxide_git_dependencies_switch_from_branch_to_rev() -> anyhow::Result<()> { p.cargo("check") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-deps") - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); assert!( @@ -746,9 +724,7 @@ fn shallow_deps_work_with_revisions_and_branches_mixed_on_same_dependency() -> a p.cargo("check") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-deps") - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); let db_paths = glob::glob(paths::home().join(".cargo/git/db/bar-*").to_str().unwrap())? @@ -789,9 +765,7 @@ fn gitoxide_clones_registry_with_shallow_protocol_and_aborts_and_updates_again( p.cargo("fetch") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); let repo = gix::open_opts(find_index(), gix::open::Options::isolated())?; @@ -814,9 +788,7 @@ fn gitoxide_clones_registry_with_shallow_protocol_and_aborts_and_updates_again( p.cargo("update") .arg("-Zgitoxide=fetch") .arg("-Zgit=shallow-index") - .masquerade_as_nightly_cargo(&[ - "unstable features must be available for -Z gitoxide and -Z git", - ]) + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-index"]) .run(); assert!(!shallow_lock.is_file(), "the repository was re-initialized"); From 1c82fe420e0989024138c47c9d8ebf58fd741980 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 18 Dec 2024 11:49:51 -0600 Subject: [PATCH 249/525] test(git): Prefer check over build for faster tests If check passes, it should be sufficient. --- tests/testsuite/git_shallow.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/testsuite/git_shallow.rs b/tests/testsuite/git_shallow.rs index f456584192d..c84e758d8b0 100644 --- a/tests/testsuite/git_shallow.rs +++ b/tests/testsuite/git_shallow.rs @@ -98,14 +98,12 @@ fn perform_two_revs_same_deps(mode: RepoMode) { .build(); let args = match mode { - RepoMode::Complete => "build -v", - RepoMode::Shallow => "build -v -Zgitoxide=fetch -Zgit=shallow-deps", + RepoMode::Complete => "check -v", + RepoMode::Shallow => "check -v -Zgitoxide=fetch -Zgit=shallow-deps", }; foo.cargo(args) .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); - assert!(foo.bin("foo").is_file()); - foo.process(&foo.bin("foo")).run(); } #[cargo_test] From 34d0bcb3e9c44df9eb80b27887aec30d00236092 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 18 Dec 2024 11:56:37 -0600 Subject: [PATCH 250/525] feat(test); Add arg_line support to Execs --- crates/cargo-test-support/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/cargo-test-support/src/lib.rs b/crates/cargo-test-support/src/lib.rs index f282b3d9bdd..9eeb4eb00f8 100644 --- a/crates/cargo-test-support/src/lib.rs +++ b/crates/cargo-test-support/src/lib.rs @@ -1524,6 +1524,12 @@ impl ArgLineCommandExt for &mut ProcessBuilder { } } +impl ArgLineCommandExt for &mut Execs { + fn arg>(self, s: S) -> Self { + self.arg(s) + } +} + impl ArgLineCommandExt for snapbox::cmd::Command { fn arg>(self, s: S) -> Self { self.arg(s) From 4e3e353978e1d54fbf754106ae14cea568f432d2 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 18 Dec 2024 11:57:16 -0600 Subject: [PATCH 251/525] test(git): Reduce duplication in shallow test --- tests/testsuite/git_shallow.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/testsuite/git_shallow.rs b/tests/testsuite/git_shallow.rs index c84e758d8b0..fce4c7c1d85 100644 --- a/tests/testsuite/git_shallow.rs +++ b/tests/testsuite/git_shallow.rs @@ -97,11 +97,12 @@ fn perform_two_revs_same_deps(mode: RepoMode) { ) .build(); - let args = match mode { - RepoMode::Complete => "check -v", - RepoMode::Shallow => "check -v -Zgitoxide=fetch -Zgit=shallow-deps", + let mode_args = match mode { + RepoMode::Complete => "", + RepoMode::Shallow => "-Zgitoxide=fetch -Zgit=shallow-deps", }; - foo.cargo(args) + foo.cargo("check -v") + .arg_line(mode_args) .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); } From cade73a7a09a8ed0384ea47f372c910cff42cc8c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 18 Dec 2024 14:56:12 -0600 Subject: [PATCH 252/525] test(git): Make test backend clearer --- tests/testsuite/git_shallow.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/tests/testsuite/git_shallow.rs b/tests/testsuite/git_shallow.rs index fce4c7c1d85..f330c7f9727 100644 --- a/tests/testsuite/git_shallow.rs +++ b/tests/testsuite/git_shallow.rs @@ -4,22 +4,27 @@ use cargo_test_support::{basic_manifest, git, paths, project}; use crate::git_gc::find_index; +enum Backend { + Git2, + Gitoxide, +} + enum RepoMode { Shallow, Complete, } #[cargo_test] -fn gitoxide_clones_shallow_two_revs_same_deps() { - perform_two_revs_same_deps(RepoMode::Shallow) +fn gitoxide_fetch_shallow_two_revs_same_deps() { + fetch_two_revs_same_deps(Backend::Gitoxide, RepoMode::Shallow) } #[cargo_test] -fn two_revs_same_deps() { - perform_two_revs_same_deps(RepoMode::Complete) +fn git2_fetch_complete_two_revs_same_deps() { + fetch_two_revs_same_deps(Backend::Git2, RepoMode::Complete) } -fn perform_two_revs_same_deps(mode: RepoMode) { +fn fetch_two_revs_same_deps(backend: Backend, mode: RepoMode) { let bar = git::new("meta-dep", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.0.0")) @@ -97,12 +102,18 @@ fn perform_two_revs_same_deps(mode: RepoMode) { ) .build(); + let backend_args = match backend { + Backend::Git2 => "", + Backend::Gitoxide => "-Zgitoxide=fetch", + }; let mode_args = match mode { RepoMode::Complete => "", - RepoMode::Shallow => "-Zgitoxide=fetch -Zgit=shallow-deps", + RepoMode::Shallow => "-Zgit=shallow-deps", }; foo.cargo("check -v") + .arg_line(backend_args) .arg_line(mode_args) + .env("__CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2", "0") // respect `backend` .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); } From adf25782e08315e2b91c79f747aa57d8b5179358 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 18 Dec 2024 14:58:36 -0600 Subject: [PATCH 253/525] test(git): Extract CLI arg creation --- tests/testsuite/git_shallow.rs | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/tests/testsuite/git_shallow.rs b/tests/testsuite/git_shallow.rs index f330c7f9727..4cd13e4dd2a 100644 --- a/tests/testsuite/git_shallow.rs +++ b/tests/testsuite/git_shallow.rs @@ -9,11 +9,29 @@ enum Backend { Gitoxide, } +impl Backend { + fn to_arg(&self) -> &'static str { + match self { + Backend::Git2 => "", + Backend::Gitoxide => "-Zgitoxide=fetch", + } + } +} + enum RepoMode { Shallow, Complete, } +impl RepoMode { + fn to_deps_arg(&self) -> &'static str { + match self { + RepoMode::Complete => "", + RepoMode::Shallow => "-Zgit=shallow-deps", + } + } +} + #[cargo_test] fn gitoxide_fetch_shallow_two_revs_same_deps() { fetch_two_revs_same_deps(Backend::Gitoxide, RepoMode::Shallow) @@ -102,17 +120,9 @@ fn fetch_two_revs_same_deps(backend: Backend, mode: RepoMode) { ) .build(); - let backend_args = match backend { - Backend::Git2 => "", - Backend::Gitoxide => "-Zgitoxide=fetch", - }; - let mode_args = match mode { - RepoMode::Complete => "", - RepoMode::Shallow => "-Zgit=shallow-deps", - }; foo.cargo("check -v") - .arg_line(backend_args) - .arg_line(mode_args) + .arg_line(backend.to_arg()) + .arg_line(mode.to_deps_arg()) .env("__CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2", "0") // respect `backend` .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) .run(); From a739b00f300d4ba317735607d504acee0bc8f14c Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 18 Dec 2024 15:03:18 -0600 Subject: [PATCH 254/525] test(git): Clarify we are doing shallow fetches --- tests/testsuite/git_shallow.rs | 44 +++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/testsuite/git_shallow.rs b/tests/testsuite/git_shallow.rs index 4cd13e4dd2a..76425c19704 100644 --- a/tests/testsuite/git_shallow.rs +++ b/tests/testsuite/git_shallow.rs @@ -129,7 +129,7 @@ fn fetch_two_revs_same_deps(backend: Backend, mode: RepoMode) { } #[cargo_test] -fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_with_git2_fetch( +fn gitoxide_fetch_registry_with_shallow_protocol_and_follow_up_with_git2_fetch( ) -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let p = project() @@ -160,7 +160,7 @@ fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_with_git2_fetch( .all()? .count(), 1, - "shallow clones always start at depth of 1 to minimize download size" + "shallow fetch always start at depth of 1 to minimize download size" ); assert!(shallow_repo.is_shallow()); @@ -179,14 +179,14 @@ fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_with_git2_fetch( .all()? .count(), 3, - "an entirely new repo was cloned which is never shallow" + "an entirely new repo was fetched which is never shallow" ); assert!(!repo.is_shallow()); Ok(()) } #[cargo_test] -fn gitoxide_clones_git_dependency_with_shallow_protocol_and_git2_is_used_for_followup_fetches( +fn gitoxide_fetch_git_dependency_with_shallow_protocol_and_git2_is_used_for_followup_fetches( ) -> anyhow::Result<()> { // Example where an old lockfile with an explicit branch="master" in Cargo.toml. Package::new("bar", "1.0.0").publish(); @@ -245,7 +245,7 @@ fn gitoxide_clones_git_dependency_with_shallow_protocol_and_git2_is_used_for_fol .all()? .count(), 1, - "db clones are shallow and have a shortened history" + "db fetch are shallow and have a shortened history" ); let dep_checkout = gix::open_opts( @@ -256,7 +256,7 @@ fn gitoxide_clones_git_dependency_with_shallow_protocol_and_git2_is_used_for_fol assert_eq!( dep_checkout.head_id()?.ancestors().all()?.count(), 1, - "db checkouts are hard-linked clones with the shallow file copied separately." + "db checkouts are hard-linked fetches with the shallow file copied separately." ); bar.change_file("src/lib.rs", "// another change"); @@ -324,7 +324,7 @@ fn gitoxide_clones_git_dependency_with_shallow_protocol_and_git2_is_used_for_fol } #[cargo_test] -fn gitoxide_shallow_clone_followed_by_non_shallow_update() -> anyhow::Result<()> { +fn gitoxide_shallow_fetch_followed_by_non_shallow_update() -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let (bar, bar_repo) = git::new_repo("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) @@ -381,7 +381,7 @@ fn gitoxide_shallow_clone_followed_by_non_shallow_update() -> anyhow::Result<()> .all()? .count(), 1, - "db clones are shallow and have a shortened history" + "db fetches are shallow and have a shortened history" ); let dep_checkout = gix::open_opts( @@ -392,7 +392,7 @@ fn gitoxide_shallow_clone_followed_by_non_shallow_update() -> anyhow::Result<()> assert_eq!( dep_checkout.head_id()?.ancestors().all()?.count(), 1, - "db checkouts are hard-linked clones with the shallow file copied separately." + "db checkouts are hard-linked fetches with the shallow file copied separately." ); bar.change_file("src/lib.rs", "// another change"); @@ -463,7 +463,7 @@ fn gitoxide_shallow_clone_followed_by_non_shallow_update() -> anyhow::Result<()> } #[cargo_test] -fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_fetch_maintains_shallowness( +fn gitoxide_fetch_registry_with_shallow_protocol_and_follow_up_fetch_maintains_shallowness( ) -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let p = project() @@ -493,7 +493,7 @@ fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_fetch_maintains_ .all()? .count(), 1, - "shallow clones always start at depth of 1 to minimize download size" + "shallow fetches always start at depth of 1 to minimize download size" ); assert!(repo.is_shallow()); @@ -535,9 +535,9 @@ fn gitoxide_clones_registry_with_shallow_protocol_and_follow_up_fetch_maintains_ Ok(()) } -/// If there is shallow *and* non-shallow clones, non-shallow will naturally be returned due to sort order. +/// If there is shallow *and* non-shallow fetches, non-shallow will naturally be returned due to sort order. #[cargo_test] -fn gitoxide_clones_registry_without_shallow_protocol_and_follow_up_fetch_uses_shallowness( +fn gitoxide_fetch_registry_without_shallow_protocol_and_follow_up_fetch_uses_shallowness( ) -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let p = project() @@ -587,7 +587,7 @@ fn gitoxide_clones_registry_without_shallow_protocol_and_follow_up_fetch_uses_sh .all()? .count(), 1, - "the follow up clones an entirely new index which is now shallow and which is in its own location" + "the follow up fetch an entirely new index which is now shallow and which is in its own location" ); assert!(shallow_repo.is_shallow()); @@ -630,14 +630,14 @@ fn gitoxide_clones_registry_without_shallow_protocol_and_follow_up_fetch_uses_sh #[cargo_test] fn gitoxide_git_dependencies_switch_from_branch_to_rev() -> anyhow::Result<()> { // db exists from previous build, then dependency changes to refer to revision that isn't - // available in the shallow clone. + // available in the shallow fetch. let (bar, bar_repo) = git::new_repo("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("src/lib.rs", "") }); - // this commit would not be available in a shallow clone. + // this commit would not be available in a shallow fetch. let first_commit_pre_change = bar_repo.head().unwrap().target().unwrap(); bar.change_file("src/lib.rs", "// change"); @@ -713,7 +713,7 @@ fn shallow_deps_work_with_revisions_and_branches_mixed_on_same_dependency() -> a .file("src/lib.rs", "") }); - // this commit would not be available in a shallow clone. + // this commit would not be available in a shallow fetch. let first_commit_pre_change = bar_repo.head().unwrap().target().unwrap(); bar.change_file("src/lib.rs", "// change"); @@ -765,8 +765,8 @@ fn shallow_deps_work_with_revisions_and_branches_mixed_on_same_dependency() -> a } #[cargo_test] -fn gitoxide_clones_registry_with_shallow_protocol_and_aborts_and_updates_again( -) -> anyhow::Result<()> { +fn gitoxide_fetch_registry_with_shallow_protocol_and_aborts_and_updates_again() -> anyhow::Result<()> +{ Package::new("bar", "1.0.0").publish(); let p = project() .file( @@ -795,11 +795,11 @@ fn gitoxide_clones_registry_with_shallow_protocol_and_aborts_and_updates_again( .all()? .count(), 1, - "shallow clones always start at depth of 1 to minimize download size" + "shallow fetches always start at depth of 1 to minimize download size" ); assert!(repo.is_shallow()); let shallow_lock = repo.shallow_file().with_extension("lock"); - // adding a lock file and deleting the original simulates a left-over clone that was aborted, leaving a lock file + // adding a lock file and deleting the original simulates a left-over fetch that was aborted, leaving a lock file // in place without ever having moved it to the right location. std::fs::write(&shallow_lock, &[])?; std::fs::remove_file(repo.shallow_file())?; @@ -819,7 +819,7 @@ fn gitoxide_clones_registry_with_shallow_protocol_and_aborts_and_updates_again( .all()? .count(), 1, - "it's a fresh shallow clone - otherwise it would have 2 commits if the previous shallow clone would still be present" + "it's a fresh shallow fetch - otherwise it would have 2 commits if the previous shallow fetch would still be present" ); Ok(()) From e0f8cc06da88353f79fa74898f0e8671bd8e0bb9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 18 Dec 2024 15:09:03 -0600 Subject: [PATCH 255/525] test(git): Group related shallow tests --- tests/testsuite/git_shallow.rs | 274 ++++++++++++++++----------------- 1 file changed, 137 insertions(+), 137 deletions(-) diff --git a/tests/testsuite/git_shallow.rs b/tests/testsuite/git_shallow.rs index 76425c19704..8a64e17cc80 100644 --- a/tests/testsuite/git_shallow.rs +++ b/tests/testsuite/git_shallow.rs @@ -128,6 +128,143 @@ fn fetch_two_revs_same_deps(backend: Backend, mode: RepoMode) { .run(); } +#[cargo_test] +fn shallow_deps_work_with_revisions_and_branches_mixed_on_same_dependency() -> anyhow::Result<()> { + let (bar, bar_repo) = git::new_repo("bar", |p| { + p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) + .file("src/lib.rs", "") + }); + + // this commit would not be available in a shallow fetch. + let first_commit_pre_change = bar_repo.head().unwrap().target().unwrap(); + + bar.change_file("src/lib.rs", "// change"); + git::add(&bar_repo); + git::commit(&bar_repo); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar-renamed = {{ package = "bar", git = "{}", rev = "{}" }} + bar = {{ git = "{}", branch = "master" }} + "#, + bar.url(), + first_commit_pre_change, + bar.url(), + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .arg("-Zgitoxide=fetch") + .arg("-Zgit=shallow-deps") + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) + .run(); + + let db_paths = glob::glob(paths::home().join(".cargo/git/db/bar-*").to_str().unwrap())? + .map(Result::unwrap) + .collect::>(); + assert_eq!( + db_paths.len(), + 1, + "only one db checkout source is used per dependency" + ); + let db_clone = gix::open_opts(&db_paths[0], gix::open::Options::isolated())?; + assert!( + db_clone.is_shallow(), + "the repo is shallow while having all data it needs" + ); + + Ok(()) +} + +#[cargo_test] +fn gitoxide_git_dependencies_switch_from_branch_to_rev() -> anyhow::Result<()> { + // db exists from previous build, then dependency changes to refer to revision that isn't + // available in the shallow fetch. + + let (bar, bar_repo) = git::new_repo("bar", |p| { + p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) + .file("src/lib.rs", "") + }); + + // this commit would not be available in a shallow fetch. + let first_commit_pre_change = bar_repo.head().unwrap().target().unwrap(); + + bar.change_file("src/lib.rs", "// change"); + git::add(&bar_repo); + git::commit(&bar_repo); + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {{ git = "{}", branch = "master" }} + "#, + bar.url(), + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .arg("-Zgitoxide=fetch") + .arg("-Zgit=shallow-deps") + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) + .run(); + + let db_clone = gix::open_opts( + find_bar_db(RepoMode::Shallow), + gix::open::Options::isolated(), + )?; + assert!(db_clone.is_shallow()); + + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = {{ git = "{}", rev = "{}" }} + "#, + bar.url(), + first_commit_pre_change + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .arg("-Zgitoxide=fetch") + .arg("-Zgit=shallow-deps") + .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) + .run(); + + assert!( + db_clone.is_shallow(), + "we maintain shallowness and never unshallow" + ); + + Ok(()) +} + #[cargo_test] fn gitoxide_fetch_registry_with_shallow_protocol_and_follow_up_with_git2_fetch( ) -> anyhow::Result<()> { @@ -627,143 +764,6 @@ fn gitoxide_fetch_registry_without_shallow_protocol_and_follow_up_fetch_uses_sha Ok(()) } -#[cargo_test] -fn gitoxide_git_dependencies_switch_from_branch_to_rev() -> anyhow::Result<()> { - // db exists from previous build, then dependency changes to refer to revision that isn't - // available in the shallow fetch. - - let (bar, bar_repo) = git::new_repo("bar", |p| { - p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) - .file("src/lib.rs", "") - }); - - // this commit would not be available in a shallow fetch. - let first_commit_pre_change = bar_repo.head().unwrap().target().unwrap(); - - bar.change_file("src/lib.rs", "// change"); - git::add(&bar_repo); - git::commit(&bar_repo); - let p = project() - .file( - "Cargo.toml", - &format!( - r#" - [package] - name = "foo" - version = "0.1.0" - - [dependencies] - bar = {{ git = "{}", branch = "master" }} - "#, - bar.url(), - ), - ) - .file("src/lib.rs", "") - .build(); - - p.cargo("check") - .arg("-Zgitoxide=fetch") - .arg("-Zgit=shallow-deps") - .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) - .run(); - - let db_clone = gix::open_opts( - find_bar_db(RepoMode::Shallow), - gix::open::Options::isolated(), - )?; - assert!(db_clone.is_shallow()); - - let p = project() - .file( - "Cargo.toml", - &format!( - r#" - [package] - name = "foo" - version = "0.1.0" - - [dependencies] - bar = {{ git = "{}", rev = "{}" }} - "#, - bar.url(), - first_commit_pre_change - ), - ) - .file("src/lib.rs", "") - .build(); - - p.cargo("check") - .arg("-Zgitoxide=fetch") - .arg("-Zgit=shallow-deps") - .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) - .run(); - - assert!( - db_clone.is_shallow(), - "we maintain shallowness and never unshallow" - ); - - Ok(()) -} - -#[cargo_test] -fn shallow_deps_work_with_revisions_and_branches_mixed_on_same_dependency() -> anyhow::Result<()> { - let (bar, bar_repo) = git::new_repo("bar", |p| { - p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) - .file("src/lib.rs", "") - }); - - // this commit would not be available in a shallow fetch. - let first_commit_pre_change = bar_repo.head().unwrap().target().unwrap(); - - bar.change_file("src/lib.rs", "// change"); - git::add(&bar_repo); - git::commit(&bar_repo); - - let p = project() - .file( - "Cargo.toml", - &format!( - r#" - [package] - name = "foo" - version = "0.1.0" - - [dependencies] - bar-renamed = {{ package = "bar", git = "{}", rev = "{}" }} - bar = {{ git = "{}", branch = "master" }} - "#, - bar.url(), - first_commit_pre_change, - bar.url(), - ), - ) - .file("src/lib.rs", "") - .build(); - - p.cargo("check") - .arg("-Zgitoxide=fetch") - .arg("-Zgit=shallow-deps") - .masquerade_as_nightly_cargo(&["gitoxide=fetch", "git=shallow-deps"]) - .run(); - - let db_paths = glob::glob(paths::home().join(".cargo/git/db/bar-*").to_str().unwrap())? - .map(Result::unwrap) - .collect::>(); - assert_eq!( - db_paths.len(), - 1, - "only one db checkout source is used per dependency" - ); - let db_clone = gix::open_opts(&db_paths[0], gix::open::Options::isolated())?; - assert!( - db_clone.is_shallow(), - "the repo is shallow while having all data it needs" - ); - - Ok(()) -} - #[cargo_test] fn gitoxide_fetch_registry_with_shallow_protocol_and_aborts_and_updates_again() -> anyhow::Result<()> { From 24388070488952b39e8d3c7d9a707b0d25e8ac7f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 18 Dec 2024 15:20:03 -0600 Subject: [PATCH 256/525] test(git): Be consistent on shallow test names --- tests/testsuite/git_shallow.rs | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/tests/testsuite/git_shallow.rs b/tests/testsuite/git_shallow.rs index 8a64e17cc80..86291714cd9 100644 --- a/tests/testsuite/git_shallow.rs +++ b/tests/testsuite/git_shallow.rs @@ -33,16 +33,16 @@ impl RepoMode { } #[cargo_test] -fn gitoxide_fetch_shallow_two_revs_same_deps() { - fetch_two_revs_same_deps(Backend::Gitoxide, RepoMode::Shallow) +fn gitoxide_fetch_shallow_dep_two_revs() { + fetch_dep_two_revs(Backend::Gitoxide, RepoMode::Shallow) } #[cargo_test] -fn git2_fetch_complete_two_revs_same_deps() { - fetch_two_revs_same_deps(Backend::Git2, RepoMode::Complete) +fn git2_fetch_complete_dep_two_revs() { + fetch_dep_two_revs(Backend::Git2, RepoMode::Complete) } -fn fetch_two_revs_same_deps(backend: Backend, mode: RepoMode) { +fn fetch_dep_two_revs(backend: Backend, mode: RepoMode) { let bar = git::new("meta-dep", |project| { project .file("Cargo.toml", &basic_manifest("bar", "0.0.0")) @@ -129,7 +129,7 @@ fn fetch_two_revs_same_deps(backend: Backend, mode: RepoMode) { } #[cargo_test] -fn shallow_deps_work_with_revisions_and_branches_mixed_on_same_dependency() -> anyhow::Result<()> { +fn gitoxide_fetch_shallow_dep_branch_and_rev() -> anyhow::Result<()> { let (bar, bar_repo) = git::new_repo("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) .file("src/lib.rs", "") @@ -187,7 +187,7 @@ fn shallow_deps_work_with_revisions_and_branches_mixed_on_same_dependency() -> a } #[cargo_test] -fn gitoxide_git_dependencies_switch_from_branch_to_rev() -> anyhow::Result<()> { +fn gitoxide_fetch_shallow_dep_branch_to_rev() -> anyhow::Result<()> { // db exists from previous build, then dependency changes to refer to revision that isn't // available in the shallow fetch. @@ -266,8 +266,7 @@ fn gitoxide_git_dependencies_switch_from_branch_to_rev() -> anyhow::Result<()> { } #[cargo_test] -fn gitoxide_fetch_registry_with_shallow_protocol_and_follow_up_with_git2_fetch( -) -> anyhow::Result<()> { +fn gitoxide_fetch_shallow_index_then_git2_fetch_complete() -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let p = project() .file( @@ -323,8 +322,7 @@ fn gitoxide_fetch_registry_with_shallow_protocol_and_follow_up_with_git2_fetch( } #[cargo_test] -fn gitoxide_fetch_git_dependency_with_shallow_protocol_and_git2_is_used_for_followup_fetches( -) -> anyhow::Result<()> { +fn gitoxide_fetch_shallow_dep_then_git2_fetch_complete() -> anyhow::Result<()> { // Example where an old lockfile with an explicit branch="master" in Cargo.toml. Package::new("bar", "1.0.0").publish(); let (bar, bar_repo) = git::new_repo("bar", |p| { @@ -461,7 +459,7 @@ fn gitoxide_fetch_git_dependency_with_shallow_protocol_and_git2_is_used_for_foll } #[cargo_test] -fn gitoxide_shallow_fetch_followed_by_non_shallow_update() -> anyhow::Result<()> { +fn gitoxide_fetch_shallow_dep_then_gitoxide_fetch_complete() -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let (bar, bar_repo) = git::new_repo("bar", |p| { p.file("Cargo.toml", &basic_manifest("bar", "1.0.0")) @@ -600,8 +598,7 @@ fn gitoxide_shallow_fetch_followed_by_non_shallow_update() -> anyhow::Result<()> } #[cargo_test] -fn gitoxide_fetch_registry_with_shallow_protocol_and_follow_up_fetch_maintains_shallowness( -) -> anyhow::Result<()> { +fn gitoxide_fetch_shallow_index_then_preserve_shallow() -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let p = project() .file( @@ -674,8 +671,7 @@ fn gitoxide_fetch_registry_with_shallow_protocol_and_follow_up_fetch_maintains_s /// If there is shallow *and* non-shallow fetches, non-shallow will naturally be returned due to sort order. #[cargo_test] -fn gitoxide_fetch_registry_without_shallow_protocol_and_follow_up_fetch_uses_shallowness( -) -> anyhow::Result<()> { +fn gitoxide_fetch_complete_index_then_shallow() -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let p = project() .file( @@ -765,8 +761,7 @@ fn gitoxide_fetch_registry_without_shallow_protocol_and_follow_up_fetch_uses_sha } #[cargo_test] -fn gitoxide_fetch_registry_with_shallow_protocol_and_aborts_and_updates_again() -> anyhow::Result<()> -{ +fn gitoxide_fetch_shallow_index_then_abort_and_update() -> anyhow::Result<()> { Package::new("bar", "1.0.0").publish(); let p = project() .file( From 252ad98bc9cd03def882372afc7e81bf8af88aca Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Wed, 18 Dec 2024 23:11:58 -0500 Subject: [PATCH 257/525] refactor(cargo-package): rename unclear argument `p` --- src/cargo/ops/cargo_package.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index 30445b8643f..570b96a782d 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -780,7 +780,7 @@ fn check_repo_state( return Ok(None); fn git( - p: &Package, + pkg: &Package, src_files: &[PathBuf], repo: &git2::Repository, opts: &PackageOpts<'_>, @@ -803,7 +803,7 @@ fn check_repo_state( .iter() .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path))) .map(|path| { - path.strip_prefix(p.root()) + path.strip_prefix(pkg.root()) .unwrap_or(path) .display() .to_string() From 6d6b6089fc09006d95d9646916ebc6fdf91ea315 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Wed, 18 Dec 2024 23:11:05 -0500 Subject: [PATCH 258/525] fix(cargo-package): add more traces --- src/cargo/ops/cargo_package.rs | 4 ++++ src/cargo/sources/path.rs | 1 + 2 files changed, 5 insertions(+) diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index 570b96a782d..7ccc6fc1ea1 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -94,6 +94,7 @@ struct GitVcsInfo { } // Builds a tarball and places it in the output directory. +#[tracing::instrument(skip_all)] fn create_package( ws: &Workspace<'_>, pkg: &Package, @@ -372,6 +373,7 @@ fn local_deps(packages: impl Iterator) -> LocalDependenc } /// Performs pre-archiving checks and builds a list of files to archive. +#[tracing::instrument(skip_all)] fn prepare_archive( ws: &Workspace<'_>, pkg: &Package, @@ -400,6 +402,7 @@ fn prepare_archive( } /// Builds list of files to archive. +#[tracing::instrument(skip_all)] fn build_ar_list( ws: &Workspace<'_>, pkg: &Package, @@ -730,6 +733,7 @@ fn check_metadata(pkg: &Package, gctx: &GlobalContext) -> CargoResult<()> { /// has not been passed, then `bail!` with an informative message. Otherwise /// return the sha1 hash of the current *HEAD* commit, or `None` if no repo is /// found. +#[tracing::instrument(skip_all)] fn check_repo_state( p: &Package, src_files: &[PathBuf], diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index 1d4a533a3c4..7765906977f 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -93,6 +93,7 @@ impl<'gctx> PathSource<'gctx> { /// are relevant for building this package, but it also contains logic to /// use other methods like `.gitignore`, `package.include`, or /// `package.exclude` to filter the list of files. + #[tracing::instrument(skip_all)] pub fn list_files(&self, pkg: &Package) -> CargoResult> { list_files(pkg, self.gctx) } From 3c75c1532e6652058c7ee51cf21dd0bf4f0d2d4b Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Wed, 18 Dec 2024 18:43:42 -0500 Subject: [PATCH 259/525] refactor(cargo-package): let-else to flatten code This also adds some more debug logs --- src/cargo/ops/cargo_package.rs | 95 ++++++++++++++++++----------- tests/testsuite/publish_lockfile.rs | 2 +- 2 files changed, 59 insertions(+), 38 deletions(-) diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index 30445b8643f..866b5c5ac6d 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -736,48 +736,69 @@ fn check_repo_state( gctx: &GlobalContext, opts: &PackageOpts<'_>, ) -> CargoResult> { - if let Ok(repo) = git2::Repository::discover(p.root()) { - if let Some(workdir) = repo.workdir() { - debug!("found a git repo at {:?}", workdir); - let path = p.manifest_path(); - let path = - paths::strip_prefix_canonical(path, workdir).unwrap_or_else(|_| path.to_path_buf()); - if let Ok(status) = repo.status_file(&path) { - if (status & git2::Status::IGNORED).is_empty() { - debug!( - "found (git) Cargo.toml at {:?} in workdir {:?}", - path, workdir - ); - let path_in_vcs = path - .parent() - .and_then(|p| p.to_str()) - .unwrap_or("") - .replace("\\", "/"); - let Some(git) = git(p, src_files, &repo, &opts)? else { - // If the git repo lacks essensial field like `sha1`, and since this field exists from the beginning, - // then don't generate the corresponding file in order to maintain consistency with past behavior. - return Ok(None); - }; - return Ok(Some(VcsInfo { git, path_in_vcs })); - } - } - gctx.shell().verbose(|shell| { - shell.warn(format!( - "no (git) Cargo.toml found at `{}` in workdir `{}`", - path.display(), - workdir.display() - )) - })?; - } - } else { + let Ok(repo) = git2::Repository::discover(p.root()) else { gctx.shell().verbose(|shell| { shell.warn(format!("no (git) VCS found for `{}`", p.root().display())) })?; + // No Git repo found. Have to assume it is clean. + return Ok(None); + }; + + let Some(workdir) = repo.workdir() else { + debug!( + "no (git) workdir found for repo at `{}`", + repo.path().display() + ); + // No git workdir. Have to assume it is clean. + return Ok(None); + }; + + debug!("found a git repo at `{}`", workdir.display()); + let path = p.manifest_path(); + let path = paths::strip_prefix_canonical(path, workdir).unwrap_or_else(|_| path.to_path_buf()); + let Ok(status) = repo.status_file(&path) else { + gctx.shell().verbose(|shell| { + shell.warn(format!( + "no (git) Cargo.toml found at `{}` in workdir `{}`", + path.display(), + workdir.display() + )) + })?; + // No checked-in `Cargo.toml` found. This package may be irrelevant. + // Have to assume it is clean. + return Ok(None); + }; + + if !(status & git2::Status::IGNORED).is_empty() { + gctx.shell().verbose(|shell| { + shell.warn(format!( + "found (git) Cargo.toml ignored at `{}` in workdir `{}`", + path.display(), + workdir.display() + )) + })?; + // An ignored `Cargo.toml` found. This package may be irrelevant. + // Have to assume it is clean. + return Ok(None); } - // No VCS with a checked in `Cargo.toml` found, so we don't know if the - // directory is dirty or not, thus we have to assume that it's clean. - return Ok(None); + debug!( + "found (git) Cargo.toml at `{}` in workdir `{}`", + path.display(), + workdir.display(), + ); + let path_in_vcs = path + .parent() + .and_then(|p| p.to_str()) + .unwrap_or("") + .replace("\\", "/"); + let Some(git) = git(p, src_files, &repo, &opts)? else { + // If the git repo lacks essensial field like `sha1`, and since this field exists from the beginning, + // then don't generate the corresponding file in order to maintain consistency with past behavior. + return Ok(None); + }; + + return Ok(Some(VcsInfo { git, path_in_vcs })); fn git( p: &Package, diff --git a/tests/testsuite/publish_lockfile.rs b/tests/testsuite/publish_lockfile.rs index b204b7cf3bb..51f376ef756 100644 --- a/tests/testsuite/publish_lockfile.rs +++ b/tests/testsuite/publish_lockfile.rs @@ -241,7 +241,7 @@ fn note_resolve_changes() { [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/main.rs [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) -[WARNING] no (git) Cargo.toml found at `[..]/foo/Cargo.toml` in workdir `[..]` +[WARNING] found (git) Cargo.toml ignored at `[..]/foo/Cargo.toml` in workdir `[..]` "#]].unordered()) .run(); From 5720eb77dbd6e650c36d7691c962e273f893a0ca Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 19 Dec 2024 20:41:23 +0100 Subject: [PATCH 260/525] Add the `test` cfg as a well known cfg before of compiler change --- src/cargo/core/compiler/mod.rs | 13 ++++++++----- tests/testsuite/check_cfg.rs | 30 +++++++++++++++--------------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 93859e914b9..277039685d6 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -1391,14 +1391,17 @@ fn check_cfg_args(unit: &Unit) -> Vec { } arg_feature.push("))"); - // We also include the `docsrs` cfg from the docs.rs service. We include it here - // (in Cargo) instead of rustc, since there is a much closer relationship between - // Cargo and docs.rs than rustc and docs.rs. In particular, all users of docs.rs use - // Cargo, but not all users of rustc (like Rust-for-Linux) use docs.rs. + // In addition to the package features, we also include the `test` cfg (since + // compiler-team#785, as to be able to someday apply yt conditionaly), as well + // the `docsrs` cfg from the docs.rs service. + // + // We include `docsrs` here (in Cargo) instead of rustc, since there is a much closer + // relationship between Cargo and docs.rs than rustc and docs.rs. In particular, all + // users of docs.rs use Cargo, but not all users of rustc (like Rust-for-Linux) use docs.rs. vec![ OsString::from("--check-cfg"), - OsString::from("cfg(docsrs)"), + OsString::from("cfg(docsrs,test)"), OsString::from("--check-cfg"), arg_feature, ] diff --git a/tests/testsuite/check_cfg.rs b/tests/testsuite/check_cfg.rs index dc34b13f0c1..2e03254337a 100644 --- a/tests/testsuite/check_cfg.rs +++ b/tests/testsuite/check_cfg.rs @@ -51,7 +51,7 @@ fn features() { p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b")) - .with_stderr_contains(x!("rustc" => "cfg" of "docsrs")) + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } @@ -81,7 +81,7 @@ fn features_with_deps() { p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b")) - .with_stderr_contains(x!("rustc" => "cfg" of "docsrs")) + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } @@ -112,7 +112,7 @@ fn features_with_opt_deps() { p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "bar" "default" "f_a" "f_b")) - .with_stderr_contains(x!("rustc" => "cfg" of "docsrs")) + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } @@ -142,7 +142,7 @@ fn features_with_namespaced_features() { p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b")) - .with_stderr_contains(x!("rustc" => "cfg" of "docsrs")) + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } @@ -232,7 +232,7 @@ fn well_known_names_values() { p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with)) - .with_stderr_contains(x!("rustc" => "cfg" of "docsrs")) + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } @@ -257,7 +257,7 @@ fn features_test() { p.cargo("test -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b")) - .with_stderr_contains(x!("rustc" => "cfg" of "docsrs")) + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } @@ -284,8 +284,8 @@ fn features_doctest() { p.cargo("test -v --doc") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "default" "f_a" "f_b")) .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with "default" "f_a" "f_b")) - .with_stderr_contains(x!("rustc" => "cfg" of "docsrs")) - .with_stderr_contains(x!("rustdoc" => "cfg" of "docsrs")) + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) + .with_stderr_contains(x!("rustdoc" => "cfg" of "docsrs,test")) .run(); } @@ -298,7 +298,7 @@ fn well_known_names_values_test() { p.cargo("test -v") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with)) - .with_stderr_contains(x!("rustc" => "cfg" of "docsrs")) + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } @@ -312,8 +312,8 @@ fn well_known_names_values_doctest() { p.cargo("test -v --doc") .with_stderr_contains(x!("rustc" => "cfg" of "feature" with)) .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with)) - .with_stderr_contains(x!("rustc" => "cfg" of "docsrs")) - .with_stderr_contains(x!("rustdoc" => "cfg" of "docsrs")) + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) + .with_stderr_contains(x!("rustdoc" => "cfg" of "docsrs,test")) .run(); } @@ -339,7 +339,7 @@ fn features_doc() { p.cargo("doc -v") .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with "default" "f_a" "f_b")) - .with_stderr_contains(x!("rustdoc" => "cfg" of "docsrs")) + .with_stderr_contains(x!("rustdoc" => "cfg" of "docsrs,test")) .run(); } @@ -366,7 +366,7 @@ fn build_script_feedback() { p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "foo")) - .with_stderr_contains(x!("rustc" => "cfg" of "docsrs")) + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } @@ -442,7 +442,7 @@ fn build_script_override() { p.cargo("check -v") .with_stderr_contains(x!("rustc" => "cfg" of "foo")) .with_stderr_contains(x!("rustc" => "cfg" of "feature" with)) - .with_stderr_contains(x!("rustc" => "cfg" of "docsrs")) + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) .run(); } @@ -877,7 +877,7 @@ fn config_features_and_build_script() { .with_stderr_contains(x!("rustc" => "cfg" of "foo")) // from build.rs .with_stderr_contains(x!("rustc" => "cfg" of "bar")) // from config .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "json" "serde")) // features - .with_stderr_contains(x!("rustc" => "cfg" of "docsrs")) // Cargo well known + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) // Cargo well known .run(); } From cc3b6cb5c50c5811468177d8ea87060ae1bacacc Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Thu, 19 Dec 2024 12:25:14 -0500 Subject: [PATCH 261/525] fix(package): sort dirty file report To make error message more deterministic --- src/cargo/ops/cargo_package.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index bc619c1b86a..2c7b6eb44cf 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -824,7 +824,7 @@ fn check_repo_state( // Find the intersection of dirty in git, and the src_files that would // be packaged. This is a lazy n^2 check, but seems fine with // thousands of files. - let dirty_src_files: Vec = src_files + let mut dirty_src_files: Vec<_> = src_files .iter() .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path))) .map(|path| { @@ -847,6 +847,7 @@ fn check_repo_state( dirty, })) } else { + dirty_src_files.sort_unstable(); anyhow::bail!( "{} files in the working directory contain changes that were \ not yet committed into git:\n\n{}\n\n\ From ecc8e37f401d87d62ce8b1d688ac8b9841aeed6d Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Thu, 19 Dec 2024 10:40:46 -0500 Subject: [PATCH 262/525] test(package): track vcs status on workspace member granularity --- tests/testsuite/package.rs | 138 +++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index 5a27cc34920..d505ae3d6dc 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -1156,6 +1156,144 @@ src/lib.rs .run(); } +#[cargo_test] +fn vcs_status_check_for_each_workspace_member() { + // Cargo checks VCS status separately for each workspace member. + // This ensure one file changed in a package won't affect the other. + // Since the dirty bit in .cargo_vcs_info.json is just for advisory purpose, + // We may change the meaning of it in the future. + let (p, repo) = git::new_repo("foo", |p| { + p.file( + "Cargo.toml", + r#" + [workspace] + members = ["isengard", "mordor"] + "#, + ) + .file("hobbit", "...") + .file( + "isengard/Cargo.toml", + r#" + [package] + name = "isengard" + edition = "2015" + homepage = "saruman" + description = "saruman" + license = "MIT" + "#, + ) + .file("isengard/src/lib.rs", "") + .file( + "mordor/Cargo.toml", + r#" + [package] + name = "mordor" + edition = "2015" + homepage = "sauron" + description = "sauron" + license = "MIT" + "#, + ) + .file("mordor/src/lib.rs", "") + }); + git::commit(&repo); + + p.change_file( + "Cargo.toml", + r#" + [workspace] + members = ["isengard", "mordor"] + [workspace.package] + edition = "2021" + "#, + ); + // Dirty file outside won't affect packaging. + p.change_file("hobbit", "changed!"); + p.change_file("mordor/src/lib.rs", "changed!"); + p.change_file("mordor/src/main.rs", "fn main() {}"); + + // Ensure dirty files be reported only for one affected package. + p.cargo("package --workspace --no-verify") + .with_status(101) + .with_stderr_data(str![[r#" +[PACKAGING] isengard v0.0.0 ([ROOT]/foo/isengard) +[PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[ERROR] 2 files in the working directory contain changes that were not yet committed into git: + +src/lib.rs +src/main.rs + +to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag + +"#]]) + .run(); + + // Ensure only dirty package be recorded as dirty. + p.cargo("package --workspace --no-verify --allow-dirty") + .with_stderr_data(str![[r#" +[PACKAGING] isengard v0.0.0 ([ROOT]/foo/isengard) +[PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[PACKAGING] mordor v0.0.0 ([ROOT]/foo/mordor) +[PACKAGED] 6 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) + +"#]]) + .run(); + + let f = File::open(&p.root().join("target/package/isengard-0.0.0.crate")).unwrap(); + validate_crate_contents( + f, + "isengard-0.0.0.crate", + &[ + ".cargo_vcs_info.json", + "Cargo.toml", + "Cargo.toml.orig", + "src/lib.rs", + "Cargo.lock", + ], + [( + ".cargo_vcs_info.json", + // No change within `isengard/`, so not dirty at all. + str![[r#" +{ + "git": { + "sha1": "[..]" + }, + "path_in_vcs": "isengard" +} +"#]] + .is_json(), + )], + ); + + let f = File::open(&p.root().join("target/package/mordor-0.0.0.crate")).unwrap(); + validate_crate_contents( + f, + "mordor-0.0.0.crate", + &[ + ".cargo_vcs_info.json", + "Cargo.toml", + "Cargo.toml.orig", + "src/lib.rs", + "src/main.rs", + "Cargo.lock", + ], + [( + ".cargo_vcs_info.json", + // Dirty bit is recorded. + str![[r#" +{ + "git": { + "dirty": true, + "sha1": "[..]" + }, + "path_in_vcs": "mordor" +} +"#]] + .is_json(), + )], + ); +} + #[cargo_test] fn issue_13695_allow_dirty_vcs_info() { let p = project() From 982eb2de3a445cb7c2c5ca8e4b24f55b875df216 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Thu, 19 Dec 2024 11:13:00 -0500 Subject: [PATCH 263/525] fix(package): show dirty filepaths relative to git workdir --- src/cargo/ops/cargo_package.rs | 6 +++--- tests/testsuite/package.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index 2c7b6eb44cf..b12a72db579 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -796,7 +796,7 @@ fn check_repo_state( .and_then(|p| p.to_str()) .unwrap_or("") .replace("\\", "/"); - let Some(git) = git(p, src_files, &repo, &opts)? else { + let Some(git) = git(src_files, &repo, &opts)? else { // If the git repo lacks essensial field like `sha1`, and since this field exists from the beginning, // then don't generate the corresponding file in order to maintain consistency with past behavior. return Ok(None); @@ -805,7 +805,6 @@ fn check_repo_state( return Ok(Some(VcsInfo { git, path_in_vcs })); fn git( - pkg: &Package, src_files: &[PathBuf], repo: &git2::Repository, opts: &PackageOpts<'_>, @@ -824,11 +823,12 @@ fn check_repo_state( // Find the intersection of dirty in git, and the src_files that would // be packaged. This is a lazy n^2 check, but seems fine with // thousands of files. + let workdir = repo.workdir().unwrap(); let mut dirty_src_files: Vec<_> = src_files .iter() .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path))) .map(|path| { - path.strip_prefix(pkg.root()) + path.strip_prefix(workdir) .unwrap_or(path) .display() .to_string() diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index d505ae3d6dc..75d21ffb2ce 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -1220,8 +1220,8 @@ fn vcs_status_check_for_each_workspace_member() { [PACKAGED] 5 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [ERROR] 2 files in the working directory contain changes that were not yet committed into git: -src/lib.rs -src/main.rs +mordor/src/lib.rs +mordor/src/main.rs to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag From 871e281cff34d9dba177c6e59077a8886f454b29 Mon Sep 17 00:00:00 2001 From: Bilal Khan Date: Fri, 20 Dec 2024 03:46:16 +0500 Subject: [PATCH 264/525] fixed the error message for a user to open the crate --- src/cargo/ops/cargo_doc.rs | 9 +++++---- tests/testsuite/doc.rs | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/cargo/ops/cargo_doc.rs b/src/cargo/ops/cargo_doc.rs index 5f7e9ad131a..efca9861d31 100644 --- a/src/cargo/ops/cargo_doc.rs +++ b/src/cargo/ops/cargo_doc.rs @@ -56,10 +56,11 @@ pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> { let compilation = ops::compile(ws, &options.compile_opts)?; if options.open_result { - let name = &compilation - .root_crate_names - .get(0) - .ok_or_else(|| anyhow::anyhow!("no crates with documentation"))?; + let name = &compilation.root_crate_names.get(0).ok_or_else(|| { + anyhow::anyhow!( + "cannot open specified crate's documentation: no documentation generated" + ) + })?; let kind = options.compile_opts.build_config.single_requested_kind()?; let path = path_by_output_format(&compilation, &kind, &name, &options.output_format); diff --git a/tests/testsuite/doc.rs b/tests/testsuite/doc.rs index ede038b0e7e..fcaa553f027 100644 --- a/tests/testsuite/doc.rs +++ b/tests/testsuite/doc.rs @@ -1501,7 +1501,7 @@ fn open_no_doc_crate() { .with_status(101) .with_stderr_data(str![[r#" [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s -[ERROR] no crates with documentation +[ERROR] cannot open specified crate's documentation: no documentation generated "#]]) .run(); From 9e2b373acb59d3ad3ca7d53c5443fd7f8c079f8a Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Thu, 19 Dec 2024 20:18:05 -0500 Subject: [PATCH 265/525] test(package): relative path to cwd for dirtiness report --- tests/testsuite/package.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index 75d21ffb2ce..31c834e401c 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -1099,6 +1099,20 @@ Cargo.toml to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag +"#]]) + .run(); + + // cd to `src` and cargo report relative paths. + p.cargo("package") + .cwd(p.root().join("src")) + .with_status(101) + .with_stderr_data(str![[r#" +[ERROR] 1 files in the working directory contain changes that were not yet committed into git: + +Cargo.toml + +to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag + "#]]) .run(); } From d325acea1d51d7ababf580500ef15228d46b2df1 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Thu, 19 Dec 2024 20:22:18 -0500 Subject: [PATCH 266/525] fix(package): use relpath to cwd for vcs dirtiness report Address https://github.com/rust-lang/cargo/pull/14968#issuecomment-2555901072 --- src/cargo/ops/cargo_package.rs | 8 +++++--- tests/testsuite/package.rs | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index b12a72db579..b54a16d2c9b 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -796,7 +796,7 @@ fn check_repo_state( .and_then(|p| p.to_str()) .unwrap_or("") .replace("\\", "/"); - let Some(git) = git(src_files, &repo, &opts)? else { + let Some(git) = git(gctx, src_files, &repo, &opts)? else { // If the git repo lacks essensial field like `sha1`, and since this field exists from the beginning, // then don't generate the corresponding file in order to maintain consistency with past behavior. return Ok(None); @@ -805,6 +805,7 @@ fn check_repo_state( return Ok(Some(VcsInfo { git, path_in_vcs })); fn git( + gctx: &GlobalContext, src_files: &[PathBuf], repo: &git2::Repository, opts: &PackageOpts<'_>, @@ -823,12 +824,13 @@ fn check_repo_state( // Find the intersection of dirty in git, and the src_files that would // be packaged. This is a lazy n^2 check, but seems fine with // thousands of files. - let workdir = repo.workdir().unwrap(); + let cwd = gctx.cwd(); let mut dirty_src_files: Vec<_> = src_files .iter() .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path))) .map(|path| { - path.strip_prefix(workdir) + pathdiff::diff_paths(path, cwd) + .as_ref() .unwrap_or(path) .display() .to_string() diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index 31c834e401c..1740de4ac77 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -1109,7 +1109,7 @@ to proceed despite this and include the uncommitted changes, pass the `--allow-d .with_stderr_data(str![[r#" [ERROR] 1 files in the working directory contain changes that were not yet committed into git: -Cargo.toml +../Cargo.toml to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag From 90aaa059a2bce4f0f1fa576cd496a05d37a5e9e8 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Fri, 20 Dec 2024 06:40:29 -0800 Subject: [PATCH 267/525] Enable triagebot merge conflict notifications --- triagebot.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/triagebot.toml b/triagebot.toml index 511e43b76fa..35256be5866 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -28,6 +28,11 @@ label = "O-windows" [transfer] +[merge-conflicts] +remove = [] +add = ["S-waiting-on-author"] +unless = ["S-blocked", "S-waiting-on-review"] + [autolabel."S-waiting-on-review"] new_pr = true From c8a9119b843c354bc1f471fd868e97bfd24d42bc Mon Sep 17 00:00:00 2001 From: Jacob Finkelman Date: Fri, 20 Dec 2024 21:37:33 +0000 Subject: [PATCH 268/525] make stable_hash not depend on hash --- src/cargo/core/source_id.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/cargo/core/source_id.rs b/src/cargo/core/source_id.rs index 501c88cba72..b85329f6f74 100644 --- a/src/cargo/core/source_id.rs +++ b/src/cargo/core/source_id.rs @@ -532,7 +532,10 @@ impl SourceId { url == CRATES_IO_INDEX || url == CRATES_IO_HTTP_INDEX || is_overridden_crates_io_url(url) } - /// Hashes `self`. + /// Hashes `self` to be used in the name of some Cargo folders, so shouldn't vary. + /// + /// For git and url, `as_str` gives the serialisation of a url (which has a spec) and so + /// insulates against possible changes in how the url crate does hashing. /// /// For paths, remove the workspace prefix so the same source will give the /// same hash in different locations, helping reproducible builds. @@ -550,7 +553,11 @@ impl SourceId { return; } } - self.hash(into) + self.inner.kind.hash(into); + match self.inner.kind { + SourceKind::Git(_) => (&self).inner.canonical_url.hash(into), + _ => (&self).inner.url.as_str().hash(into), + } } pub fn full_eq(self, other: SourceId) -> bool { @@ -665,9 +672,6 @@ impl fmt::Display for SourceId { } } -/// The hash of `SourceId` is used in the name of some Cargo folders, so shouldn't -/// vary. `as_str` gives the serialisation of a url (which has a spec) and so -/// insulates against possible changes in how the url crate does hashing. impl Hash for SourceId { fn hash(&self, into: &mut S) { self.inner.kind.hash(into); From 9c19032ca753506da645302bdb52c76e8c218909 Mon Sep 17 00:00:00 2001 From: Jacob Finkelman Date: Fri, 20 Dec 2024 21:39:20 +0000 Subject: [PATCH 269/525] simplify SourceID Hash --- src/cargo/core/source_id.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/cargo/core/source_id.rs b/src/cargo/core/source_id.rs index b85329f6f74..876d34281e9 100644 --- a/src/cargo/core/source_id.rs +++ b/src/cargo/core/source_id.rs @@ -675,10 +675,7 @@ impl fmt::Display for SourceId { impl Hash for SourceId { fn hash(&self, into: &mut S) { self.inner.kind.hash(into); - match self.inner.kind { - SourceKind::Git(_) => self.inner.canonical_url.hash(into), - _ => self.inner.url.as_str().hash(into), - } + self.inner.canonical_url.hash(into); } } From 3a180448082290936a069fef52216934462b1ec1 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 22 Dec 2024 18:23:52 +0100 Subject: [PATCH 270/525] upgrade `gix` to the latest release 0.69. The main benefit is that it won't facilitate hangs due to attempts to read from untrackable directory entries, like names pipes or sockets. Related to https://github.com/GitoxideLabs/gitoxide/pull/1629 --- Cargo.lock | 99 +++++++++++++++++++++------------- Cargo.toml | 2 +- src/cargo/sources/git/oxide.rs | 2 +- src/cargo/sources/git/utils.rs | 4 +- 4 files changed, 66 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1004313ef91..ddb1eaee5b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1161,9 +1161,9 @@ dependencies = [ [[package]] name = "gix" -version = "0.68.0" +version = "0.69.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b04c66359b5e17f92395abc433861df0edf48f39f3f590818d1d7217327dd6a1" +checksum = "8d0eebdaecdcf405d5433a36f85e4f058cf4de48ee2604388be0dbccbaad353e" dependencies = [ "gix-actor", "gix-attributes", @@ -1197,6 +1197,7 @@ dependencies = [ "gix-revision", "gix-revwalk", "gix-sec", + "gix-shallow", "gix-submodule", "gix-tempfile", "gix-trace", @@ -1263,9 +1264,9 @@ dependencies = [ [[package]] name = "gix-command" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d7d6b8f3a64453fd7e8191eb80b351eb7ac0839b40a1237cd2c137d5079fe53" +checksum = "9405c0a56e17f8365a46870cd2c7db71323ecc8bda04b50cb746ea37bd091e90" dependencies = [ "bstr", "gix-path", @@ -1323,9 +1324,9 @@ dependencies = [ [[package]] name = "gix-credentials" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2be87bb8685fc7e6e7032ef71c45068ffff609724a0c897b8047fde10db6ae71" +checksum = "82a50c56b785c29a151ab4ccf74a83fe4e21d2feda0d30549504b4baed353e0a" dependencies = [ "bstr", "gix-command", @@ -1340,9 +1341,9 @@ dependencies = [ [[package]] name = "gix-date" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "691142b1a34d18e8ed6e6114bc1a2736516c5ad60ef3aa9bd1b694886e3ca92d" +checksum = "c57c477b645ee248b173bb1176b52dd528872f12c50375801a58aaf5ae91113f" dependencies = [ "bstr", "itoa 1.0.11", @@ -1352,9 +1353,9 @@ dependencies = [ [[package]] name = "gix-diff" -version = "0.48.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a327be31a392144b60ab0b1c863362c32a1c8f7effdfa2141d5d5b6b916ef3bf" +checksum = "a8e92566eccbca205a0a0f96ffb0327c061e85bc5c95abbcddfe177498aa04f6" dependencies = [ "bstr", "gix-hash", @@ -1364,9 +1365,9 @@ dependencies = [ [[package]] name = "gix-dir" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd6a0618958f9cce78a32724f8e06c4f4a57ca7080f645736d53676dc9b4db9" +checksum = "fba2ffbcf4bd34438e8a8367ccbc94870549903d1f193a14f47eb6b0967e1293" dependencies = [ "bstr", "gix-discover", @@ -1422,9 +1423,9 @@ dependencies = [ [[package]] name = "gix-filter" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5108cc58d58b27df10ac4de7f31b2eb96d588a33e5eba23739b865f5d8db7995" +checksum = "3d0ecdee5667f840ba20c7fe56d63f8e1dc1e6b3bfd296151fe5ef07c874790a" dependencies = [ "bstr", "encoding_rs", @@ -1443,9 +1444,9 @@ dependencies = [ [[package]] name = "gix-fs" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34740384d8d763975858fa2c176b68652a6fcc09f616e24e3ce967b0d370e4d8" +checksum = "3b3d4fac505a621f97e5ce2c69fdc425742af00c0920363ca4074f0eb48b1db9" dependencies = [ "fastrand", "gix-features", @@ -1555,9 +1556,9 @@ dependencies = [ [[package]] name = "gix-object" -version = "0.46.0" +version = "0.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65d93e2bbfa83a307e47f45e45de7b6c04d7375a8bd5907b215f4bf45237d879" +checksum = "e42d58010183ef033f31088479b4eb92b44fe341b35b62d39eb8b185573d77ea" dependencies = [ "bstr", "gix-actor", @@ -1565,6 +1566,7 @@ dependencies = [ "gix-features", "gix-hash", "gix-hashtable", + "gix-path", "gix-utils", "gix-validate", "itoa 1.0.11", @@ -1575,9 +1577,9 @@ dependencies = [ [[package]] name = "gix-odb" -version = "0.65.0" +version = "0.66.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93bed6e1b577c25a6bb8e6ecbf4df525f29a671ddf5f2221821a56a8dbeec4e3" +checksum = "cb780eceb3372ee204469478de02eaa34f6ba98247df0186337e0333de97d0ae" dependencies = [ "arc-swap", "gix-date", @@ -1596,9 +1598,9 @@ dependencies = [ [[package]] name = "gix-pack" -version = "0.55.0" +version = "0.56.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b91fec04d359544fecbb8e85117ec746fbaa9046ebafcefb58cb74f20dc76d4" +checksum = "4158928929be29cae7ab97afc8e820a932071a7f39d8ba388eed2380c12c566c" dependencies = [ "clru", "gix-chunk", @@ -1616,9 +1618,9 @@ dependencies = [ [[package]] name = "gix-packetline" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a720e5bebf494c3ceffa85aa89f57a5859450a0da0a29ebe89171e23543fa78" +checksum = "911aeea8b2dabeed2f775af9906152a1f0109787074daf9e64224e3892dde453" dependencies = [ "bstr", "faster-hex", @@ -1668,9 +1670,9 @@ dependencies = [ [[package]] name = "gix-prompt" -version = "0.8.9" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a7822afc4bc9c5fbbc6ce80b00f41c129306b7685cac3248dbfa14784960594" +checksum = "82433a19aa44688e3bde05c692870eda50b5db053df53ed5ae6d8ea594a6babd" dependencies = [ "gix-command", "gix-config-value", @@ -1681,15 +1683,23 @@ dependencies = [ [[package]] name = "gix-protocol" -version = "0.46.1" +version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a7e7e51a0dea531d3448c297e2fa919b2de187111a210c324b7e9f81508b8ca" +checksum = "c84642e8b6fed7035ce9cc449593019c55b0ec1af7a5dce1ab8a0636eaaeb067" dependencies = [ "bstr", "gix-credentials", "gix-date", "gix-features", "gix-hash", + "gix-lock", + "gix-negotiate", + "gix-object", + "gix-ref", + "gix-refspec", + "gix-revwalk", + "gix-shallow", + "gix-trace", "gix-transport", "gix-utils", "maybe-async", @@ -1710,9 +1720,9 @@ dependencies = [ [[package]] name = "gix-ref" -version = "0.49.0" +version = "0.49.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eae462723686272a58f49501015ef7c0d67c3e042c20049d8dd9c7eff92efde" +checksum = "a91b61776c839d0f1b7114901179afb0947aa7f4d30793ca1c56d335dfef485f" dependencies = [ "gix-actor", "gix-features", @@ -1745,9 +1755,9 @@ dependencies = [ [[package]] name = "gix-revision" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44488e0380847967bc3e3cacd8b22652e02ea1eb58afb60edd91847695cd2d8d" +checksum = "61e1ddc474405a68d2ce8485705dd72fe6ce959f2f5fe718601ead5da2c8f9e7" dependencies = [ "bitflags 2.6.0", "bstr", @@ -1788,6 +1798,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "gix-shallow" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2673242e87492cb6ff671f0c01f689061ca306c4020f137197f3abc84ce01" +dependencies = [ + "bstr", + "gix-hash", + "gix-lock", + "thiserror 2.0.3", +] + [[package]] name = "gix-submodule" version = "0.16.0" @@ -1824,9 +1846,9 @@ checksum = "04bdde120c29f1fc23a24d3e115aeeea3d60d8e65bab92cc5f9d90d9302eb952" [[package]] name = "gix-transport" -version = "0.43.1" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a1a41357b7236c03e0c984147f823d87c3e445a8581bac7006df141577200b" +checksum = "dd04d91e507a8713cfa2318d5a85d75b36e53a40379cc7eb7634ce400ecacbaf" dependencies = [ "base64", "bstr", @@ -1843,9 +1865,9 @@ dependencies = [ [[package]] name = "gix-traverse" -version = "0.43.0" +version = "0.43.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff2ec9f779680f795363db1c563168b32b8d6728ec58564c628e85c92d29faf" +checksum = "6ed47d648619e23e93f971d2bba0d10c1100e54ef95d2981d609907a8cabac89" dependencies = [ "bitflags 2.6.0", "gix-commitgraph", @@ -1860,13 +1882,14 @@ dependencies = [ [[package]] name = "gix-url" -version = "0.28.1" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e09f97db3618fb8e473d7d97e77296b50aaee0ddcd6a867f07443e3e87391099" +checksum = "d096fb733ba6bd3f5403dba8bd72bdd8809fe2b347b57844040b8f49c93492d9" dependencies = [ "bstr", "gix-features", "gix-path", + "percent-encoding", "thiserror 2.0.3", "url", ] diff --git a/Cargo.toml b/Cargo.toml index 00dbda420cf..0a73f86fdb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,7 @@ filetime = "0.2.23" flate2 = { version = "1.0.30", default-features = false, features = ["zlib"] } git2 = "0.19.0" git2-curl = "0.20.0" -gix = { version = "0.68.0", default-features = false, features = ["blocking-http-transport-curl", "progress-tree", "parallel", "dirwalk"] } +gix = { version = "0.69.1", default-features = false, features = ["blocking-http-transport-curl", "progress-tree", "parallel", "dirwalk"] } glob = "0.3.1" handlebars = { version = "6.0.0", features = ["dir_source"] } hex = "0.4.3" diff --git a/src/cargo/sources/git/oxide.rs b/src/cargo/sources/git/oxide.rs index bbeca5a38fb..3380e014e84 100644 --- a/src/cargo/sources/git/oxide.rs +++ b/src/cargo/sources/git/oxide.rs @@ -352,7 +352,7 @@ pub fn cargo_config_to_gitoxide_overrides(gctx: &GlobalContext) -> CargoResult CargoResult<()> { fn init(path: &Path, bare: bool) -> CargoResult<()> { let mut opts = git2::RepositoryInitOptions::new(); diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index 781699b85e7..b40988724c9 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -1057,7 +1057,9 @@ pub fn fetch( fn has_shallow_lock_file(err: &crate::sources::git::fetch::Error) -> bool { matches!( err, - gix::env::collate::fetch::Error::Fetch(gix::remote::fetch::Error::LockShallowFile(_)) + gix::env::collate::fetch::Error::Fetch(gix::remote::fetch::Error::Fetch( + gix::protocol::fetch::Error::LockShallowFile(_) + )) ) } From 2c3f8d87c41980c8ea5d75676011c09d9d727b88 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Sun, 22 Dec 2024 19:05:40 +0100 Subject: [PATCH 271/525] fix: assure possibly blocking non-files (like FIFOs) won't be picked up for publishing. This would otherwise cause the publish to hang. --- src/cargo/sources/path.rs | 8 ++++++-- tests/testsuite/git.rs | 31 +++++++++++++++++++++++++++++++ tests/testsuite/package.rs | 29 +++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index 7765906977f..ee2e6fea47f 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -626,8 +626,11 @@ fn list_files_gix( .filter(|res| { // Don't include Cargo.lock if it is untracked. Packaging will // generate a new one as needed. + // Also don't include untrackable directory entries, like FIFOs. res.as_ref().map_or(true, |item| { - !(item.entry.status == Status::Untracked && item.entry.rela_path == "Cargo.lock") + item.entry.disk_kind != Some(gix::dir::entry::Kind::Untrackable) + && !(item.entry.status == Status::Untracked + && item.entry.rela_path == "Cargo.lock") }) }) .map(|res| res.map(|item| (item.entry.rela_path, item.entry.disk_kind))) @@ -751,7 +754,8 @@ fn list_files_walk( for entry in walkdir { match entry { Ok(entry) => { - if !entry.file_type().is_dir() { + let file_type = entry.file_type(); + if file_type.is_file() || file_type.is_symlink() { ret.push(entry.into_path()); } } diff --git a/tests/testsuite/git.rs b/tests/testsuite/git.rs index 40e9db15c86..7ccd08b7950 100644 --- a/tests/testsuite/git.rs +++ b/tests/testsuite/git.rs @@ -4221,3 +4221,34 @@ src/lib.rs "#]]) .run(); } + +#[cargo_test] +#[cfg(unix)] +fn simple_with_fifo() { + let git_project = git::new("foo", |project| { + project + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2015" + "#, + ) + .file("src/main.rs", "fn main() {}") + }); + + std::process::Command::new("mkfifo") + .current_dir(git_project.root()) + .arg(git_project.root().join("blocks-when-read")) + .status() + .expect("a FIFO can be created"); + + // Avoid actual blocking even in case of failure, assuming that what it lists here + // would also be read eventually. + git_project + .cargo("package -l") + .with_stdout_does_not_contain("blocks-when-read") + .run(); +} diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index 1740de4ac77..2ee3a2ef7d4 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -6873,3 +6873,32 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for "#]]) .run(); } + +#[cargo_test] +#[cfg(unix)] +fn simple_with_fifo() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2015" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + std::process::Command::new("mkfifo") + .current_dir(p.root()) + .arg(p.root().join("blocks-when-read")) + .status() + .expect("a FIFO can be created"); + + // Avoid actual blocking even in case of failure, assuming that what it lists here + // would also be read eventually. + p.cargo("package -l") + .with_stdout_does_not_contain("blocks-when-read") + .run(); +} From af92c4445b4028ce381586554924883d92a81369 Mon Sep 17 00:00:00 2001 From: Sebastian Thiel Date: Tue, 24 Dec 2024 08:03:16 +0100 Subject: [PATCH 272/525] Use snapshots instead of matching on parts of the output Personally I liked that the test was only dependent on what really matters, the lack of presence of a particular filename. Now the test would fail if Cargo one day adds more (generated) files to the package. Co-authored-by: Weihang Lo --- tests/testsuite/git.rs | 9 ++++++++- tests/testsuite/package.rs | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/testsuite/git.rs b/tests/testsuite/git.rs index 7ccd08b7950..c63d355c445 100644 --- a/tests/testsuite/git.rs +++ b/tests/testsuite/git.rs @@ -4249,6 +4249,13 @@ fn simple_with_fifo() { // would also be read eventually. git_project .cargo("package -l") - .with_stdout_does_not_contain("blocks-when-read") + .with_stdout_data(str![[r#" +.cargo_vcs_info.json +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs + +"#]]) .run(); } diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index 2ee3a2ef7d4..e349f744ce8 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -6899,6 +6899,12 @@ fn simple_with_fifo() { // Avoid actual blocking even in case of failure, assuming that what it lists here // would also be read eventually. p.cargo("package -l") - .with_stdout_does_not_contain("blocks-when-read") + .with_stdout_data(str![[r#" +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs + +"#]]) .run(); } From 3d7b154d158eea5fbf1564320f9043371ff92c62 Mon Sep 17 00:00:00 2001 From: ranger-ross Date: Sat, 21 Dec 2024 17:30:53 +0900 Subject: [PATCH 273/525] Moved manifest metadata tracking from fingerprint to dep info This change moves the manifest metadata track to dep-info files with the goal of reduce unneeded rebuilds when metadata is changed as well fixing issues where builds are not retrigged due to metadata changes when they should (ie. #14154) --- src/cargo/core/compiler/compilation.rs | 41 +----- .../core/compiler/fingerprint/dep_info.rs | 6 +- .../core/compiler/fingerprint/dirty_reason.rs | 2 - src/cargo/core/compiler/fingerprint/mod.rs | 33 ++--- src/cargo/core/manifest.rs | 81 +++++++++++ tests/testsuite/freshness.rs | 133 +++++++++++++----- tests/testsuite/freshness_checksum.rs | 53 +------ 7 files changed, 207 insertions(+), 142 deletions(-) diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index 7531f0fa61b..c30343ef16c 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -351,8 +351,6 @@ impl<'gctx> Compilation<'gctx> { } } - let metadata = pkg.manifest().metadata(); - let cargo_exe = self.gctx.cargo_exe()?; cmd.env(crate::CARGO_ENV, cargo_exe); @@ -360,7 +358,6 @@ impl<'gctx> Compilation<'gctx> { // crate properties which might require rebuild upon change // consider adding the corresponding properties to the hash // in BuildContext::target_metadata() - let rust_version = pkg.rust_version().as_ref().map(ToString::to_string); cmd.env("CARGO_MANIFEST_DIR", pkg.root()) .env("CARGO_MANIFEST_PATH", pkg.manifest_path()) .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string()) @@ -368,37 +365,13 @@ impl<'gctx> Compilation<'gctx> { .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string()) .env("CARGO_PKG_VERSION_PRE", pkg.version().pre.as_str()) .env("CARGO_PKG_VERSION", &pkg.version().to_string()) - .env("CARGO_PKG_NAME", &*pkg.name()) - .env( - "CARGO_PKG_DESCRIPTION", - metadata.description.as_ref().unwrap_or(&String::new()), - ) - .env( - "CARGO_PKG_HOMEPAGE", - metadata.homepage.as_ref().unwrap_or(&String::new()), - ) - .env( - "CARGO_PKG_REPOSITORY", - metadata.repository.as_ref().unwrap_or(&String::new()), - ) - .env( - "CARGO_PKG_LICENSE", - metadata.license.as_ref().unwrap_or(&String::new()), - ) - .env( - "CARGO_PKG_LICENSE_FILE", - metadata.license_file.as_ref().unwrap_or(&String::new()), - ) - .env("CARGO_PKG_AUTHORS", &pkg.authors().join(":")) - .env( - "CARGO_PKG_RUST_VERSION", - &rust_version.as_deref().unwrap_or_default(), - ) - .env( - "CARGO_PKG_README", - metadata.readme.as_ref().unwrap_or(&String::new()), - ) - .cwd(pkg.root()); + .env("CARGO_PKG_NAME", &*pkg.name()); + + for (key, value) in pkg.manifest().metadata().env_vars() { + cmd.env(key, value.as_ref()); + } + + cmd.cwd(pkg.root()); apply_env_config(self.gctx, &mut cmd)?; diff --git a/src/cargo/core/compiler/fingerprint/dep_info.rs b/src/cargo/core/compiler/fingerprint/dep_info.rs index e8628f34ebf..746c0832035 100644 --- a/src/cargo/core/compiler/fingerprint/dep_info.rs +++ b/src/cargo/core/compiler/fingerprint/dep_info.rs @@ -19,6 +19,7 @@ use cargo_util::paths; use cargo_util::ProcessBuilder; use cargo_util::Sha256; +use crate::core::manifest::ManifestMetadata; use crate::CargoResult; use crate::CARGO_ENV; @@ -334,7 +335,10 @@ pub fn translate_dep_info( // // For cargo#13280, We trace env vars that are defined in the `[env]` config table. on_disk_info.env.retain(|(key, _)| { - env_config.contains_key(key) || !rustc_cmd.get_envs().contains_key(key) || key == CARGO_ENV + ManifestMetadata::should_track(key) + || env_config.contains_key(key) + || !rustc_cmd.get_envs().contains_key(key) + || key == CARGO_ENV }); let serialize_path = |file| { diff --git a/src/cargo/core/compiler/fingerprint/dirty_reason.rs b/src/cargo/core/compiler/fingerprint/dirty_reason.rs index 2da7d1e6d88..6881f72a837 100644 --- a/src/cargo/core/compiler/fingerprint/dirty_reason.rs +++ b/src/cargo/core/compiler/fingerprint/dirty_reason.rs @@ -26,7 +26,6 @@ pub enum DirtyReason { old: Vec, new: Vec, }, - MetadataChanged, ConfigSettingsChanged, CompileKindChanged, LocalLengthsChanged, @@ -168,7 +167,6 @@ impl DirtyReason { s.dirty_because(unit, "the profile configuration changed") } DirtyReason::RustflagsChanged { .. } => s.dirty_because(unit, "the rustflags changed"), - DirtyReason::MetadataChanged => s.dirty_because(unit, "the metadata changed"), DirtyReason::ConfigSettingsChanged => { s.dirty_because(unit, "the config settings changed") } diff --git a/src/cargo/core/compiler/fingerprint/mod.rs b/src/cargo/core/compiler/fingerprint/mod.rs index ac29b0d15d9..623e4c59b60 100644 --- a/src/cargo/core/compiler/fingerprint/mod.rs +++ b/src/cargo/core/compiler/fingerprint/mod.rs @@ -78,7 +78,6 @@ //! [`CompileKind`] (host/target) | ✓ | ✓ | ✓ | ✓ //! `__CARGO_DEFAULT_LIB_METADATA`[^4] | | ✓ | ✓ | ✓ //! `package_id` | | ✓ | ✓ | ✓ -//! authors, description, homepage, repo | ✓ | | | //! Target src path relative to ws | ✓ | | | //! Target flags (test/bench/for_host/edition) | ✓ | | | //! -C incremental=… flag | ✓ | | | @@ -189,6 +188,8 @@ //! files to learn about environment variables that the rustc compile depends on. //! Cargo then later uses this to trigger a recompile if a referenced env var //! changes (even if the source didn't change). +//! This also includes env vars generated from Cargo metadata like `CARGO_PKG_DESCRIPTION`. +//! (See [`crate::core::manifest::ManifestMetadata`] //! //! #### dep-info files for build system integration. //! @@ -612,10 +613,6 @@ pub struct Fingerprint { memoized_hash: Mutex>, /// RUSTFLAGS/RUSTDOCFLAGS environment variable value (or config value). rustflags: Vec, - /// Hash of some metadata from the manifest, such as "authors", or - /// "description", which are exposed as environment variables during - /// compilation. - metadata: u64, /// Hash of various config settings that change how things are compiled. config: u64, /// The rustc target. This is only relevant for `.json` files, otherwise @@ -831,11 +828,12 @@ impl LocalFingerprint { &self, mtime_cache: &mut HashMap, checksum_cache: &mut HashMap, - pkg_root: &Path, + pkg: &Package, target_root: &Path, cargo_exe: &Path, gctx: &GlobalContext, ) -> CargoResult> { + let pkg_root = pkg.root(); match self { // We need to parse `dep_info`, learn about the crate's dependencies. // @@ -849,6 +847,12 @@ impl LocalFingerprint { return Ok(Some(StaleItem::MissingFile(dep_info))); }; for (key, previous) in info.env.iter() { + if let Some(value) = pkg.manifest().metadata().env_var(key.as_str()) { + if Some(value.as_ref()) == previous.as_deref() { + continue; + } + } + let current = if key == CARGO_ENV { Some(cargo_exe.to_str().ok_or_else(|| { format_err!( @@ -932,7 +936,6 @@ impl Fingerprint { local: Mutex::new(Vec::new()), memoized_hash: Mutex::new(None), rustflags: Vec::new(), - metadata: 0, config: 0, compile_kind: 0, fs_status: FsStatus::Stale, @@ -995,9 +998,6 @@ impl Fingerprint { new: self.rustflags.clone(), }; } - if self.metadata != old.metadata { - return DirtyReason::MetadataChanged; - } if self.config != old.config { return DirtyReason::ConfigSettingsChanged; } @@ -1142,13 +1142,14 @@ impl Fingerprint { &mut self, mtime_cache: &mut HashMap, checksum_cache: &mut HashMap, - pkg_root: &Path, + pkg: &Package, target_root: &Path, cargo_exe: &Path, gctx: &GlobalContext, ) -> CargoResult<()> { assert!(!self.fs_status.up_to_date()); + let pkg_root = pkg.root(); let mut mtimes = HashMap::new(); // Get the `mtime` of all outputs. Optionally update their mtime @@ -1249,7 +1250,7 @@ impl Fingerprint { if let Some(item) = local.find_stale_item( mtime_cache, checksum_cache, - pkg_root, + pkg, target_root, cargo_exe, gctx, @@ -1279,7 +1280,6 @@ impl hash::Hash for Fingerprint { profile, ref deps, ref local, - metadata, config, compile_kind, ref rustflags, @@ -1294,7 +1294,6 @@ impl hash::Hash for Fingerprint { path, profile, &*local, - metadata, config, compile_kind, rustflags, @@ -1445,7 +1444,7 @@ fn calculate(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult fingerprint.check_filesystem( &mut build_runner.mtime_cache, &mut build_runner.checksum_cache, - unit.pkg.root(), + &unit.pkg, &target_root, cargo_exe, build_runner.bcx.gctx, @@ -1529,9 +1528,6 @@ fn calculate_normal( build_runner.lto[unit], unit.pkg.manifest().lint_rustflags(), )); - // Include metadata since it is exposed as environment variables. - let m = unit.pkg.manifest().metadata(); - let metadata = util::hash_u64((&m.authors, &m.description, &m.homepage, &m.repository)); let mut config = StableHasher::new(); if let Some(linker) = build_runner.compilation.target_linker(unit.kind) { linker.hash(&mut config); @@ -1560,7 +1556,6 @@ fn calculate_normal( deps, local: Mutex::new(local), memoized_hash: Mutex::new(None), - metadata, config: Hasher::finish(&config), compile_kind, rustflags: extra_flags, diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index d63dbd61de3..88845ff26e0 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::collections::{BTreeMap, HashMap}; use std::fmt; use std::hash::{Hash, Hasher}; @@ -146,6 +147,86 @@ pub struct ManifestMetadata { pub rust_version: Option, } +macro_rules! get_metadata_env { + ($meta:ident, $field:ident) => { + $meta.$field.as_deref().unwrap_or_default().into() + }; + ($meta:ident, $field:ident, $to_var:expr) => { + $to_var($meta).into() + }; +} + +macro_rules! metadata_envs { + ( + $( + ($field:ident, $key:literal$(, $to_var:expr)?), + )* + ) => { + struct MetadataEnvs; + impl MetadataEnvs { + $( + fn $field(meta: &ManifestMetadata) -> Cow<'_, str> { + get_metadata_env!(meta, $field$(, $to_var)?) + } + )* + + pub fn should_track(key: &str) -> bool { + let keys = [$($key),*]; + key.strip_prefix("CARGO_PKG_") + .map(|key| keys.iter().any(|k| *k == key)) + .unwrap_or_default() + } + + pub fn var<'a>(meta: &'a ManifestMetadata, key: &str) -> Option> { + key.strip_prefix("CARGO_PKG_").and_then(|key| match key { + $($key => Some(Self::$field(meta)),)* + _ => None, + }) + } + + pub fn vars(meta: &ManifestMetadata) -> impl Iterator)> { + [ + $( + ( + concat!("CARGO_PKG_", $key), + Self::$field(meta), + ), + )* + ].into_iter() + } + } + } +} + +// Metadata enviromental variables that are emitted to rustc. Usable by `env!()` +// If these change we need to trigger a rebuild. +// NOTE: The env var name will be prefixed with `CARGO_PKG_` +metadata_envs! { + (description, "DESCRIPTION"), + (homepage, "HOMEPAGE"), + (repository, "REPOSITORY"), + (license, "LICENSE"), + (license_file, "LICENSE_FILE"), + (authors, "AUTHORS", |m: &ManifestMetadata| m.authors.join(":")), + (rust_version, "RUST_VERSION", |m: &ManifestMetadata| m.rust_version.as_ref().map(ToString::to_string).unwrap_or_default()), + (readme, "README"), +} + +impl ManifestMetadata { + /// Whether the given env var should be tracked by Cargo's dep-info. + pub fn should_track(env_key: &str) -> bool { + MetadataEnvs::should_track(env_key) + } + + pub fn env_var<'a>(&'a self, env_key: &str) -> Option> { + MetadataEnvs::var(self, env_key) + } + + pub fn env_vars(&self) -> impl Iterator)> { + MetadataEnvs::vars(self) + } +} + #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum TargetKind { Lib(Vec), diff --git a/tests/testsuite/freshness.rs b/tests/testsuite/freshness.rs index d2a6135e8a5..5b3042f721f 100644 --- a/tests/testsuite/freshness.rs +++ b/tests/testsuite/freshness.rs @@ -953,7 +953,7 @@ new desc "#]]) .with_stderr_data(str![[r#" -[DIRTY] foo v0.0.1 ([ROOT]/foo): the metadata changed +[DIRTY] foo v0.0.1 ([ROOT]/foo): the environment variable CARGO_PKG_DESCRIPTION changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -2113,49 +2113,114 @@ fn simulated_docker_deps_stay_cached() { #[cargo_test] fn metadata_change_invalidates() { - let p = project() - .file( - "Cargo.toml", + // (key, value, value-updated, env-var-name) + let scenarios = [ + ( + "description", + r#""foo""#, + r#""foo_updated""#, + "CARGO_PKG_DESCRIPTION", + ), + ( + "homepage", + r#""foo""#, + r#""foo_updated""#, + "CARGO_PKG_HOMEPAGE", + ), + ( + "repository", + r#""foo""#, + r#""foo_updated""#, + "CARGO_PKG_REPOSITORY", + ), + ( + "license", + r#""foo""#, + r#""foo_updated""#, + "CARGO_PKG_LICENSE", + ), + ( + "license-file", + r#""foo""#, + r#""foo_updated""#, + "CARGO_PKG_LICENSE_FILE", + ), + ( + "authors", + r#"["foo"]"#, + r#"["foo_updated"]"#, + "CARGO_PKG_AUTHORS", + ), + ( + "rust-version", + r#""1.0.0""#, + r#""1.0.1""#, + "CARGO_PKG_RUST_VERSION", + ), + ("readme", r#""foo""#, r#""foo_updated""#, "CARGO_PKG_README"), + ]; + let base_cargo_toml = r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2015" + "#; + + let p = project().build(); + for (key, value, value_updated, env_var) in scenarios { + p.change_file("Cargo.toml", base_cargo_toml); + p.change_file( + "src/main.rs", + &format!( + r#" + fn main() {{ + let output = env!("{env_var}"); + println!("{{output}}"); + }} + "# + ), + ); + + // Compile the first time + p.cargo("build").run(); + + // Update the manifest, rebuild, and verify the build was invalided + p.change_file("Cargo.toml", &format!("{base_cargo_toml}\n{key} = {value}")); + p.cargo("build -v") + .with_stderr_data(format!( + r#"[DIRTY] foo v0.1.0 ([ROOT]/foo): the environment variable {env_var} changed +[COMPILING] foo v0.1.0 ([ROOT]/foo) +[RUNNING] `rustc [..] +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s +"# + )) + .run(); + + // Remove references to the metadata and rebuild + p.change_file( + "src/main.rs", r#" - [package] - name = "foo" - version = "0.1.0" - edition = "2015" + fn main() { + println!("foo"); + } "#, - ) - .file("src/lib.rs", "") - .build(); + ); + p.cargo("build").run(); - p.cargo("build").run(); + // Update the manifest value and verify the build is NOT invalidated. + p.change_file( + "Cargo.toml", + &format!("{base_cargo_toml}\n{key} = {value_updated}"), + ); - for attr in &[ - "authors = [\"foo\"]", - "description = \"desc\"", - "homepage = \"https://example.com\"", - "repository =\"https://example.com\"", - ] { - let mut file = OpenOptions::new() - .write(true) - .append(true) - .open(p.root().join("Cargo.toml")) - .unwrap(); - writeln!(file, "{}", attr).unwrap(); - p.cargo("build") + p.cargo("build -v") .with_stderr_data(str![[r#" -[COMPILING] foo v0.1.0 ([ROOT]/foo) +[FRESH] foo v0.1.0 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } - p.cargo("build -v") - .with_stderr_data(str![[r#" -[FRESH] foo v0.1.0 ([ROOT]/foo) -[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s - -"#]]) - .run(); - assert_eq!(p.glob("target/debug/deps/libfoo-*.rlib").count(), 1); } #[cargo_test] diff --git a/tests/testsuite/freshness_checksum.rs b/tests/testsuite/freshness_checksum.rs index 5c4b88fdbc3..1ae2ba1cd2c 100644 --- a/tests/testsuite/freshness_checksum.rs +++ b/tests/testsuite/freshness_checksum.rs @@ -1099,7 +1099,7 @@ new desc "#]]) .with_stderr_data(str![[r#" -[DIRTY] foo v0.0.1 ([ROOT]/foo): the metadata changed +[DIRTY] foo v0.0.1 ([ROOT]/foo): the environment variable CARGO_PKG_DESCRIPTION changed [COMPILING] foo v0.0.1 ([ROOT]/foo) [RUNNING] `rustc [..] [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s @@ -1994,57 +1994,6 @@ fn script_fails_stay_dirty() { .run(); } -#[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] -fn metadata_change_invalidates() { - let p = project() - .file( - "Cargo.toml", - r#" - [package] - name = "foo" - version = "0.1.0" - edition = "2015" - "#, - ) - .file("src/lib.rs", "") - .build(); - - p.cargo("build -Zchecksum-freshness") - .masquerade_as_nightly_cargo(&["checksum-freshness"]) - .run(); - - for attr in &[ - "authors = [\"foo\"]", - "description = \"desc\"", - "homepage = \"https://example.com\"", - "repository =\"https://example.com\"", - ] { - let mut file = OpenOptions::new() - .write(true) - .append(true) - .open(p.root().join("Cargo.toml")) - .unwrap(); - writeln!(file, "{}", attr).unwrap(); - p.cargo("build -Zchecksum-freshness") - .masquerade_as_nightly_cargo(&["checksum-freshness"]) - .with_stderr_data(str![[r#" -[COMPILING] foo v0.1.0 ([ROOT]/foo) -[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s - -"#]]) - .run(); - } - p.cargo("build -Zchecksum-freshness -v") - .masquerade_as_nightly_cargo(&["checksum-freshness"]) - .with_stderr_data(str![[r#" -[FRESH] foo v0.1.0 ([ROOT]/foo) -[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s - -"#]]) - .run(); - assert_eq!(p.glob("target/debug/deps/libfoo-*.rlib").count(), 1); -} - #[cargo_test(nightly, reason = "requires -Zchecksum-hash-algorithm")] fn edition_change_invalidates() { const MANIFEST: &str = r#" From 0921264bc541426e96ca00587e9134ef302e00c0 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 24 Dec 2024 10:30:52 -0500 Subject: [PATCH 274/525] test: make path arguments more generic and flexible So we don't need to `p.to_str().unwrap()` and are able to pass different types for each argument --- crates/cargo-test-support/src/lib.rs | 14 +++++++------- tests/testsuite/binary_name.rs | 2 +- tests/testsuite/dep_info.rs | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/cargo-test-support/src/lib.rs b/crates/cargo-test-support/src/lib.rs index a36e61c3b80..f282b3d9bdd 100644 --- a/crates/cargo-test-support/src/lib.rs +++ b/crates/cargo-test-support/src/lib.rs @@ -299,7 +299,7 @@ impl ProjectBuilder { } /// Adds a symlink to a file to the project. - pub fn symlink>(mut self, dst: T, src: T) -> Self { + pub fn symlink(mut self, dst: impl AsRef, src: impl AsRef) -> Self { self.symlinks.push(SymlinkBuilder::new( self.root.root().join(dst), self.root.root().join(src), @@ -308,7 +308,7 @@ impl ProjectBuilder { } /// Create a symlink to a directory - pub fn symlink_dir>(mut self, dst: T, src: T) -> Self { + pub fn symlink_dir(mut self, dst: impl AsRef, src: impl AsRef) -> Self { self.symlinks.push(SymlinkBuilder::new_dir( self.root.root().join(dst), self.root.root().join(src), @@ -368,7 +368,7 @@ impl ProjectBuilder { impl Project { /// Copy the test project from a fixed state - pub fn from_template(template_path: impl AsRef) -> Self { + pub fn from_template(template_path: impl AsRef) -> Self { let root = paths::root(); let project_root = root.join("case"); snapbox::dir::copy_template(template_path.as_ref(), &project_root).unwrap(); @@ -459,7 +459,7 @@ impl Project { /// # let p = cargo_test_support::project().build(); /// p.change_file("src/lib.rs", "fn new_fn() {}"); /// ``` - pub fn change_file(&self, path: &str, body: &str) { + pub fn change_file(&self, path: impl AsRef, body: &str) { FileBuilder::new(self.root().join(path), body, false).mk() } @@ -530,7 +530,7 @@ impl Project { } /// Returns the contents of a path in the project root - pub fn read_file(&self, path: &str) -> String { + pub fn read_file(&self, path: impl AsRef) -> String { let full = self.root().join(path); fs::read_to_string(&full) .unwrap_or_else(|e| panic!("could not read file {}: {}", full.display(), e)) @@ -572,12 +572,12 @@ pub fn project() -> ProjectBuilder { } /// Generates a project layout in given directory, see [`ProjectBuilder`] -pub fn project_in(dir: &str) -> ProjectBuilder { +pub fn project_in(dir: impl AsRef) -> ProjectBuilder { ProjectBuilder::new(paths::root().join(dir).join("foo")) } /// Generates a project layout inside our fake home dir, see [`ProjectBuilder`] -pub fn project_in_home(name: &str) -> ProjectBuilder { +pub fn project_in_home(name: impl AsRef) -> ProjectBuilder { ProjectBuilder::new(paths::home().join(name)) } diff --git a/tests/testsuite/binary_name.rs b/tests/testsuite/binary_name.rs index 61569168c22..a26ba9882cf 100644 --- a/tests/testsuite/binary_name.rs +++ b/tests/testsuite/binary_name.rs @@ -91,7 +91,7 @@ fn binary_name1() { let deps_path = p.bin("007bar").with_extension("d"); assert!(deps_path.is_file(), "{:?}", bar_path); - let depinfo = p.read_file(deps_path.to_str().unwrap()); + let depinfo = p.read_file(&deps_path); // Prepare what content we expect to be present in deps file. let deps_exp = format!( diff --git a/tests/testsuite/dep_info.rs b/tests/testsuite/dep_info.rs index d29ca370caf..8104f62ba86 100644 --- a/tests/testsuite/dep_info.rs +++ b/tests/testsuite/dep_info.rs @@ -25,7 +25,7 @@ fn build_dep_info() { assert!(depinfo_bin_path.is_file()); - let depinfo = p.read_file(depinfo_bin_path.to_str().unwrap()); + let depinfo = p.read_file(depinfo_bin_path); let bin_path = p.bin("foo"); let src_path = p.root().join("src").join("foo.rs"); @@ -134,7 +134,7 @@ fn dep_path_inside_target_has_correct_path() { assert!(depinfo_path.is_file(), "{:?}", depinfo_path); - let depinfo = p.read_file(depinfo_path.to_str().unwrap()); + let depinfo = p.read_file(depinfo_path); let bin_path = p.bin("a"); let target_debug_blah = Path::new("target").join("debug").join("blah"); From a991a7dd9847ae31c9f7bce97de9411579254453 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sun, 22 Dec 2024 03:39:02 -0500 Subject: [PATCH 275/525] test(package): show corner cases of vcs dirtiness check This is a test showing corner cases that dirty files outside the package being packaging actually made the `.crate` file dirty. However, `cargo package` and `.cargo_vcs_info.json` didn't capture it. --- tests/testsuite/package.rs | 112 +++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index e349f744ce8..405cd8bbeca 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -1308,6 +1308,118 @@ to proceed despite this and include the uncommitted changes, pass the `--allow-d ); } +#[cargo_test] +fn dirty_file_outside_pkg_root_considered_dirty() { + if !symlink_supported() { + return; + } + let main_outside_pkg_root = paths::root().join("main.rs"); + let (p, repo) = git::new_repo("foo", |p| { + p.file( + "Cargo.toml", + r#" + [workspace] + members = ["isengard"] + resolver = "2" + [workspace.package] + edition = "2015" + "#, + ) + .file("lib.rs", r#"compile_error!("you shall not pass")"#) + .file("LICENSE", "before") + .file("README.md", "before") + .file( + "isengard/Cargo.toml", + r#" + [package] + name = "isengard" + edition.workspace = true + homepage = "saruman" + description = "saruman" + license-file = "../LICENSE" + "#, + ) + .symlink("lib.rs", "isengard/src/lib.rs") + .symlink("README.md", "isengard/README.md") + .file(&main_outside_pkg_root, "fn main() {}") + .symlink(&main_outside_pkg_root, "isengard/src/main.rs") + }); + git::commit(&repo); + + // Changing files outside pkg root under situations below should be treated + // as dirty. `cargo package` is expected to fail on VCS stastus check. + // + // * Changes in files outside package root that source files symlink to + p.change_file("README.md", "after"); + p.change_file("lib.rs", "pub fn after() {}"); + // * Changes in files outside pkg root that `license-file`/`readme` point to + p.change_file("LICENSE", "after"); + // * When workspace inheritance is involved and changed + p.change_file( + "Cargo.toml", + r#" + [workspace] + members = ["isengard"] + resolver = "2" + [workspace.package] + edition = "2021" + "#, + ); + // Changes in files outside git workdir won't affect vcs status check + p.change_file( + &main_outside_pkg_root, + r#"fn main() { eprintln!("after"); }"#, + ); + + // Ensure dirty files be reported. + p.cargo("package --workspace --no-verify") + .with_stderr_data(str![[r#" +[PACKAGING] isengard v0.0.0 ([ROOT]/foo/isengard) +[PACKAGED] 8 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) + +"#]]) + .run(); + + p.cargo("package --workspace --no-verify --allow-dirty") + .with_stderr_data(str![[r#" +[PACKAGING] isengard v0.0.0 ([ROOT]/foo/isengard) +[PACKAGED] 8 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) + +"#]]) + .run(); + + let cargo_toml = str![[r##" +... +[package] +edition = "2021" +... + +"##]]; + + let f = File::open(&p.root().join("target/package/isengard-0.0.0.crate")).unwrap(); + validate_crate_contents( + f, + "isengard-0.0.0.crate", + &[ + ".cargo_vcs_info.json", + "Cargo.toml", + "Cargo.toml.orig", + "src/lib.rs", + "src/main.rs", + "Cargo.lock", + "LICENSE", + "README.md", + ], + [ + ("src/lib.rs", str!["pub fn after() {}"]), + ("src/main.rs", str![r#"fn main() { eprintln!("after"); }"#]), + ("README.md", str!["after"]), + ("LICENSE", str!["after"]), + ("Cargo.toml", cargo_toml), + ], + ); +} + #[cargo_test] fn issue_13695_allow_dirty_vcs_info() { let p = project() From 863e1f40deebd27fbfac3d292327bd066905a297 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sun, 22 Dec 2024 03:31:11 -0500 Subject: [PATCH 276/525] fix(package): check dirtiness of path fields in manifest This adds a special case for `package.{readme,license-file}` to Git VCS status check. If they were specified with paths outside the current package root, but still under git workdir, Cargo checks git status of those files to determine if they were dirty. We don't need to take care of other fields with path values because * `PathSource` only list files under the package root. Things like `target.path` works for `cargo build`, but won't be included in `.crate` file from `cargo publish`. * The only exceptions are `package.readme`/`package.license-file`. Cargo would copy files over if they are outside package root. --- src/cargo/ops/cargo_package.rs | 39 +++++++++++++++++++++++++++++++++- tests/testsuite/package.rs | 9 ++++++-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index b54a16d2c9b..9c0b1d6ac43 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -796,7 +796,7 @@ fn check_repo_state( .and_then(|p| p.to_str()) .unwrap_or("") .replace("\\", "/"); - let Some(git) = git(gctx, src_files, &repo, &opts)? else { + let Some(git) = git(p, gctx, src_files, &repo, &opts)? else { // If the git repo lacks essensial field like `sha1`, and since this field exists from the beginning, // then don't generate the corresponding file in order to maintain consistency with past behavior. return Ok(None); @@ -805,6 +805,7 @@ fn check_repo_state( return Ok(Some(VcsInfo { git, path_in_vcs })); fn git( + pkg: &Package, gctx: &GlobalContext, src_files: &[PathBuf], repo: &git2::Repository, @@ -828,6 +829,7 @@ fn check_repo_state( let mut dirty_src_files: Vec<_> = src_files .iter() .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path))) + .chain(dirty_metadata_paths(pkg, repo)?.iter()) .map(|path| { pathdiff::diff_paths(path, cwd) .as_ref() @@ -860,6 +862,41 @@ fn check_repo_state( } } + /// Checks whether files at paths specified in `package.readme` and + /// `package.license-file` have been modified. + /// + /// This is required because those paths may link to a file outside the + /// current package root, but still under the git workdir, affecting the + /// final packaged `.crate` file. + fn dirty_metadata_paths(pkg: &Package, repo: &git2::Repository) -> CargoResult> { + let mut dirty_files = Vec::new(); + let workdir = repo.workdir().unwrap(); + let root = pkg.root(); + let meta = pkg.manifest().metadata(); + for path in [&meta.license_file, &meta.readme] { + let Some(path) = path.as_deref().map(Path::new) else { + continue; + }; + let abs_path = paths::normalize_path(&root.join(path)); + if paths::strip_prefix_canonical(abs_path.as_path(), root).is_ok() { + // Inside package root. Don't bother checking git status. + continue; + } + if let Ok(rel_path) = paths::strip_prefix_canonical(abs_path.as_path(), workdir) { + // Outside package root but under git workdir, + if repo.status_file(&rel_path)? != git2::Status::CURRENT { + dirty_files.push(if abs_path.is_symlink() { + // For symlinks, shows paths to symlink sources + workdir.join(rel_path) + } else { + abs_path + }); + } + } + } + Ok(dirty_files) + } + // Helper to collect dirty statuses for a single repo. fn collect_statuses( repo: &git2::Repository, diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index 405cd8bbeca..b418513eace 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -1373,9 +1373,14 @@ fn dirty_file_outside_pkg_root_considered_dirty() { // Ensure dirty files be reported. p.cargo("package --workspace --no-verify") + .with_status(101) .with_stderr_data(str![[r#" -[PACKAGING] isengard v0.0.0 ([ROOT]/foo/isengard) -[PACKAGED] 8 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[ERROR] 2 files in the working directory contain changes that were not yet committed into git: + +LICENSE +README.md + +to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag "#]]) .run(); From 2a9527be4926c75f99030d76cc6715641acf48b9 Mon Sep 17 00:00:00 2001 From: Jacob Finkelman Date: Tue, 24 Dec 2024 19:04:38 +0000 Subject: [PATCH 277/525] assert that this comparison is unneeded --- src/cargo/core/source_id.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/src/cargo/core/source_id.rs b/src/cargo/core/source_id.rs index 876d34281e9..4104cf31a6f 100644 --- a/src/cargo/core/source_id.rs +++ b/src/cargo/core/source_id.rs @@ -598,14 +598,38 @@ impl Ord for SourceId { other => return other, } - // If the `kind` and the `url` are equal, then for git sources we also - // ensure that the canonical urls are equal. + let ord = self.inner.canonical_url.cmp(&other.inner.canonical_url); + match (&self.inner.kind, &other.inner.kind) { (SourceKind::Git(_), SourceKind::Git(_)) => { - self.inner.canonical_url.cmp(&other.inner.canonical_url) + // In the pre-PR code we returned Ord here, + // so there is no chance that this commit has broken anything about this match arm. + } + _ => { + // In the pre-PR code we returned cmp of url here, so let's make sure that's the same. + assert_eq!(self.inner.url.cmp(&other.inner.url), ord); + // I am quite sure that this assert will never fire. + // In order for it to fire either `url`s are equal but `canonical_url`s are not, + // or the other way around. The algorithm for constructing a canonical URL is deterministic, + // so if it's given the same URL it will return the same canonical URL. + + // But what if we have two different URLs that canonical eyes the same? + // I assert that the second one would get thrown out when the second `SourceId` was interned. + // `SourceId::new` is the only way to make a `SourceId`. It allways construct them with + // `precise: None`. Furthermore, it uses `SourceId::wrap` to see if it has ever constructed + // a previous instance with a `SourceIdInner` that is `SourceIdInner::eq` with the one beeing added. + // `SourceIdInner::eq` only looks at `kind`, `precise`, and `canonical_url`. + // Proof by contradiction: If we had constructed two `SourceId` that: + // 1. have the same `kind` (check that a few lines ago) + // 2. have the same `precise` (as at construction time it is allways None) + // 3. have the same `canonical_url` (by the assumption of this paragraph) + // then the `ptr::eq` would return equal. Even if they were constructed with different `url`s, + // `SourceId::wrap` would have noticed that they `SourceIdInner::eq` + // and thus returned a pointer to the first one. } - _ => self.inner.url.cmp(&other.inner.url), } + + ord } } From a84336e792a9ee6e2ac81544ead50f83ff419c31 Mon Sep 17 00:00:00 2001 From: Jacob Finkelman Date: Tue, 24 Dec 2024 19:50:02 +0000 Subject: [PATCH 278/525] remove the assert for cleaner code --- src/cargo/core/source_id.rs | 41 +++---------------------------------- 1 file changed, 3 insertions(+), 38 deletions(-) diff --git a/src/cargo/core/source_id.rs b/src/cargo/core/source_id.rs index 4104cf31a6f..d3578d98b9a 100644 --- a/src/cargo/core/source_id.rs +++ b/src/cargo/core/source_id.rs @@ -591,45 +591,10 @@ impl Ord for SourceId { return Ordering::Equal; } - // Sort first based on `kind`, deferring to the URL comparison below if + // Sort first based on `kind`, deferring to the URL comparison if // the kinds are equal. - match self.inner.kind.cmp(&other.inner.kind) { - Ordering::Equal => {} - other => return other, - } - - let ord = self.inner.canonical_url.cmp(&other.inner.canonical_url); - - match (&self.inner.kind, &other.inner.kind) { - (SourceKind::Git(_), SourceKind::Git(_)) => { - // In the pre-PR code we returned Ord here, - // so there is no chance that this commit has broken anything about this match arm. - } - _ => { - // In the pre-PR code we returned cmp of url here, so let's make sure that's the same. - assert_eq!(self.inner.url.cmp(&other.inner.url), ord); - // I am quite sure that this assert will never fire. - // In order for it to fire either `url`s are equal but `canonical_url`s are not, - // or the other way around. The algorithm for constructing a canonical URL is deterministic, - // so if it's given the same URL it will return the same canonical URL. - - // But what if we have two different URLs that canonical eyes the same? - // I assert that the second one would get thrown out when the second `SourceId` was interned. - // `SourceId::new` is the only way to make a `SourceId`. It allways construct them with - // `precise: None`. Furthermore, it uses `SourceId::wrap` to see if it has ever constructed - // a previous instance with a `SourceIdInner` that is `SourceIdInner::eq` with the one beeing added. - // `SourceIdInner::eq` only looks at `kind`, `precise`, and `canonical_url`. - // Proof by contradiction: If we had constructed two `SourceId` that: - // 1. have the same `kind` (check that a few lines ago) - // 2. have the same `precise` (as at construction time it is allways None) - // 3. have the same `canonical_url` (by the assumption of this paragraph) - // then the `ptr::eq` would return equal. Even if they were constructed with different `url`s, - // `SourceId::wrap` would have noticed that they `SourceIdInner::eq` - // and thus returned a pointer to the first one. - } - } - - ord + let ord_kind = self.inner.kind.cmp(&other.inner.kind); + ord_kind.then_with(|| self.inner.canonical_url.cmp(&other.inner.canonical_url)) } } From b018327fd4b8139c747dddc0b4937004d31b2233 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 24 Dec 2024 17:47:24 -0500 Subject: [PATCH 279/525] refactor(package): move cargo_package to a mod --- src/cargo/ops/{cargo_package.rs => cargo_package/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/cargo/ops/{cargo_package.rs => cargo_package/mod.rs} (100%) diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package/mod.rs similarity index 100% rename from src/cargo/ops/cargo_package.rs rename to src/cargo/ops/cargo_package/mod.rs From 3907e2d8b2de764ea90cd9cdaa3695c704b2c736 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 24 Dec 2024 21:04:41 -0500 Subject: [PATCH 280/525] refactor(package): extract vcs check to a separate module --- src/cargo/ops/cargo_package/mod.rs | 240 +--------------------------- src/cargo/ops/cargo_package/vcs.rs | 248 +++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 236 deletions(-) create mode 100644 src/cargo/ops/cargo_package/vcs.rs diff --git a/src/cargo/ops/cargo_package/mod.rs b/src/cargo/ops/cargo_package/mod.rs index 9c0b1d6ac43..df7e1f09f97 100644 --- a/src/cargo/ops/cargo_package/mod.rs +++ b/src/cargo/ops/cargo_package/mod.rs @@ -29,11 +29,14 @@ use anyhow::{bail, Context as _}; use cargo_util::paths; use flate2::read::GzDecoder; use flate2::{Compression, GzBuilder}; -use serde::Serialize; use tar::{Archive, Builder, EntryType, Header, HeaderMode}; use tracing::debug; use unicase::Ascii as UncasedAscii; +mod vcs; +use self::vcs::check_repo_state; +use self::vcs::VcsInfo; + #[derive(Clone)] pub struct PackageOpts<'gctx> { pub gctx: &'gctx GlobalContext, @@ -78,21 +81,6 @@ enum GeneratedFile { VcsInfo(VcsInfo), } -#[derive(Serialize)] -struct VcsInfo { - git: GitVcsInfo, - /// Path to the package within repo (empty string if root). / not \ - path_in_vcs: String, -} - -#[derive(Serialize)] -struct GitVcsInfo { - sha1: String, - /// Indicate whether or not the Git worktree is dirty. - #[serde(skip_serializing_if = "std::ops::Not::not")] - dirty: bool, -} - // Builds a tarball and places it in the output directory. #[tracing::instrument(skip_all)] fn create_package( @@ -728,226 +716,6 @@ fn check_metadata(pkg: &Package, gctx: &GlobalContext) -> CargoResult<()> { Ok(()) } -/// Checks if the package source is in a *git* DVCS repository. If *git*, and -/// the source is *dirty* (e.g., has uncommitted changes), and `--allow-dirty` -/// has not been passed, then `bail!` with an informative message. Otherwise -/// return the sha1 hash of the current *HEAD* commit, or `None` if no repo is -/// found. -#[tracing::instrument(skip_all)] -fn check_repo_state( - p: &Package, - src_files: &[PathBuf], - gctx: &GlobalContext, - opts: &PackageOpts<'_>, -) -> CargoResult> { - let Ok(repo) = git2::Repository::discover(p.root()) else { - gctx.shell().verbose(|shell| { - shell.warn(format!("no (git) VCS found for `{}`", p.root().display())) - })?; - // No Git repo found. Have to assume it is clean. - return Ok(None); - }; - - let Some(workdir) = repo.workdir() else { - debug!( - "no (git) workdir found for repo at `{}`", - repo.path().display() - ); - // No git workdir. Have to assume it is clean. - return Ok(None); - }; - - debug!("found a git repo at `{}`", workdir.display()); - let path = p.manifest_path(); - let path = paths::strip_prefix_canonical(path, workdir).unwrap_or_else(|_| path.to_path_buf()); - let Ok(status) = repo.status_file(&path) else { - gctx.shell().verbose(|shell| { - shell.warn(format!( - "no (git) Cargo.toml found at `{}` in workdir `{}`", - path.display(), - workdir.display() - )) - })?; - // No checked-in `Cargo.toml` found. This package may be irrelevant. - // Have to assume it is clean. - return Ok(None); - }; - - if !(status & git2::Status::IGNORED).is_empty() { - gctx.shell().verbose(|shell| { - shell.warn(format!( - "found (git) Cargo.toml ignored at `{}` in workdir `{}`", - path.display(), - workdir.display() - )) - })?; - // An ignored `Cargo.toml` found. This package may be irrelevant. - // Have to assume it is clean. - return Ok(None); - } - - debug!( - "found (git) Cargo.toml at `{}` in workdir `{}`", - path.display(), - workdir.display(), - ); - let path_in_vcs = path - .parent() - .and_then(|p| p.to_str()) - .unwrap_or("") - .replace("\\", "/"); - let Some(git) = git(p, gctx, src_files, &repo, &opts)? else { - // If the git repo lacks essensial field like `sha1`, and since this field exists from the beginning, - // then don't generate the corresponding file in order to maintain consistency with past behavior. - return Ok(None); - }; - - return Ok(Some(VcsInfo { git, path_in_vcs })); - - fn git( - pkg: &Package, - gctx: &GlobalContext, - src_files: &[PathBuf], - repo: &git2::Repository, - opts: &PackageOpts<'_>, - ) -> CargoResult> { - // This is a collection of any dirty or untracked files. This covers: - // - new/modified/deleted/renamed/type change (index or worktree) - // - untracked files (which are "new" worktree files) - // - ignored (in case the user has an `include` directive that - // conflicts with .gitignore). - let mut dirty_files = Vec::new(); - collect_statuses(repo, &mut dirty_files)?; - // Include each submodule so that the error message can provide - // specifically *which* files in a submodule are modified. - status_submodules(repo, &mut dirty_files)?; - - // Find the intersection of dirty in git, and the src_files that would - // be packaged. This is a lazy n^2 check, but seems fine with - // thousands of files. - let cwd = gctx.cwd(); - let mut dirty_src_files: Vec<_> = src_files - .iter() - .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path))) - .chain(dirty_metadata_paths(pkg, repo)?.iter()) - .map(|path| { - pathdiff::diff_paths(path, cwd) - .as_ref() - .unwrap_or(path) - .display() - .to_string() - }) - .collect(); - let dirty = !dirty_src_files.is_empty(); - if !dirty || opts.allow_dirty { - // Must check whetherthe repo has no commit firstly, otherwise `revparse_single` would fail on bare commit repo. - // Due to lacking the `sha1` field, it's better not record the `GitVcsInfo` for consistency. - if repo.is_empty()? { - return Ok(None); - } - let rev_obj = repo.revparse_single("HEAD")?; - Ok(Some(GitVcsInfo { - sha1: rev_obj.id().to_string(), - dirty, - })) - } else { - dirty_src_files.sort_unstable(); - anyhow::bail!( - "{} files in the working directory contain changes that were \ - not yet committed into git:\n\n{}\n\n\ - to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag", - dirty_src_files.len(), - dirty_src_files.join("\n") - ) - } - } - - /// Checks whether files at paths specified in `package.readme` and - /// `package.license-file` have been modified. - /// - /// This is required because those paths may link to a file outside the - /// current package root, but still under the git workdir, affecting the - /// final packaged `.crate` file. - fn dirty_metadata_paths(pkg: &Package, repo: &git2::Repository) -> CargoResult> { - let mut dirty_files = Vec::new(); - let workdir = repo.workdir().unwrap(); - let root = pkg.root(); - let meta = pkg.manifest().metadata(); - for path in [&meta.license_file, &meta.readme] { - let Some(path) = path.as_deref().map(Path::new) else { - continue; - }; - let abs_path = paths::normalize_path(&root.join(path)); - if paths::strip_prefix_canonical(abs_path.as_path(), root).is_ok() { - // Inside package root. Don't bother checking git status. - continue; - } - if let Ok(rel_path) = paths::strip_prefix_canonical(abs_path.as_path(), workdir) { - // Outside package root but under git workdir, - if repo.status_file(&rel_path)? != git2::Status::CURRENT { - dirty_files.push(if abs_path.is_symlink() { - // For symlinks, shows paths to symlink sources - workdir.join(rel_path) - } else { - abs_path - }); - } - } - } - Ok(dirty_files) - } - - // Helper to collect dirty statuses for a single repo. - fn collect_statuses( - repo: &git2::Repository, - dirty_files: &mut Vec, - ) -> CargoResult<()> { - let mut status_opts = git2::StatusOptions::new(); - // Exclude submodules, as they are being handled manually by recursing - // into each one so that details about specific files can be - // retrieved. - status_opts - .exclude_submodules(true) - .include_ignored(true) - .include_untracked(true); - let repo_statuses = repo.statuses(Some(&mut status_opts)).with_context(|| { - format!( - "failed to retrieve git status from repo {}", - repo.path().display() - ) - })?; - let workdir = repo.workdir().unwrap(); - let this_dirty = repo_statuses.iter().filter_map(|entry| { - let path = entry.path().expect("valid utf-8 path"); - if path.ends_with("Cargo.lock") && entry.status() == git2::Status::IGNORED { - // It is OK to include Cargo.lock even if it is ignored. - return None; - } - // Use an absolute path, so that comparing paths is easier - // (particularly with submodules). - Some(workdir.join(path)) - }); - dirty_files.extend(this_dirty); - Ok(()) - } - - // Helper to collect dirty statuses while recursing into submodules. - fn status_submodules( - repo: &git2::Repository, - dirty_files: &mut Vec, - ) -> CargoResult<()> { - for submodule in repo.submodules()? { - // Ignore submodules that don't open, they are probably not initialized. - // If its files are required, then the verification step should fail. - if let Ok(sub_repo) = submodule.open() { - status_submodules(&sub_repo, dirty_files)?; - collect_statuses(&sub_repo, dirty_files)?; - } - } - Ok(()) - } -} - /// Compresses and packages a list of [`ArchiveFile`]s and writes into the given file. /// /// Returns the uncompressed size of the contents of the new archive file. diff --git a/src/cargo/ops/cargo_package/vcs.rs b/src/cargo/ops/cargo_package/vcs.rs new file mode 100644 index 00000000000..33a82081e44 --- /dev/null +++ b/src/cargo/ops/cargo_package/vcs.rs @@ -0,0 +1,248 @@ +use std::path::Path; +use std::path::PathBuf; + +use anyhow::Context as _; +use cargo_util::paths; +use serde::Serialize; +use tracing::debug; + +use crate::core::Package; +use crate::CargoResult; +use crate::GlobalContext; + +use super::PackageOpts; + +#[derive(Serialize)] +pub struct VcsInfo { + git: GitVcsInfo, + /// Path to the package within repo (empty string if root). / not \ + path_in_vcs: String, +} + +#[derive(Serialize)] +pub struct GitVcsInfo { + sha1: String, + /// Indicate whether or not the Git worktree is dirty. + #[serde(skip_serializing_if = "std::ops::Not::not")] + dirty: bool, +} + +/// Checks if the package source is in a *git* DVCS repository. If *git*, and +/// the source is *dirty* (e.g., has uncommitted changes), and `--allow-dirty` +/// has not been passed, then `bail!` with an informative message. Otherwise +/// return the sha1 hash of the current *HEAD* commit, or `None` if no repo is +/// found. +#[tracing::instrument(skip_all)] +pub fn check_repo_state( + p: &Package, + src_files: &[PathBuf], + gctx: &GlobalContext, + opts: &PackageOpts<'_>, +) -> CargoResult> { + let Ok(repo) = git2::Repository::discover(p.root()) else { + gctx.shell().verbose(|shell| { + shell.warn(format!("no (git) VCS found for `{}`", p.root().display())) + })?; + // No Git repo found. Have to assume it is clean. + return Ok(None); + }; + + let Some(workdir) = repo.workdir() else { + debug!( + "no (git) workdir found for repo at `{}`", + repo.path().display() + ); + // No git workdir. Have to assume it is clean. + return Ok(None); + }; + + debug!("found a git repo at `{}`", workdir.display()); + let path = p.manifest_path(); + let path = paths::strip_prefix_canonical(path, workdir).unwrap_or_else(|_| path.to_path_buf()); + let Ok(status) = repo.status_file(&path) else { + gctx.shell().verbose(|shell| { + shell.warn(format!( + "no (git) Cargo.toml found at `{}` in workdir `{}`", + path.display(), + workdir.display() + )) + })?; + // No checked-in `Cargo.toml` found. This package may be irrelevant. + // Have to assume it is clean. + return Ok(None); + }; + + if !(status & git2::Status::IGNORED).is_empty() { + gctx.shell().verbose(|shell| { + shell.warn(format!( + "found (git) Cargo.toml ignored at `{}` in workdir `{}`", + path.display(), + workdir.display() + )) + })?; + // An ignored `Cargo.toml` found. This package may be irrelevant. + // Have to assume it is clean. + return Ok(None); + } + + debug!( + "found (git) Cargo.toml at `{}` in workdir `{}`", + path.display(), + workdir.display(), + ); + let path_in_vcs = path + .parent() + .and_then(|p| p.to_str()) + .unwrap_or("") + .replace("\\", "/"); + let Some(git) = git(p, gctx, src_files, &repo, &opts)? else { + // If the git repo lacks essensial field like `sha1`, and since this field exists from the beginning, + // then don't generate the corresponding file in order to maintain consistency with past behavior. + return Ok(None); + }; + + return Ok(Some(VcsInfo { git, path_in_vcs })); + + fn git( + pkg: &Package, + gctx: &GlobalContext, + src_files: &[PathBuf], + repo: &git2::Repository, + opts: &PackageOpts<'_>, + ) -> CargoResult> { + // This is a collection of any dirty or untracked files. This covers: + // - new/modified/deleted/renamed/type change (index or worktree) + // - untracked files (which are "new" worktree files) + // - ignored (in case the user has an `include` directive that + // conflicts with .gitignore). + let mut dirty_files = Vec::new(); + collect_statuses(repo, &mut dirty_files)?; + // Include each submodule so that the error message can provide + // specifically *which* files in a submodule are modified. + status_submodules(repo, &mut dirty_files)?; + + // Find the intersection of dirty in git, and the src_files that would + // be packaged. This is a lazy n^2 check, but seems fine with + // thousands of files. + let cwd = gctx.cwd(); + let mut dirty_src_files: Vec<_> = src_files + .iter() + .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path))) + .chain(dirty_metadata_paths(pkg, repo)?.iter()) + .map(|path| { + pathdiff::diff_paths(path, cwd) + .as_ref() + .unwrap_or(path) + .display() + .to_string() + }) + .collect(); + let dirty = !dirty_src_files.is_empty(); + if !dirty || opts.allow_dirty { + // Must check whetherthe repo has no commit firstly, otherwise `revparse_single` would fail on bare commit repo. + // Due to lacking the `sha1` field, it's better not record the `GitVcsInfo` for consistency. + if repo.is_empty()? { + return Ok(None); + } + let rev_obj = repo.revparse_single("HEAD")?; + Ok(Some(GitVcsInfo { + sha1: rev_obj.id().to_string(), + dirty, + })) + } else { + dirty_src_files.sort_unstable(); + anyhow::bail!( + "{} files in the working directory contain changes that were \ + not yet committed into git:\n\n{}\n\n\ + to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag", + dirty_src_files.len(), + dirty_src_files.join("\n") + ) + } + } + + /// Checks whether files at paths specified in `package.readme` and + /// `package.license-file` have been modified. + /// + /// This is required because those paths may link to a file outside the + /// current package root, but still under the git workdir, affecting the + /// final packaged `.crate` file. + fn dirty_metadata_paths(pkg: &Package, repo: &git2::Repository) -> CargoResult> { + let mut dirty_files = Vec::new(); + let workdir = repo.workdir().unwrap(); + let root = pkg.root(); + let meta = pkg.manifest().metadata(); + for path in [&meta.license_file, &meta.readme] { + let Some(path) = path.as_deref().map(Path::new) else { + continue; + }; + let abs_path = paths::normalize_path(&root.join(path)); + if paths::strip_prefix_canonical(abs_path.as_path(), root).is_ok() { + // Inside package root. Don't bother checking git status. + continue; + } + if let Ok(rel_path) = paths::strip_prefix_canonical(abs_path.as_path(), workdir) { + // Outside package root but under git workdir, + if repo.status_file(&rel_path)? != git2::Status::CURRENT { + dirty_files.push(if abs_path.is_symlink() { + // For symlinks, shows paths to symlink sources + workdir.join(rel_path) + } else { + abs_path + }); + } + } + } + Ok(dirty_files) + } + + // Helper to collect dirty statuses for a single repo. + fn collect_statuses( + repo: &git2::Repository, + dirty_files: &mut Vec, + ) -> CargoResult<()> { + let mut status_opts = git2::StatusOptions::new(); + // Exclude submodules, as they are being handled manually by recursing + // into each one so that details about specific files can be + // retrieved. + status_opts + .exclude_submodules(true) + .include_ignored(true) + .include_untracked(true); + let repo_statuses = repo.statuses(Some(&mut status_opts)).with_context(|| { + format!( + "failed to retrieve git status from repo {}", + repo.path().display() + ) + })?; + let workdir = repo.workdir().unwrap(); + let this_dirty = repo_statuses.iter().filter_map(|entry| { + let path = entry.path().expect("valid utf-8 path"); + if path.ends_with("Cargo.lock") && entry.status() == git2::Status::IGNORED { + // It is OK to include Cargo.lock even if it is ignored. + return None; + } + // Use an absolute path, so that comparing paths is easier + // (particularly with submodules). + Some(workdir.join(path)) + }); + dirty_files.extend(this_dirty); + Ok(()) + } + + // Helper to collect dirty statuses while recursing into submodules. + fn status_submodules( + repo: &git2::Repository, + dirty_files: &mut Vec, + ) -> CargoResult<()> { + for submodule in repo.submodules()? { + // Ignore submodules that don't open, they are probably not initialized. + // If its files are required, then the verification step should fail. + if let Ok(sub_repo) = submodule.open() { + status_submodules(&sub_repo, dirty_files)?; + collect_statuses(&sub_repo, dirty_files)?; + } + } + Ok(()) + } +} From 2f5788ae2e9d5b95be4b6706171367b33929c65e Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 24 Dec 2024 21:08:32 -0500 Subject: [PATCH 281/525] refactor(package): flatten nested functions in vcs check --- src/cargo/ops/cargo_package/mod.rs | 8 +- src/cargo/ops/cargo_package/vcs.rs | 258 ++++++++++++++--------------- 2 files changed, 129 insertions(+), 137 deletions(-) diff --git a/src/cargo/ops/cargo_package/mod.rs b/src/cargo/ops/cargo_package/mod.rs index df7e1f09f97..48ef4886f22 100644 --- a/src/cargo/ops/cargo_package/mod.rs +++ b/src/cargo/ops/cargo_package/mod.rs @@ -34,8 +34,6 @@ use tracing::debug; use unicase::Ascii as UncasedAscii; mod vcs; -use self::vcs::check_repo_state; -use self::vcs::VcsInfo; #[derive(Clone)] pub struct PackageOpts<'gctx> { @@ -78,7 +76,7 @@ enum GeneratedFile { /// Generates `Cargo.lock` in some cases (like if there is a binary). Lockfile, /// Adds a `.cargo_vcs_info.json` file if in a (clean) git repo. - VcsInfo(VcsInfo), + VcsInfo(vcs::VcsInfo), } // Builds a tarball and places it in the output directory. @@ -384,7 +382,7 @@ fn prepare_archive( let src_files = src.list_files(pkg)?; // Check (git) repository state, getting the current commit hash. - let vcs_info = check_repo_state(pkg, &src_files, gctx, &opts)?; + let vcs_info = vcs::check_repo_state(pkg, &src_files, gctx, &opts)?; build_ar_list(ws, pkg, src_files, vcs_info) } @@ -395,7 +393,7 @@ fn build_ar_list( ws: &Workspace<'_>, pkg: &Package, src_files: Vec, - vcs_info: Option, + vcs_info: Option, ) -> CargoResult> { let mut result = HashMap::new(); let root = pkg.root(); diff --git a/src/cargo/ops/cargo_package/vcs.rs b/src/cargo/ops/cargo_package/vcs.rs index 33a82081e44..168e69b0647 100644 --- a/src/cargo/ops/cargo_package/vcs.rs +++ b/src/cargo/ops/cargo_package/vcs.rs @@ -102,147 +102,141 @@ pub fn check_repo_state( }; return Ok(Some(VcsInfo { git, path_in_vcs })); +} - fn git( - pkg: &Package, - gctx: &GlobalContext, - src_files: &[PathBuf], - repo: &git2::Repository, - opts: &PackageOpts<'_>, - ) -> CargoResult> { - // This is a collection of any dirty or untracked files. This covers: - // - new/modified/deleted/renamed/type change (index or worktree) - // - untracked files (which are "new" worktree files) - // - ignored (in case the user has an `include` directive that - // conflicts with .gitignore). - let mut dirty_files = Vec::new(); - collect_statuses(repo, &mut dirty_files)?; - // Include each submodule so that the error message can provide - // specifically *which* files in a submodule are modified. - status_submodules(repo, &mut dirty_files)?; - - // Find the intersection of dirty in git, and the src_files that would - // be packaged. This is a lazy n^2 check, but seems fine with - // thousands of files. - let cwd = gctx.cwd(); - let mut dirty_src_files: Vec<_> = src_files - .iter() - .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path))) - .chain(dirty_metadata_paths(pkg, repo)?.iter()) - .map(|path| { - pathdiff::diff_paths(path, cwd) - .as_ref() - .unwrap_or(path) - .display() - .to_string() - }) - .collect(); - let dirty = !dirty_src_files.is_empty(); - if !dirty || opts.allow_dirty { - // Must check whetherthe repo has no commit firstly, otherwise `revparse_single` would fail on bare commit repo. - // Due to lacking the `sha1` field, it's better not record the `GitVcsInfo` for consistency. - if repo.is_empty()? { - return Ok(None); - } - let rev_obj = repo.revparse_single("HEAD")?; - Ok(Some(GitVcsInfo { - sha1: rev_obj.id().to_string(), - dirty, - })) - } else { - dirty_src_files.sort_unstable(); - anyhow::bail!( - "{} files in the working directory contain changes that were \ - not yet committed into git:\n\n{}\n\n\ - to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag", - dirty_src_files.len(), - dirty_src_files.join("\n") - ) +fn git( + pkg: &Package, + gctx: &GlobalContext, + src_files: &[PathBuf], + repo: &git2::Repository, + opts: &PackageOpts<'_>, +) -> CargoResult> { + // This is a collection of any dirty or untracked files. This covers: + // - new/modified/deleted/renamed/type change (index or worktree) + // - untracked files (which are "new" worktree files) + // - ignored (in case the user has an `include` directive that + // conflicts with .gitignore). + let mut dirty_files = Vec::new(); + collect_statuses(repo, &mut dirty_files)?; + // Include each submodule so that the error message can provide + // specifically *which* files in a submodule are modified. + status_submodules(repo, &mut dirty_files)?; + + // Find the intersection of dirty in git, and the src_files that would + // be packaged. This is a lazy n^2 check, but seems fine with + // thousands of files. + let cwd = gctx.cwd(); + let mut dirty_src_files: Vec<_> = src_files + .iter() + .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path))) + .chain(dirty_metadata_paths(pkg, repo)?.iter()) + .map(|path| { + pathdiff::diff_paths(path, cwd) + .as_ref() + .unwrap_or(path) + .display() + .to_string() + }) + .collect(); + let dirty = !dirty_src_files.is_empty(); + if !dirty || opts.allow_dirty { + // Must check whetherthe repo has no commit firstly, otherwise `revparse_single` would fail on bare commit repo. + // Due to lacking the `sha1` field, it's better not record the `GitVcsInfo` for consistency. + if repo.is_empty()? { + return Ok(None); } + let rev_obj = repo.revparse_single("HEAD")?; + Ok(Some(GitVcsInfo { + sha1: rev_obj.id().to_string(), + dirty, + })) + } else { + dirty_src_files.sort_unstable(); + anyhow::bail!( + "{} files in the working directory contain changes that were \ + not yet committed into git:\n\n{}\n\n\ + to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag", + dirty_src_files.len(), + dirty_src_files.join("\n") + ) } +} - /// Checks whether files at paths specified in `package.readme` and - /// `package.license-file` have been modified. - /// - /// This is required because those paths may link to a file outside the - /// current package root, but still under the git workdir, affecting the - /// final packaged `.crate` file. - fn dirty_metadata_paths(pkg: &Package, repo: &git2::Repository) -> CargoResult> { - let mut dirty_files = Vec::new(); - let workdir = repo.workdir().unwrap(); - let root = pkg.root(); - let meta = pkg.manifest().metadata(); - for path in [&meta.license_file, &meta.readme] { - let Some(path) = path.as_deref().map(Path::new) else { - continue; - }; - let abs_path = paths::normalize_path(&root.join(path)); - if paths::strip_prefix_canonical(abs_path.as_path(), root).is_ok() { - // Inside package root. Don't bother checking git status. - continue; - } - if let Ok(rel_path) = paths::strip_prefix_canonical(abs_path.as_path(), workdir) { - // Outside package root but under git workdir, - if repo.status_file(&rel_path)? != git2::Status::CURRENT { - dirty_files.push(if abs_path.is_symlink() { - // For symlinks, shows paths to symlink sources - workdir.join(rel_path) - } else { - abs_path - }); - } +/// Checks whether files at paths specified in `package.readme` and +/// `package.license-file` have been modified. +/// +/// This is required because those paths may link to a file outside the +/// current package root, but still under the git workdir, affecting the +/// final packaged `.crate` file. +fn dirty_metadata_paths(pkg: &Package, repo: &git2::Repository) -> CargoResult> { + let mut dirty_files = Vec::new(); + let workdir = repo.workdir().unwrap(); + let root = pkg.root(); + let meta = pkg.manifest().metadata(); + for path in [&meta.license_file, &meta.readme] { + let Some(path) = path.as_deref().map(Path::new) else { + continue; + }; + let abs_path = paths::normalize_path(&root.join(path)); + if paths::strip_prefix_canonical(abs_path.as_path(), root).is_ok() { + // Inside package root. Don't bother checking git status. + continue; + } + if let Ok(rel_path) = paths::strip_prefix_canonical(abs_path.as_path(), workdir) { + // Outside package root but under git workdir, + if repo.status_file(&rel_path)? != git2::Status::CURRENT { + dirty_files.push(if abs_path.is_symlink() { + // For symlinks, shows paths to symlink sources + workdir.join(rel_path) + } else { + abs_path + }); } } - Ok(dirty_files) } + Ok(dirty_files) +} - // Helper to collect dirty statuses for a single repo. - fn collect_statuses( - repo: &git2::Repository, - dirty_files: &mut Vec, - ) -> CargoResult<()> { - let mut status_opts = git2::StatusOptions::new(); - // Exclude submodules, as they are being handled manually by recursing - // into each one so that details about specific files can be - // retrieved. - status_opts - .exclude_submodules(true) - .include_ignored(true) - .include_untracked(true); - let repo_statuses = repo.statuses(Some(&mut status_opts)).with_context(|| { - format!( - "failed to retrieve git status from repo {}", - repo.path().display() - ) - })?; - let workdir = repo.workdir().unwrap(); - let this_dirty = repo_statuses.iter().filter_map(|entry| { - let path = entry.path().expect("valid utf-8 path"); - if path.ends_with("Cargo.lock") && entry.status() == git2::Status::IGNORED { - // It is OK to include Cargo.lock even if it is ignored. - return None; - } - // Use an absolute path, so that comparing paths is easier - // (particularly with submodules). - Some(workdir.join(path)) - }); - dirty_files.extend(this_dirty); - Ok(()) - } +/// Helper to collect dirty statuses for a single repo. +fn collect_statuses(repo: &git2::Repository, dirty_files: &mut Vec) -> CargoResult<()> { + let mut status_opts = git2::StatusOptions::new(); + // Exclude submodules, as they are being handled manually by recursing + // into each one so that details about specific files can be + // retrieved. + status_opts + .exclude_submodules(true) + .include_ignored(true) + .include_untracked(true); + let repo_statuses = repo.statuses(Some(&mut status_opts)).with_context(|| { + format!( + "failed to retrieve git status from repo {}", + repo.path().display() + ) + })?; + let workdir = repo.workdir().unwrap(); + let this_dirty = repo_statuses.iter().filter_map(|entry| { + let path = entry.path().expect("valid utf-8 path"); + if path.ends_with("Cargo.lock") && entry.status() == git2::Status::IGNORED { + // It is OK to include Cargo.lock even if it is ignored. + return None; + } + // Use an absolute path, so that comparing paths is easier + // (particularly with submodules). + Some(workdir.join(path)) + }); + dirty_files.extend(this_dirty); + Ok(()) +} - // Helper to collect dirty statuses while recursing into submodules. - fn status_submodules( - repo: &git2::Repository, - dirty_files: &mut Vec, - ) -> CargoResult<()> { - for submodule in repo.submodules()? { - // Ignore submodules that don't open, they are probably not initialized. - // If its files are required, then the verification step should fail. - if let Ok(sub_repo) = submodule.open() { - status_submodules(&sub_repo, dirty_files)?; - collect_statuses(&sub_repo, dirty_files)?; - } +/// Helper to collect dirty statuses while recursing into submodules. +fn status_submodules(repo: &git2::Repository, dirty_files: &mut Vec) -> CargoResult<()> { + for submodule in repo.submodules()? { + // Ignore submodules that don't open, they are probably not initialized. + // If its files are required, then the verification step should fail. + if let Ok(sub_repo) = submodule.open() { + status_submodules(&sub_repo, dirty_files)?; + collect_statuses(&sub_repo, dirty_files)?; } - Ok(()) } + Ok(()) } From b033b977c6c95f825a61c237d4be38ca00ccc99d Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 24 Dec 2024 21:32:42 -0500 Subject: [PATCH 282/525] refactor(package): extract verification code --- src/cargo/ops/cargo_package/mod.rs | 176 ++---------------------- src/cargo/ops/cargo_package/verify.rs | 184 ++++++++++++++++++++++++++ 2 files changed, 197 insertions(+), 163 deletions(-) create mode 100644 src/cargo/ops/cargo_package/verify.rs diff --git a/src/cargo/ops/cargo_package/mod.rs b/src/cargo/ops/cargo_package/mod.rs index 48ef4886f22..b6bbc58f595 100644 --- a/src/cargo/ops/cargo_package/mod.rs +++ b/src/cargo/ops/cargo_package/mod.rs @@ -3,15 +3,16 @@ use std::fs::{self, File}; use std::io::prelude::*; use std::io::SeekFrom; use std::path::{Path, PathBuf}; -use std::sync::Arc; use std::task::Poll; -use crate::core::compiler::{BuildConfig, CompileMode, DefaultExecutor, Executor}; use crate::core::dependency::DepKind; use crate::core::manifest::Target; use crate::core::resolver::CliFeatures; use crate::core::resolver::HasDevUnits; -use crate::core::{Feature, PackageIdSpecQuery, Shell, Verbosity, Workspace}; +use crate::core::PackageIdSpecQuery; +use crate::core::Shell; +use crate::core::Verbosity; +use crate::core::Workspace; use crate::core::{Package, PackageId, PackageSet, Resolve, SourceId}; use crate::ops::lockfile::LOCKFILE_NAME; use crate::ops::registry::{infer_registry, RegistryOrIndex}; @@ -20,20 +21,23 @@ use crate::sources::{PathSource, CRATES_IO_REGISTRY}; use crate::util::cache_lock::CacheLockMode; use crate::util::context::JobsConfig; use crate::util::errors::CargoResult; +use crate::util::human_readable_bytes; +use crate::util::restricted_names; use crate::util::toml::prepare_for_publish; -use crate::util::{ - self, human_readable_bytes, restricted_names, FileLock, Filesystem, GlobalContext, Graph, -}; +use crate::util::FileLock; +use crate::util::Filesystem; +use crate::util::GlobalContext; +use crate::util::Graph; use crate::{drop_println, ops}; use anyhow::{bail, Context as _}; use cargo_util::paths; -use flate2::read::GzDecoder; use flate2::{Compression, GzBuilder}; -use tar::{Archive, Builder, EntryType, Header, HeaderMode}; +use tar::{Builder, EntryType, Header, HeaderMode}; use tracing::debug; use unicase::Ascii as UncasedAscii; mod vcs; +mod verify; #[derive(Clone)] pub struct PackageOpts<'gctx> { @@ -250,7 +254,7 @@ fn do_package<'a>( // are already all in the local registry overlay. if opts.verify { for (pkg, opts, tarball) in &outputs { - run_verify(ws, pkg, tarball, local_reg.as_ref(), opts) + verify::run_verify(ws, pkg, tarball, local_reg.as_ref(), opts) .context("failed to verify package tarball")? } } @@ -926,160 +930,6 @@ pub fn check_yanked( Ok(()) } -fn run_verify( - ws: &Workspace<'_>, - pkg: &Package, - tar: &FileLock, - local_reg: Option<&TmpRegistry<'_>>, - opts: &PackageOpts<'_>, -) -> CargoResult<()> { - let gctx = ws.gctx(); - - gctx.shell().status("Verifying", pkg)?; - - tar.file().seek(SeekFrom::Start(0))?; - let f = GzDecoder::new(tar.file()); - let dst = tar - .parent() - .join(&format!("{}-{}", pkg.name(), pkg.version())); - if dst.exists() { - paths::remove_dir_all(&dst)?; - } - let mut archive = Archive::new(f); - // We don't need to set the Modified Time, as it's not relevant to verification - // and it errors on filesystems that don't support setting a modified timestamp - archive.set_preserve_mtime(false); - archive.unpack(dst.parent().unwrap())?; - - // Manufacture an ephemeral workspace to ensure that even if the top-level - // package has a workspace we can still build our new crate. - let id = SourceId::for_path(&dst)?; - let mut src = PathSource::new(&dst, id, ws.gctx()); - let new_pkg = src.root_package()?; - let pkg_fingerprint = hash_all(&dst)?; - let mut ws = Workspace::ephemeral(new_pkg, gctx, None, true)?; - if let Some(local_reg) = local_reg { - ws.add_local_overlay( - local_reg.upstream, - local_reg.root.as_path_unlocked().to_owned(), - ); - } - - let rustc_args = if pkg - .manifest() - .unstable_features() - .require(Feature::public_dependency()) - .is_ok() - || ws.gctx().cli_unstable().public_dependency - { - // FIXME: Turn this on at some point in the future - //Some(vec!["-D exported_private_dependencies".to_string()]) - Some(vec![]) - } else { - None - }; - - let exec: Arc = Arc::new(DefaultExecutor); - ops::compile_with_exec( - &ws, - &ops::CompileOptions { - build_config: BuildConfig::new( - gctx, - opts.jobs.clone(), - opts.keep_going, - &opts.targets, - CompileMode::Build, - )?, - cli_features: opts.cli_features.clone(), - spec: ops::Packages::Packages(Vec::new()), - filter: ops::CompileFilter::Default { - required_features_filterable: true, - }, - target_rustdoc_args: None, - target_rustc_args: rustc_args, - target_rustc_crate_types: None, - rustdoc_document_private_items: false, - honor_rust_version: None, - }, - &exec, - )?; - - // Check that `build.rs` didn't modify any files in the `src` directory. - let ws_fingerprint = hash_all(&dst)?; - if pkg_fingerprint != ws_fingerprint { - let changes = report_hash_difference(&pkg_fingerprint, &ws_fingerprint); - anyhow::bail!( - "Source directory was modified by build.rs during cargo publish. \ - Build scripts should not modify anything outside of OUT_DIR.\n\ - {}\n\n\ - To proceed despite this, pass the `--no-verify` flag.", - changes - ) - } - - Ok(()) -} - -fn hash_all(path: &Path) -> CargoResult> { - fn wrap(path: &Path) -> CargoResult> { - let mut result = HashMap::new(); - let walker = walkdir::WalkDir::new(path).into_iter(); - for entry in walker.filter_entry(|e| !(e.depth() == 1 && e.file_name() == "target")) { - let entry = entry?; - let file_type = entry.file_type(); - if file_type.is_file() { - let file = File::open(entry.path())?; - let hash = util::hex::hash_u64_file(&file)?; - result.insert(entry.path().to_path_buf(), hash); - } else if file_type.is_symlink() { - let hash = util::hex::hash_u64(&fs::read_link(entry.path())?); - result.insert(entry.path().to_path_buf(), hash); - } else if file_type.is_dir() { - let hash = util::hex::hash_u64(&()); - result.insert(entry.path().to_path_buf(), hash); - } - } - Ok(result) - } - let result = wrap(path).with_context(|| format!("failed to verify output at {:?}", path))?; - Ok(result) -} - -fn report_hash_difference(orig: &HashMap, after: &HashMap) -> String { - let mut changed = Vec::new(); - let mut removed = Vec::new(); - for (key, value) in orig { - match after.get(key) { - Some(after_value) => { - if value != after_value { - changed.push(key.to_string_lossy()); - } - } - None => removed.push(key.to_string_lossy()), - } - } - let mut added: Vec<_> = after - .keys() - .filter(|key| !orig.contains_key(*key)) - .map(|key| key.to_string_lossy()) - .collect(); - let mut result = Vec::new(); - if !changed.is_empty() { - changed.sort_unstable(); - result.push(format!("Changed: {}", changed.join("\n\t"))); - } - if !added.is_empty() { - added.sort_unstable(); - result.push(format!("Added: {}", added.join("\n\t"))); - } - if !removed.is_empty() { - removed.sort_unstable(); - result.push(format!("Removed: {}", removed.join("\n\t"))); - } - assert!(!result.is_empty(), "unexpected empty change detection"); - result.join("\n") -} - // It can often be the case that files of a particular name on one platform // can't actually be created on another platform. For example files with colons // in the name are allowed on Unix but not on Windows. diff --git a/src/cargo/ops/cargo_package/verify.rs b/src/cargo/ops/cargo_package/verify.rs new file mode 100644 index 00000000000..f7668b39612 --- /dev/null +++ b/src/cargo/ops/cargo_package/verify.rs @@ -0,0 +1,184 @@ +use std::collections::HashMap; +use std::fs; +use std::fs::File; +use std::io::prelude::*; +use std::io::SeekFrom; +use std::path::Path; +use std::path::PathBuf; +use std::sync::Arc; + +use anyhow::Context as _; +use cargo_util::paths; +use flate2::read::GzDecoder; +use tar::Archive; + +use crate::core::compiler::BuildConfig; +use crate::core::compiler::CompileMode; +use crate::core::compiler::DefaultExecutor; +use crate::core::compiler::Executor; +use crate::core::Feature; +use crate::core::Package; +use crate::core::SourceId; +use crate::core::Workspace; +use crate::ops; +use crate::sources::PathSource; +use crate::util; +use crate::util::FileLock; +use crate::CargoResult; + +use super::PackageOpts; +use super::TmpRegistry; + +pub fn run_verify( + ws: &Workspace<'_>, + pkg: &Package, + tar: &FileLock, + local_reg: Option<&TmpRegistry<'_>>, + opts: &PackageOpts<'_>, +) -> CargoResult<()> { + let gctx = ws.gctx(); + + gctx.shell().status("Verifying", pkg)?; + + tar.file().seek(SeekFrom::Start(0))?; + let f = GzDecoder::new(tar.file()); + let dst = tar + .parent() + .join(&format!("{}-{}", pkg.name(), pkg.version())); + if dst.exists() { + paths::remove_dir_all(&dst)?; + } + let mut archive = Archive::new(f); + // We don't need to set the Modified Time, as it's not relevant to verification + // and it errors on filesystems that don't support setting a modified timestamp + archive.set_preserve_mtime(false); + archive.unpack(dst.parent().unwrap())?; + + // Manufacture an ephemeral workspace to ensure that even if the top-level + // package has a workspace we can still build our new crate. + let id = SourceId::for_path(&dst)?; + let mut src = PathSource::new(&dst, id, ws.gctx()); + let new_pkg = src.root_package()?; + let pkg_fingerprint = hash_all(&dst)?; + let mut ws = Workspace::ephemeral(new_pkg, gctx, None, true)?; + if let Some(local_reg) = local_reg { + ws.add_local_overlay( + local_reg.upstream, + local_reg.root.as_path_unlocked().to_owned(), + ); + } + + let rustc_args = if pkg + .manifest() + .unstable_features() + .require(Feature::public_dependency()) + .is_ok() + || ws.gctx().cli_unstable().public_dependency + { + // FIXME: Turn this on at some point in the future + //Some(vec!["-D exported_private_dependencies".to_string()]) + Some(vec![]) + } else { + None + }; + + let exec: Arc = Arc::new(DefaultExecutor); + ops::compile_with_exec( + &ws, + &ops::CompileOptions { + build_config: BuildConfig::new( + gctx, + opts.jobs.clone(), + opts.keep_going, + &opts.targets, + CompileMode::Build, + )?, + cli_features: opts.cli_features.clone(), + spec: ops::Packages::Packages(Vec::new()), + filter: ops::CompileFilter::Default { + required_features_filterable: true, + }, + target_rustdoc_args: None, + target_rustc_args: rustc_args, + target_rustc_crate_types: None, + rustdoc_document_private_items: false, + honor_rust_version: None, + }, + &exec, + )?; + + // Check that `build.rs` didn't modify any files in the `src` directory. + let ws_fingerprint = hash_all(&dst)?; + if pkg_fingerprint != ws_fingerprint { + let changes = report_hash_difference(&pkg_fingerprint, &ws_fingerprint); + anyhow::bail!( + "Source directory was modified by build.rs during cargo publish. \ + Build scripts should not modify anything outside of OUT_DIR.\n\ + {}\n\n\ + To proceed despite this, pass the `--no-verify` flag.", + changes + ) + } + + Ok(()) +} + +fn hash_all(path: &Path) -> CargoResult> { + fn wrap(path: &Path) -> CargoResult> { + let mut result = HashMap::new(); + let walker = walkdir::WalkDir::new(path).into_iter(); + for entry in walker.filter_entry(|e| !(e.depth() == 1 && e.file_name() == "target")) { + let entry = entry?; + let file_type = entry.file_type(); + if file_type.is_file() { + let file = File::open(entry.path())?; + let hash = util::hex::hash_u64_file(&file)?; + result.insert(entry.path().to_path_buf(), hash); + } else if file_type.is_symlink() { + let hash = util::hex::hash_u64(&fs::read_link(entry.path())?); + result.insert(entry.path().to_path_buf(), hash); + } else if file_type.is_dir() { + let hash = util::hex::hash_u64(&()); + result.insert(entry.path().to_path_buf(), hash); + } + } + Ok(result) + } + let result = wrap(path).with_context(|| format!("failed to verify output at {:?}", path))?; + Ok(result) +} + +fn report_hash_difference(orig: &HashMap, after: &HashMap) -> String { + let mut changed = Vec::new(); + let mut removed = Vec::new(); + for (key, value) in orig { + match after.get(key) { + Some(after_value) => { + if value != after_value { + changed.push(key.to_string_lossy()); + } + } + None => removed.push(key.to_string_lossy()), + } + } + let mut added: Vec<_> = after + .keys() + .filter(|key| !orig.contains_key(*key)) + .map(|key| key.to_string_lossy()) + .collect(); + let mut result = Vec::new(); + if !changed.is_empty() { + changed.sort_unstable(); + result.push(format!("Changed: {}", changed.join("\n\t"))); + } + if !added.is_empty() { + added.sort_unstable(); + result.push(format!("Added: {}", added.join("\n\t"))); + } + if !removed.is_empty() { + removed.sort_unstable(); + result.push(format!("Removed: {}", removed.join("\n\t"))); + } + assert!(!result.is_empty(), "unexpected empty change detection"); + result.join("\n") +} From 5b14e6e5d243375c38ba0b929920e119611bf6d9 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 24 Dec 2024 21:15:05 -0500 Subject: [PATCH 283/525] refactor(package): add comments --- src/cargo/ops/cargo_package/mod.rs | 2 +- src/cargo/ops/cargo_package/vcs.rs | 19 +++++++++++++------ src/cargo/ops/cargo_package/verify.rs | 9 +++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/cargo/ops/cargo_package/mod.rs b/src/cargo/ops/cargo_package/mod.rs index b6bbc58f595..07ba919c98a 100644 --- a/src/cargo/ops/cargo_package/mod.rs +++ b/src/cargo/ops/cargo_package/mod.rs @@ -79,7 +79,7 @@ enum GeneratedFile { Manifest, /// Generates `Cargo.lock` in some cases (like if there is a binary). Lockfile, - /// Adds a `.cargo_vcs_info.json` file if in a (clean) git repo. + /// Adds a `.cargo_vcs_info.json` file if in a git repo. VcsInfo(vcs::VcsInfo), } diff --git a/src/cargo/ops/cargo_package/vcs.rs b/src/cargo/ops/cargo_package/vcs.rs index 168e69b0647..44adfd85a1a 100644 --- a/src/cargo/ops/cargo_package/vcs.rs +++ b/src/cargo/ops/cargo_package/vcs.rs @@ -1,3 +1,5 @@ +//! Helpers to gather the VCS information for `cargo package`. + use std::path::Path; use std::path::PathBuf; @@ -12,13 +14,15 @@ use crate::GlobalContext; use super::PackageOpts; +/// Represents the VCS information when packaging. #[derive(Serialize)] pub struct VcsInfo { git: GitVcsInfo, - /// Path to the package within repo (empty string if root). / not \ + /// Path to the package within repo (empty string if root). path_in_vcs: String, } +/// Represents the Git VCS information when packaging. #[derive(Serialize)] pub struct GitVcsInfo { sha1: String, @@ -27,11 +31,13 @@ pub struct GitVcsInfo { dirty: bool, } -/// Checks if the package source is in a *git* DVCS repository. If *git*, and -/// the source is *dirty* (e.g., has uncommitted changes), and `--allow-dirty` -/// has not been passed, then `bail!` with an informative message. Otherwise -/// return the sha1 hash of the current *HEAD* commit, or `None` if no repo is -/// found. +/// Checks if the package source is in a *git* DVCS repository. +/// +/// If *git*, and the source is *dirty* (e.g., has uncommitted changes), +/// and `--allow-dirty` has not been passed, +/// then `bail!` with an informative message. +/// Otherwise return the sha1 hash of the current *HEAD* commit, +/// or `None` if no repo is found. #[tracing::instrument(skip_all)] pub fn check_repo_state( p: &Package, @@ -104,6 +110,7 @@ pub fn check_repo_state( return Ok(Some(VcsInfo { git, path_in_vcs })); } +/// The real git status check starts from here. fn git( pkg: &Package, gctx: &GlobalContext, diff --git a/src/cargo/ops/cargo_package/verify.rs b/src/cargo/ops/cargo_package/verify.rs index f7668b39612..794e5d30581 100644 --- a/src/cargo/ops/cargo_package/verify.rs +++ b/src/cargo/ops/cargo_package/verify.rs @@ -1,3 +1,5 @@ +//! Helpers to verify a packaged `.crate` file. + use std::collections::HashMap; use std::fs; use std::fs::File; @@ -29,6 +31,7 @@ use crate::CargoResult; use super::PackageOpts; use super::TmpRegistry; +/// Verifies whether a `.crate` file is able to compile. pub fn run_verify( ws: &Workspace<'_>, pkg: &Package, @@ -123,6 +126,11 @@ pub fn run_verify( Ok(()) } +/// Hashes everything under a given directory. +/// +/// This is for checking if any source file inside a `.crate` file has changed +/// durint the compilation. It is usually caused by bad build scripts or proc +/// macros trying to modify source files. Cargo disallows that. fn hash_all(path: &Path) -> CargoResult> { fn wrap(path: &Path) -> CargoResult> { let mut result = HashMap::new(); @@ -148,6 +156,7 @@ fn hash_all(path: &Path) -> CargoResult> { Ok(result) } +/// Reports the hash difference before and after the compilation computed by [`hash_all`]. fn report_hash_difference(orig: &HashMap, after: &HashMap) -> String { let mut changed = Vec::new(); let mut removed = Vec::new(); From dac06bfc883706eea10ad6a9d5b82c0190d3a786 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 24 Dec 2024 21:57:47 -0500 Subject: [PATCH 284/525] chore: update autolabel --- triagebot.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/triagebot.toml b/triagebot.toml index 35256be5866..196c7ee6092 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -141,7 +141,7 @@ trigger_files = ["src/cargo/util/flock.rs", "src/cargo/util/important_paths.rs"] trigger_files = ["src/cargo/core/compiler/future_incompat.rs"] [autolabel."A-git"] -trigger_files = ["src/cargo/sources/git/"] +trigger_files = ["src/cargo/sources/git/", "src/cargo/ops/cargo_package/vcs.rs"] [autolabel."A-home"] trigger_files = ["crates/home/"] @@ -309,7 +309,7 @@ trigger_files = ["src/bin/cargo/commands/new.rs", "src/cargo/ops/cargo_new.rs"] trigger_files = ["src/bin/cargo/commands/owner.rs", "src/cargo/ops/registry/owner.rs"] [autolabel."Command-package"] -trigger_files = ["src/bin/cargo/commands/package.rs", "src/cargo/ops/cargo_package.rs"] +trigger_files = ["src/bin/cargo/commands/package.rs", "src/cargo/ops/cargo_package/"] [autolabel."Command-pkgid"] trigger_files = ["src/bin/cargo/commands/pkgid.rs", "src/cargo/ops/cargo_pkgid.rs"] From b7d98618b6d9e0dcd7cd7e010b42145fb041d02c Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sun, 29 Dec 2024 13:30:55 -0500 Subject: [PATCH 285/525] test: relax panic output assertion rust-lang/rust#122565 adds a new line to thread panic output. To make the current test suites works on stable, beta, and nightly, similar to rust-lang/cargo#14602, this relaxes the assertion around that by globbing everything. --- tests/build-std/main.rs | 1 + tests/testsuite/build_script.rs | 18 ++++++++---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/build-std/main.rs b/tests/build-std/main.rs index 976fc7e141b..ffe9f426492 100644 --- a/tests/build-std/main.rs +++ b/tests/build-std/main.rs @@ -376,6 +376,7 @@ fn remap_path_scope() { str![[r#" [FINISHED] `release` profile [optimized + debuginfo] target(s) in [ELAPSED]s [RUNNING] `target/[HOST_TARGET]/release/foo` +... [..]thread '[..]' panicked at [..]src/main.rs:3:[..]: [..]remap to /rustc/[..] [..]at /rustc/[..]/library/std/src/[..] diff --git a/tests/testsuite/build_script.rs b/tests/testsuite/build_script.rs index 78af30351ca..280575ec6a2 100644 --- a/tests/testsuite/build_script.rs +++ b/tests/testsuite/build_script.rs @@ -4000,7 +4000,7 @@ fn warnings_emitted_when_build_script_panics() { fn main() { println!("cargo::warning=foo"); println!("cargo::warning=bar"); - panic!(); + panic!("our crate panicked"); } "#, ) @@ -4022,10 +4022,9 @@ Caused by: cargo::warning=bar --- stderr - thread 'main' panicked at build.rs:5:21: - explicit panic - [NOTE] run with `RUST_BACKTRACE=1` environment variable to display a backtrace - +... +[..]our crate panicked[..] +... "#]]) .run(); } @@ -4039,7 +4038,7 @@ fn warnings_emitted_when_dependency_panics() { fn main() { println!("cargo::warning=foo"); println!("cargo::warning=bar"); - panic!(); + panic!("dependency panicked"); } "#, ) @@ -4093,10 +4092,9 @@ Caused by: cargo::warning=bar --- stderr - thread 'main' panicked at [ROOT]/home/.cargo/registry/src/-[HASH]/published-0.1.0/build.rs:5:21: - explicit panic - [NOTE] run with `RUST_BACKTRACE=1` environment variable to display a backtrace - +... +[..]dependency panicked[..] +... "#]]) .run(); } From 17aaafd92fe5e471f5210b3b75534ed23fddfed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AE=B8=E6=9D=B0=E5=8F=8B=20Jieyou=20Xu=20=28Joe=29?= <39484203+jieyouxu@users.noreply.github.com> Date: Mon, 30 Dec 2024 04:16:02 +0800 Subject: [PATCH 286/525] tests: relax `bad_crate_type` to only match error message prefix So that the cargo test isn't sensitive to suggestions for known crate types that a rustc PR is trying to add. --- tests/testsuite/bad_config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testsuite/bad_config.rs b/tests/testsuite/bad_config.rs index bf1b0da2a34..0fb00f8e8fa 100644 --- a/tests/testsuite/bad_config.rs +++ b/tests/testsuite/bad_config.rs @@ -427,7 +427,7 @@ fn bad_crate_type() { Caused by: process didn't exit successfully: `rustc - --crate-name ___ --print=file-names --crate-type bad_type` ([EXIT_STATUS]: 1) --- stderr - [ERROR] unknown crate type: `bad_type` + [ERROR] unknown crate type: `bad_type`[..] "#]]) From 562e83a443d70b26ec395c08f39e8e61ed945aab Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Mon, 30 Dec 2024 09:44:02 -0500 Subject: [PATCH 287/525] test: track caller for `.crate` file publish verification This was found during some recent works around `cargo package`. The purpose of it is showing the caller's line number when panicking. --- crates/cargo-test-support/src/publish.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/cargo-test-support/src/publish.rs b/crates/cargo-test-support/src/publish.rs index f6fde1b9805..b17f0a92217 100644 --- a/crates/cargo-test-support/src/publish.rs +++ b/crates/cargo-test-support/src/publish.rs @@ -78,6 +78,7 @@ where } /// Check the `cargo publish` API call +#[track_caller] pub fn validate_upload(expected_json: &str, expected_crate_name: &str, expected_files: &[&str]) { let new_path = registry::api_path().join("api/v1/crates/new"); _validate_upload( @@ -90,6 +91,7 @@ pub fn validate_upload(expected_json: &str, expected_crate_name: &str, expected_ } /// Check the `cargo publish` API call, with file contents +#[track_caller] pub fn validate_upload_with_contents( expected_json: &str, expected_crate_name: &str, @@ -107,6 +109,7 @@ pub fn validate_upload_with_contents( } /// Check the `cargo publish` API call to the alternative test registry +#[track_caller] pub fn validate_alt_upload( expected_json: &str, expected_crate_name: &str, @@ -122,6 +125,7 @@ pub fn validate_alt_upload( ); } +#[track_caller] fn _validate_upload( new_path: &Path, expected_json: &str, @@ -142,6 +146,7 @@ fn _validate_upload( ); } +#[track_caller] fn read_new_post(new_path: &Path) -> (Vec, Vec) { let mut f = File::open(new_path).unwrap(); @@ -170,6 +175,7 @@ fn read_new_post(new_path: &Path) -> (Vec, Vec) { /// - `expected_contents` should be a list of `(file_name, contents)` tuples /// to validate the contents of the given file. Only the listed files will /// be checked (others will be ignored). +#[track_caller] pub fn validate_crate_contents( reader: impl Read, expected_crate_name: &str, @@ -185,6 +191,7 @@ pub fn validate_crate_contents( ) } +#[track_caller] fn validate_crate_contents_( reader: impl Read, expected_crate_name: &str, @@ -192,10 +199,11 @@ fn validate_crate_contents_( expected_contents: InMemoryDir, ) { let mut rdr = GzDecoder::new(reader); - assert_eq!( - rdr.header().unwrap().filename().unwrap(), - expected_crate_name.as_bytes() - ); + snapbox::assert_data_eq!(rdr.header().unwrap().filename().unwrap(), { + let expected: snapbox::Data = expected_crate_name.into(); + expected.raw() + }); + let mut contents = Vec::new(); rdr.read_to_end(&mut contents).unwrap(); let mut ar = Archive::new(&contents[..]); From a014fd0814c3f513c52eb84f70949d89e681084b Mon Sep 17 00:00:00 2001 From: Lin Yihai Date: Wed, 30 Oct 2024 11:15:47 +0800 Subject: [PATCH 288/525] test: add test for `rerun-if-env-changed` custom build script. --- tests/testsuite/build_script_env.rs | 67 +++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/tests/testsuite/build_script_env.rs b/tests/testsuite/build_script_env.rs index 2c6b984b070..426c815e28c 100644 --- a/tests/testsuite/build_script_env.rs +++ b/tests/testsuite/build_script_env.rs @@ -383,3 +383,70 @@ fn rustc_cfg_with_and_without_value() { ); check.run(); } + +#[cargo_test] +fn rerun_if_env_is_exsited_config() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#" + fn main() { + println!("cargo::rerun-if-env-changed=FOO"); + } + "#, + ) + .file( + ".cargo/config.toml", + r#" + [env] + FOO = "foo" + "#, + ) + .build(); + + p.cargo("check") + .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo(r#"check --config 'env.FOO="bar"'"#) + .with_stderr_data(str![[r#" +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} + +#[cargo_test] +fn rerun_if_env_newly_added_in_config() { + let p = project() + .file("src/main.rs", "fn main() {}") + .file( + "build.rs", + r#" + fn main() { + println!("cargo::rerun-if-env-changed=FOO"); + } + "#, + ) + .build(); + + p.cargo("check") + .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + p.cargo(r#"check --config 'env.FOO="foo"'"#) + .with_stderr_data(str![[r#" +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} From 1df629b2fa126f6fad984e3a42a076346fe1ea25 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sat, 28 Dec 2024 18:52:22 -0500 Subject: [PATCH 289/525] refactor(source): wrap `PathBuf` with `PathEntry` This gives us more room to store file metadata. For example, * knowing a source file is a symlink and resolving it when packaging, * providing a rich JSON output for `cargo package --list` * enriching the `.cargo-vcs-info.json` with copied/symlinked file info --- src/cargo/ops/cargo_package/mod.rs | 5 +-- src/cargo/ops/cargo_package/vcs.rs | 6 ++-- src/cargo/ops/vendor.rs | 4 ++- src/cargo/sources/mod.rs | 1 + src/cargo/sources/path.rs | 52 +++++++++++++++++++++++------- 5 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/cargo/ops/cargo_package/mod.rs b/src/cargo/ops/cargo_package/mod.rs index 07ba919c98a..6c920eec4e3 100644 --- a/src/cargo/ops/cargo_package/mod.rs +++ b/src/cargo/ops/cargo_package/mod.rs @@ -16,6 +16,7 @@ use crate::core::Workspace; use crate::core::{Package, PackageId, PackageSet, Resolve, SourceId}; use crate::ops::lockfile::LOCKFILE_NAME; use crate::ops::registry::{infer_registry, RegistryOrIndex}; +use crate::sources::path::PathEntry; use crate::sources::registry::index::{IndexPackage, RegistryDependency}; use crate::sources::{PathSource, CRATES_IO_REGISTRY}; use crate::util::cache_lock::CacheLockMode; @@ -396,7 +397,7 @@ fn prepare_archive( fn build_ar_list( ws: &Workspace<'_>, pkg: &Package, - src_files: Vec, + src_files: Vec, vcs_info: Option, ) -> CargoResult> { let mut result = HashMap::new(); @@ -420,7 +421,7 @@ fn build_ar_list( .push(ArchiveFile { rel_path: rel_path.to_owned(), rel_str: rel_str.to_owned(), - contents: FileContents::OnDisk(src_file.clone()), + contents: FileContents::OnDisk(src_file.to_path_buf()), }); } } diff --git a/src/cargo/ops/cargo_package/vcs.rs b/src/cargo/ops/cargo_package/vcs.rs index 44adfd85a1a..ce81235fddc 100644 --- a/src/cargo/ops/cargo_package/vcs.rs +++ b/src/cargo/ops/cargo_package/vcs.rs @@ -9,6 +9,7 @@ use serde::Serialize; use tracing::debug; use crate::core::Package; +use crate::sources::PathEntry; use crate::CargoResult; use crate::GlobalContext; @@ -41,7 +42,7 @@ pub struct GitVcsInfo { #[tracing::instrument(skip_all)] pub fn check_repo_state( p: &Package, - src_files: &[PathBuf], + src_files: &[PathEntry], gctx: &GlobalContext, opts: &PackageOpts<'_>, ) -> CargoResult> { @@ -114,7 +115,7 @@ pub fn check_repo_state( fn git( pkg: &Package, gctx: &GlobalContext, - src_files: &[PathBuf], + src_files: &[PathEntry], repo: &git2::Repository, opts: &PackageOpts<'_>, ) -> CargoResult> { @@ -136,6 +137,7 @@ fn git( let mut dirty_src_files: Vec<_> = src_files .iter() .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path))) + .map(|p| p.as_ref()) .chain(dirty_metadata_paths(pkg, repo)?.iter()) .map(|path| { pathdiff::diff_paths(path, cwd) diff --git a/src/cargo/ops/vendor.rs b/src/cargo/ops/vendor.rs index 35d0e0c9417..c6c76f624da 100644 --- a/src/cargo/ops/vendor.rs +++ b/src/cargo/ops/vendor.rs @@ -2,6 +2,7 @@ use crate::core::shell::Verbosity; use crate::core::{GitReference, Package, Workspace}; use crate::ops; use crate::sources::path::PathSource; +use crate::sources::PathEntry; use crate::sources::CRATES_IO_REGISTRY; use crate::util::cache_lock::CacheLockMode; use crate::util::{try_canonicalize, CargoResult, GlobalContext}; @@ -315,13 +316,14 @@ fn sync( fn cp_sources( pkg: &Package, src: &Path, - paths: &[PathBuf], + paths: &[PathEntry], dst: &Path, cksums: &mut BTreeMap, tmp_buf: &mut [u8], gctx: &GlobalContext, ) -> CargoResult<()> { for p in paths { + let p = p.as_ref(); let relative = p.strip_prefix(&src).unwrap(); match relative.to_str() { diff --git a/src/cargo/sources/mod.rs b/src/cargo/sources/mod.rs index 9c98cc49eaa..dfbf79c76bc 100644 --- a/src/cargo/sources/mod.rs +++ b/src/cargo/sources/mod.rs @@ -29,6 +29,7 @@ pub use self::config::SourceConfigMap; pub use self::directory::DirectorySource; pub use self::git::GitSource; +pub use self::path::PathEntry; pub use self::path::PathSource; pub use self::path::RecursivePathSource; pub use self::registry::{ diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index ee2e6fea47f..a2e8c22fe06 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -94,7 +94,7 @@ impl<'gctx> PathSource<'gctx> { /// use other methods like `.gitignore`, `package.include`, or /// `package.exclude` to filter the list of files. #[tracing::instrument(skip_all)] - pub fn list_files(&self, pkg: &Package) -> CargoResult> { + pub fn list_files(&self, pkg: &Package) -> CargoResult> { list_files(pkg, self.gctx) } @@ -278,7 +278,7 @@ impl<'gctx> RecursivePathSource<'gctx> { /// are relevant for building this package, but it also contains logic to /// use other methods like `.gitignore`, `package.include`, or /// `package.exclude` to filter the list of files. - pub fn list_files(&self, pkg: &Package) -> CargoResult> { + pub fn list_files(&self, pkg: &Package) -> CargoResult> { list_files(pkg, self.gctx) } @@ -404,6 +404,32 @@ impl<'gctx> Source for RecursivePathSource<'gctx> { } } +/// [`PathBuf`] with extra metadata. +#[derive(Clone, Debug)] +pub struct PathEntry { + path: PathBuf, +} + +impl PathEntry { + pub fn into_path_buf(self) -> PathBuf { + self.path + } +} + +impl std::ops::Deref for PathEntry { + type Target = Path; + + fn deref(&self) -> &Self::Target { + self.path.as_path() + } +} + +impl AsRef for PathEntry { + fn as_ref(&self) -> &PathBuf { + &self.path + } +} + fn first_package<'p>( pkg_id: PackageId, pkgs: &'p Vec, @@ -446,7 +472,7 @@ fn first_package<'p>( /// are relevant for building this package, but it also contains logic to /// use other methods like `.gitignore`, `package.include`, or /// `package.exclude` to filter the list of files. -pub fn list_files(pkg: &Package, gctx: &GlobalContext) -> CargoResult> { +pub fn list_files(pkg: &Package, gctx: &GlobalContext) -> CargoResult> { _list_files(pkg, gctx).with_context(|| { format!( "failed to determine list of files in {}", @@ -456,7 +482,7 @@ pub fn list_files(pkg: &Package, gctx: &GlobalContext) -> CargoResult CargoResult> { +fn _list_files(pkg: &Package, gctx: &GlobalContext) -> CargoResult> { let root = pkg.root(); let no_include_option = pkg.manifest().include().is_empty(); let git_repo = if no_include_option { @@ -580,7 +606,7 @@ fn list_files_gix( repo: &gix::Repository, filter: &dyn Fn(&Path, bool) -> bool, gctx: &GlobalContext, -) -> CargoResult> { +) -> CargoResult> { debug!("list_files_gix {}", pkg.package_id()); let options = repo .dirwalk_options()? @@ -619,7 +645,7 @@ fn list_files_gix( vec![include, exclude] }; - let mut files = Vec::::new(); + let mut files = Vec::::new(); let mut subpackages_found = Vec::new(); for item in repo .dirwalk_iter(index.clone(), pathspec, Default::default(), options)? @@ -701,7 +727,7 @@ fn list_files_gix( } else if (filter)(&file_path, is_dir) { assert!(!is_dir); trace!(" found {}", file_path.display()); - files.push(file_path); + files.push(PathEntry { path: file_path }); } } @@ -715,7 +741,7 @@ fn list_files_gix( /// is not tracked under a Git repository. fn list_files_walk( path: &Path, - ret: &mut Vec, + ret: &mut Vec, is_root: bool, filter: &dyn Fn(&Path, bool) -> bool, gctx: &GlobalContext, @@ -756,7 +782,9 @@ fn list_files_walk( Ok(entry) => { let file_type = entry.file_type(); if file_type.is_file() || file_type.is_symlink() { - ret.push(entry.into_path()); + ret.push(PathEntry { + path: entry.into_path(), + }); } } Err(err) if err.loop_ancestor().is_some() => { @@ -770,7 +798,9 @@ fn list_files_walk( // Otherwise, simply recover from it. // Don't worry about error skipping here, the callers would // still hit the IO error if they do access it thereafter. - Some(path) => ret.push(path.to_path_buf()), + Some(path) => ret.push(PathEntry { + path: path.to_path_buf(), + }), None => return Err(err.into()), }, } @@ -801,7 +831,7 @@ fn last_modified_file( let mtime = paths::mtime(&file).unwrap_or_else(|_| FileTime::zero()); if mtime > max { max = mtime; - max_path = file; + max_path = file.into_path_buf(); } } trace!("last modified file {}: {}", path.display(), max); From 8adbe0eb064a0899937014053c7a4b1557e10800 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sat, 28 Dec 2024 21:47:01 -0500 Subject: [PATCH 290/525] refactor(package): preserve file type information in PathEntry So that we can tell whether a path is a symlink and need to traverse to the actual file to check dirtiness or copy real content. --- src/cargo/sources/path.rs | 67 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index a2e8c22fe06..5ced38da6b5 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -404,16 +404,70 @@ impl<'gctx> Source for RecursivePathSource<'gctx> { } } +/// Type that abstracts over [`gix::dir::entry::Kind`] and [`fs::FileType`]. +#[derive(Debug, Clone, Copy)] +enum FileType { + File, + Dir, + Symlink, + Other, +} + +impl From for FileType { + fn from(value: fs::FileType) -> Self { + if value.is_file() { + FileType::File + } else if value.is_dir() { + FileType::Dir + } else if value.is_symlink() { + FileType::Symlink + } else { + FileType::Other + } + } +} + +impl From for FileType { + fn from(value: gix::dir::entry::Kind) -> Self { + use gix::dir::entry::Kind; + match value { + Kind::Untrackable => FileType::Other, + Kind::File => FileType::File, + Kind::Symlink => FileType::Symlink, + Kind::Directory | Kind::Repository => FileType::Dir, + } + } +} + /// [`PathBuf`] with extra metadata. #[derive(Clone, Debug)] pub struct PathEntry { path: PathBuf, + ty: FileType, } impl PathEntry { pub fn into_path_buf(self) -> PathBuf { self.path } + + /// Similar to [`std::path::Path::is_file`] + /// but doesn't follow the symbolic link nor make any system call + pub fn is_file(&self) -> bool { + matches!(self.ty, FileType::File) + } + + /// Similar to [`std::path::Path::is_dir`] + /// but doesn't follow the symbolic link nor make any system call + pub fn is_dir(&self) -> bool { + matches!(self.ty, FileType::Dir) + } + + /// Similar to [`std::path::Path::is_symlink`] + /// but doesn't follow the symbolic link nor make any system call + pub fn is_symlink(&self) -> bool { + matches!(self.ty, FileType::Symlink) + } } impl std::ops::Deref for PathEntry { @@ -727,7 +781,10 @@ fn list_files_gix( } else if (filter)(&file_path, is_dir) { assert!(!is_dir); trace!(" found {}", file_path.display()); - files.push(PathEntry { path: file_path }); + files.push(PathEntry { + path: file_path, + ty: kind.map(Into::into).unwrap_or(FileType::Other), + }); } } @@ -782,8 +839,15 @@ fn list_files_walk( Ok(entry) => { let file_type = entry.file_type(); if file_type.is_file() || file_type.is_symlink() { + // We follow_links(true) here so check if entry was created from a symlink + let ty = if entry.path_is_symlink() { + FileType::Symlink + } else { + file_type.into() + }; ret.push(PathEntry { path: entry.into_path(), + ty, }); } } @@ -800,6 +864,7 @@ fn list_files_walk( // still hit the IO error if they do access it thereafter. Some(path) => ret.push(PathEntry { path: path.to_path_buf(), + ty: FileType::Other, }), None => return Err(err.into()), }, From 871b17f59af1743bf0d475ec69daee31f940ebef Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 31 Dec 2024 11:51:10 -0500 Subject: [PATCH 291/525] test(package): show behavior with `core.symlinks=false` --- tests/testsuite/package.rs | 82 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index b418513eace..093cf89eb27 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -7025,3 +7025,85 @@ src/main.rs "#]]) .run(); } + +#[cargo_test] +fn git_core_symlinks_false() { + if !symlink_supported() { + return; + } + + let git_project = git::new("bar", |p| { + p.file( + "Cargo.toml", + r#" + [package] + name = "bar" + description = "bar" + license = "MIT" + edition = "2021" + documentation = "foo" + "#, + ) + .file("src/lib.rs", "//! This is a module") + .symlink("src/lib.rs", "symlink-lib.rs") + .symlink_dir("src", "symlink-dir") + }); + + let url = git_project.root().to_url().to_string(); + + let p = project().build(); + let root = p.root(); + // Remove the default project layout, + // so we can git-fetch from git_project under the same directory + fs::remove_dir_all(&root).unwrap(); + fs::create_dir_all(&root).unwrap(); + let repo = git::init(&root); + + let mut cfg = repo.config().unwrap(); + cfg.set_bool("core.symlinks", false).unwrap(); + + // let's fetch from git_project so it respects our core.symlinks=false config. + repo.remote_anonymous(&url) + .unwrap() + .fetch(&["HEAD"], None, None) + .unwrap(); + let rev = repo + .find_reference("FETCH_HEAD") + .unwrap() + .peel_to_commit() + .unwrap(); + repo.reset(rev.as_object(), git2::ResetType::Hard, None) + .unwrap(); + + p.cargo("package --allow-dirty") + .with_stderr_data(str![[r#" +[PACKAGING] bar v0.0.0 ([ROOT]/foo) +[PACKAGED] 7 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[VERIFYING] bar v0.0.0 ([ROOT]/foo) +[COMPILING] bar v0.0.0 ([ROOT]/foo/target/package/bar-0.0.0) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); + + let f = File::open(&p.root().join("target/package/bar-0.0.0.crate")).unwrap(); + validate_crate_contents( + f, + "bar-0.0.0.crate", + &[ + "Cargo.lock", + "Cargo.toml", + "Cargo.toml.orig", + "src/lib.rs", + // We're missing symlink-dir/lib.rs in the `.crate` file. + "symlink-dir", + "symlink-lib.rs", + ".cargo_vcs_info.json", + ], + [ + // And their contents are incorrect. + ("symlink-dir", str!["[ROOT]/bar/src"]), + ("symlink-lib.rs", str!["[ROOT]/bar/src/lib.rs"]), + ], + ); +} From 059fe1608590afba6edebf7e13bb927817e98ed3 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 31 Dec 2024 11:54:12 -0500 Subject: [PATCH 292/525] fix(package): warn if symlinks checked out as plain text files `cargo package` will warn users when git `core.symlinks` is `false` and some symlinks were checked out as plain files during packaging. Git config [`core.symlinks`] defaults to true when unset. In git-for-windows (and git as well), the config should be set to false explicitly when the repo was created, if symlink support wasn't detected [^1]. We assume the config was always set at creation time and never changed. So, if it is true, we don't bother users with any warning. [^1]: [`core.symlinks`]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-coresymlinks --- src/cargo/ops/cargo_package/vcs.rs | 48 +++++++++++++++++++++++++ src/cargo/sources/path.rs | 56 ++++++++++++++++++++++++++---- tests/testsuite/package.rs | 4 +++ 3 files changed, 101 insertions(+), 7 deletions(-) diff --git a/src/cargo/ops/cargo_package/vcs.rs b/src/cargo/ops/cargo_package/vcs.rs index ce81235fddc..3c447f32b72 100644 --- a/src/cargo/ops/cargo_package/vcs.rs +++ b/src/cargo/ops/cargo_package/vcs.rs @@ -92,6 +92,8 @@ pub fn check_repo_state( return Ok(None); } + warn_symlink_checked_out_as_plain_text_file(gctx, src_files, &repo)?; + debug!( "found (git) Cargo.toml at `{}` in workdir `{}`", path.display(), @@ -111,6 +113,52 @@ pub fn check_repo_state( return Ok(Some(VcsInfo { git, path_in_vcs })); } +/// Warns if any symlinks were checked out as plain text files. +/// +/// Git config [`core.symlinks`] defaults to true when unset. +/// In git-for-windows (and git as well), +/// the config should be set to false explicitly when the repo was created, +/// if symlink support wasn't detected [^1]. +/// +/// We assume the config was always set at creation time and never changed. +/// So, if it is true, we don't bother users with any warning. +/// +/// [^1]: +/// +/// [`core.symlinks`]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-coresymlinks +fn warn_symlink_checked_out_as_plain_text_file( + gctx: &GlobalContext, + src_files: &[PathEntry], + repo: &git2::Repository, +) -> CargoResult<()> { + if repo + .config() + .and_then(|c| c.get_bool("core.symlinks")) + .unwrap_or(true) + { + return Ok(()); + } + + if src_files.iter().any(|f| f.maybe_plain_text_symlink()) { + let mut shell = gctx.shell(); + shell.warn(format_args!( + "found symbolic links that may be checked out as regular files for git repo at `{}`\n\ + This might cause the `.crate` file to include incorrect or incomplete files", + repo.workdir().unwrap().display(), + ))?; + let extra_note = if cfg!(windows) { + "\nAnd on Windows, enable the Developer Mode to support symlinks" + } else { + "" + }; + shell.note(format_args!( + "to avoid this, set the Git config `core.symlinks` to `true`{extra_note}", + ))?; + } + + Ok(()) +} + /// The real git status check starts from here. fn git( pkg: &Package, diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index 5ced38da6b5..8bafcf18099 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -407,7 +407,7 @@ impl<'gctx> Source for RecursivePathSource<'gctx> { /// Type that abstracts over [`gix::dir::entry::Kind`] and [`fs::FileType`]. #[derive(Debug, Clone, Copy)] enum FileType { - File, + File { maybe_symlink: bool }, Dir, Symlink, Other, @@ -416,7 +416,9 @@ enum FileType { impl From for FileType { fn from(value: fs::FileType) -> Self { if value.is_file() { - FileType::File + FileType::File { + maybe_symlink: false, + } } else if value.is_dir() { FileType::Dir } else if value.is_symlink() { @@ -432,7 +434,9 @@ impl From for FileType { use gix::dir::entry::Kind; match value { Kind::Untrackable => FileType::Other, - Kind::File => FileType::File, + Kind::File => FileType::File { + maybe_symlink: false, + }, Kind::Symlink => FileType::Symlink, Kind::Directory | Kind::Repository => FileType::Dir, } @@ -454,7 +458,7 @@ impl PathEntry { /// Similar to [`std::path::Path::is_file`] /// but doesn't follow the symbolic link nor make any system call pub fn is_file(&self) -> bool { - matches!(self.ty, FileType::File) + matches!(self.ty, FileType::File { .. }) } /// Similar to [`std::path::Path::is_dir`] @@ -468,6 +472,19 @@ impl PathEntry { pub fn is_symlink(&self) -> bool { matches!(self.ty, FileType::Symlink) } + + /// Whether this path might be a plain text symlink. + /// + /// Git may check out symlinks as plain text files that contain the link texts, + /// when either `core.symlinks` is `false`, or on Windows. + pub fn maybe_plain_text_symlink(&self) -> bool { + matches!( + self.ty, + FileType::File { + maybe_symlink: true + } + ) + } } impl std::ops::Deref for PathEntry { @@ -713,7 +730,24 @@ fn list_files_gix( && item.entry.rela_path == "Cargo.lock") }) }) - .map(|res| res.map(|item| (item.entry.rela_path, item.entry.disk_kind))) + .map(|res| { + res.map(|item| { + // Assumption: if a file tracked as a symlink in Git index, and + // the actual file type on disk is file, then it might be a + // plain text file symlink. + // There are exceptions like the file has changed from a symlink + // to a real text file, but hasn't been committed to Git index. + // Exceptions may be rare so we're okay with this now. + let maybe_plain_text_symlink = item.entry.index_kind + == Some(gix::dir::entry::Kind::Symlink) + && item.entry.disk_kind == Some(gix::dir::entry::Kind::File); + ( + item.entry.rela_path, + item.entry.disk_kind, + maybe_plain_text_symlink, + ) + }) + }) .chain( // Append entries that might be tracked in `/target/`. index @@ -731,12 +765,13 @@ fn list_files_gix( // This traversal is not part of a `status()`, and tracking things in `target/` // is rare. None, + false, ) }) .map(Ok), ) { - let (rela_path, kind) = item?; + let (rela_path, kind, maybe_plain_text_symlink) = item?; let file_path = root.join(gix::path::from_bstr(rela_path)); if file_path.file_name().and_then(|name| name.to_str()) == Some("Cargo.toml") { // Keep track of all sub-packages found and also strip out all @@ -781,9 +816,16 @@ fn list_files_gix( } else if (filter)(&file_path, is_dir) { assert!(!is_dir); trace!(" found {}", file_path.display()); + let ty = match kind.map(Into::into) { + Some(FileType::File { .. }) => FileType::File { + maybe_symlink: maybe_plain_text_symlink, + }, + Some(ty) => ty, + None => FileType::Other, + }; files.push(PathEntry { path: file_path, - ty: kind.map(Into::into).unwrap_or(FileType::Other), + ty, }); } } diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index 093cf89eb27..9c6d0493f83 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -7077,6 +7077,10 @@ fn git_core_symlinks_false() { p.cargo("package --allow-dirty") .with_stderr_data(str![[r#" +[WARNING] found symbolic links that may be checked out as regular files for git repo at `[ROOT]/foo/` +This might cause the `.crate` file to include incorrect or incomplete files +[NOTE] to avoid this, set the Git config `core.symlinks` to `true` +... [PACKAGING] bar v0.0.0 ([ROOT]/foo) [PACKAGED] 7 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [VERIFYING] bar v0.0.0 ([ROOT]/foo) From 4c06c57d0dc303b2bc93a5a52f5b962cae48bbce Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 31 Dec 2024 13:24:38 -0500 Subject: [PATCH 293/525] refactor(cargo-util): one generic for each argument So `path` and `base` are able to accept different types --- crates/cargo-util/src/paths.rs | 6 +++--- src/cargo/ops/cargo_package/vcs.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/cargo-util/src/paths.rs b/crates/cargo-util/src/paths.rs index 5d7e3c5a681..cc73fbad892 100644 --- a/crates/cargo-util/src/paths.rs +++ b/crates/cargo-util/src/paths.rs @@ -710,9 +710,9 @@ pub fn set_file_time_no_err>(path: P, time: FileTime) { /// This canonicalizes both paths before stripping. This is useful if the /// paths are obtained in different ways, and one or the other may or may not /// have been normalized in some way. -pub fn strip_prefix_canonical>( - path: P, - base: P, +pub fn strip_prefix_canonical( + path: impl AsRef, + base: impl AsRef, ) -> Result { // Not all filesystems support canonicalize. Just ignore if it doesn't work. let safe_canonicalize = |path: &Path| match path.canonicalize() { diff --git a/src/cargo/ops/cargo_package/vcs.rs b/src/cargo/ops/cargo_package/vcs.rs index 3c447f32b72..990c944a6c1 100644 --- a/src/cargo/ops/cargo_package/vcs.rs +++ b/src/cargo/ops/cargo_package/vcs.rs @@ -235,11 +235,11 @@ fn dirty_metadata_paths(pkg: &Package, repo: &git2::Repository) -> CargoResult Date: Tue, 31 Dec 2024 12:40:23 -0500 Subject: [PATCH 294/525] refactor(package): simplify metadata path field report path join `abs_path` and `workdir.join(rel_path)` are the same at that point. --- src/cargo/ops/cargo_package/vcs.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/cargo/ops/cargo_package/vcs.rs b/src/cargo/ops/cargo_package/vcs.rs index 990c944a6c1..522133b94aa 100644 --- a/src/cargo/ops/cargo_package/vcs.rs +++ b/src/cargo/ops/cargo_package/vcs.rs @@ -242,12 +242,7 @@ fn dirty_metadata_paths(pkg: &Package, repo: &git2::Repository) -> CargoResult Date: Sat, 28 Dec 2024 22:42:11 -0500 Subject: [PATCH 295/525] refactor(source): preserve whether a path is under a symlink dir This is helpful for VCS status check. Paths emitted by PathSource are always under package root, We lose the track of file type info of paths under symlink dirs, so we need this extra bit of information. --- src/cargo/sources/path.rs | 40 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index 8bafcf18099..c1e81528fbd 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -448,6 +448,8 @@ impl From for FileType { pub struct PathEntry { path: PathBuf, ty: FileType, + /// Whether this path was visited when traversing a symlink directory. + under_symlink_dir: bool, } impl PathEntry { @@ -469,10 +471,21 @@ impl PathEntry { /// Similar to [`std::path::Path::is_symlink`] /// but doesn't follow the symbolic link nor make any system call + /// + /// If the path is not a symlink but under a symlink parent directory, + /// this will return false. + /// See [`PathEntry::is_symlink_or_under_symlink`] for an alternative. pub fn is_symlink(&self) -> bool { matches!(self.ty, FileType::Symlink) } + /// Whether a path is a symlink or a path under a symlink directory. + /// + /// Use [`PathEntry::is_symlink`] to get the exact file type of the path only. + pub fn is_symlink_or_under_symlink(&self) -> bool { + self.is_symlink() || self.under_symlink_dir + } + /// Whether this path might be a plain text symlink. /// /// Git may check out symlinks as plain text files that contain the link texts, @@ -826,6 +839,9 @@ fn list_files_gix( files.push(PathEntry { path: file_path, ty, + // Git index doesn't include files from symlink diretory, + // symlink dirs are handled in `list_files_walk`. + under_symlink_dir: false, }); } } @@ -847,6 +863,10 @@ fn list_files_walk( ) -> CargoResult<()> { let walkdir = WalkDir::new(path) .follow_links(true) + // While this is the default, set it explicitly. + // We need walkdir to visit the directory tree in depth-first order, + // so we can ensure a path visited later be under a certain directory. + .contents_first(false) .into_iter() .filter_entry(|entry| { let path = entry.path(); @@ -876,10 +896,27 @@ fn list_files_walk( true }); + + let mut current_symlink_dir = None; for entry in walkdir { match entry { Ok(entry) => { let file_type = entry.file_type(); + + match current_symlink_dir.as_ref() { + Some(dir) if entry.path().starts_with(dir) => { + // Still walk under the same parent symlink dir, so keep it + } + Some(_) | None => { + // Not under any parent symlink dir, update the current one. + current_symlink_dir = if file_type.is_dir() && entry.path_is_symlink() { + Some(entry.path().to_path_buf()) + } else { + None + }; + } + } + if file_type.is_file() || file_type.is_symlink() { // We follow_links(true) here so check if entry was created from a symlink let ty = if entry.path_is_symlink() { @@ -890,6 +927,8 @@ fn list_files_walk( ret.push(PathEntry { path: entry.into_path(), ty, + // This rely on contents_first(false), which walks in depth-first order + under_symlink_dir: current_symlink_dir.is_some(), }); } } @@ -907,6 +946,7 @@ fn list_files_walk( Some(path) => ret.push(PathEntry { path: path.to_path_buf(), ty: FileType::Other, + under_symlink_dir: false, }), None => return Err(err.into()), }, From 014e516e743cc9edd567ce956304f98841c45f17 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sun, 29 Dec 2024 01:43:43 -0500 Subject: [PATCH 296/525] test(package): symlink dirty also under dirtiness check This show that a regular file under a symlink directory is also not tarcked by the current vcs dirtiness check. --- tests/testsuite/package.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index 9c6d0493f83..7ee9433a2e3 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -1339,10 +1339,12 @@ fn dirty_file_outside_pkg_root_considered_dirty() { license-file = "../LICENSE" "#, ) + .file("original-dir/file", "before") .symlink("lib.rs", "isengard/src/lib.rs") .symlink("README.md", "isengard/README.md") .file(&main_outside_pkg_root, "fn main() {}") .symlink(&main_outside_pkg_root, "isengard/src/main.rs") + .symlink_dir("original-dir", "isengard/symlink-dir") }); git::commit(&repo); @@ -1352,6 +1354,7 @@ fn dirty_file_outside_pkg_root_considered_dirty() { // * Changes in files outside package root that source files symlink to p.change_file("README.md", "after"); p.change_file("lib.rs", "pub fn after() {}"); + p.change_file("original-dir/file", "after"); // * Changes in files outside pkg root that `license-file`/`readme` point to p.change_file("LICENSE", "after"); // * When workspace inheritance is involved and changed @@ -1388,7 +1391,7 @@ to proceed despite this and include the uncommitted changes, pass the `--allow-d p.cargo("package --workspace --no-verify --allow-dirty") .with_stderr_data(str![[r#" [PACKAGING] isengard v0.0.0 ([ROOT]/foo/isengard) -[PACKAGED] 8 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[PACKAGED] 9 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]]) .run(); @@ -1411,6 +1414,7 @@ edition = "2021" "Cargo.toml.orig", "src/lib.rs", "src/main.rs", + "symlink-dir/file", "Cargo.lock", "LICENSE", "README.md", @@ -1418,6 +1422,7 @@ edition = "2021" [ ("src/lib.rs", str!["pub fn after() {}"]), ("src/main.rs", str![r#"fn main() { eprintln!("after"); }"#]), + ("symlink-dir/file", str!["after"]), ("README.md", str!["after"]), ("LICENSE", str!["after"]), ("Cargo.toml", cargo_toml), From de39f59e260eaeb7a6186da3311c8658ddf6c453 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 24 Dec 2024 00:09:17 -0500 Subject: [PATCH 297/525] fix(package): check dirtiness of symlink source files This adds a special case for checking source files are symlinks and have been modified when under a VCS control This is required because those paths may link to a file outside the current package root, but still under the git workdir, affecting the final packaged `.crate` file. This may have potential performance issue. If a package contains thousands of symlinks, Cargo will fire `git status` for each of them. --- src/cargo/ops/cargo_package/vcs.rs | 30 ++++++++++++++++++++++++++++++ tests/testsuite/package.rs | 5 ++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/cargo/ops/cargo_package/vcs.rs b/src/cargo/ops/cargo_package/vcs.rs index 522133b94aa..e007841e9e8 100644 --- a/src/cargo/ops/cargo_package/vcs.rs +++ b/src/cargo/ops/cargo_package/vcs.rs @@ -1,5 +1,6 @@ //! Helpers to gather the VCS information for `cargo package`. +use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; @@ -187,6 +188,7 @@ fn git( .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path))) .map(|p| p.as_ref()) .chain(dirty_metadata_paths(pkg, repo)?.iter()) + .chain(dirty_symlinks(pkg, repo, src_files)?.iter()) .map(|path| { pathdiff::diff_paths(path, cwd) .as_ref() @@ -249,6 +251,34 @@ fn dirty_metadata_paths(pkg: &Package, repo: &git2::Repository) -> CargoResult CargoResult> { + let workdir = repo.workdir().unwrap(); + let mut dirty_symlinks = HashSet::new(); + for rel_path in src_files + .iter() + .filter(|p| p.is_symlink_or_under_symlink()) + .map(|p| p.as_ref().as_path()) + // If inside package root. Don't bother checking git status. + .filter(|p| paths::strip_prefix_canonical(p, pkg.root()).is_err()) + // Handle files outside package root but under git workdir, + .filter_map(|p| paths::strip_prefix_canonical(p, workdir).ok()) + { + if repo.status_file(&rel_path)? != git2::Status::CURRENT { + dirty_symlinks.insert(workdir.join(rel_path)); + } + } + Ok(dirty_symlinks) +} + /// Helper to collect dirty statuses for a single repo. fn collect_statuses(repo: &git2::Repository, dirty_files: &mut Vec) -> CargoResult<()> { let mut status_opts = git2::StatusOptions::new(); diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index 7ee9433a2e3..bb20f53e947 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -1378,10 +1378,13 @@ fn dirty_file_outside_pkg_root_considered_dirty() { p.cargo("package --workspace --no-verify") .with_status(101) .with_stderr_data(str![[r#" -[ERROR] 2 files in the working directory contain changes that were not yet committed into git: +[ERROR] 5 files in the working directory contain changes that were not yet committed into git: LICENSE README.md +README.md +lib.rs +original-dir/file to proceed despite this and include the uncommitted changes, pass the `--allow-dirty` flag From 24dd205d5a395ef9c462d1bc4599df3fb2c87f9a Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 31 Dec 2024 13:32:11 -0500 Subject: [PATCH 298/525] fix(package): deduplicate dirty symlink detection metdata path fields may point to a dirty symlilnk and cause duplicate report. This commit combines `dirty_metadata_paths` and `dirty_symlinks` into one so is able to de-duplicate them. --- src/cargo/ops/cargo_package/vcs.rs | 55 +++++++++++------------------- tests/testsuite/package.rs | 3 +- 2 files changed, 20 insertions(+), 38 deletions(-) diff --git a/src/cargo/ops/cargo_package/vcs.rs b/src/cargo/ops/cargo_package/vcs.rs index e007841e9e8..9f2f57284c4 100644 --- a/src/cargo/ops/cargo_package/vcs.rs +++ b/src/cargo/ops/cargo_package/vcs.rs @@ -1,7 +1,6 @@ //! Helpers to gather the VCS information for `cargo package`. use std::collections::HashSet; -use std::path::Path; use std::path::PathBuf; use anyhow::Context as _; @@ -187,8 +186,7 @@ fn git( .iter() .filter(|src_file| dirty_files.iter().any(|path| src_file.starts_with(path))) .map(|p| p.as_ref()) - .chain(dirty_metadata_paths(pkg, repo)?.iter()) - .chain(dirty_symlinks(pkg, repo, src_files)?.iter()) + .chain(dirty_files_outside_pkg_root(pkg, repo, src_files)?.iter()) .map(|path| { pathdiff::diff_paths(path, cwd) .as_ref() @@ -221,54 +219,39 @@ fn git( } } -/// Checks whether files at paths specified in `package.readme` and -/// `package.license-file` have been modified. +/// Checks whether "included" source files outside package root have been modified. /// -/// This is required because those paths may link to a file outside the -/// current package root, but still under the git workdir, affecting the -/// final packaged `.crate` file. -fn dirty_metadata_paths(pkg: &Package, repo: &git2::Repository) -> CargoResult> { - let mut dirty_files = Vec::new(); - let workdir = repo.workdir().unwrap(); - let root = pkg.root(); - let meta = pkg.manifest().metadata(); - for path in [&meta.license_file, &meta.readme] { - let Some(path) = path.as_deref().map(Path::new) else { - continue; - }; - let abs_path = paths::normalize_path(&root.join(path)); - if paths::strip_prefix_canonical(&abs_path, root).is_ok() { - // Inside package root. Don't bother checking git status. - continue; - } - if let Ok(rel_path) = paths::strip_prefix_canonical(&abs_path, workdir) { - // Outside package root but under git workdir, - if repo.status_file(&rel_path)? != git2::Status::CURRENT { - dirty_files.push(workdir.join(rel_path)) - } - } - } - Ok(dirty_files) -} - -/// Checks whether source files are symlinks and have been modified. +/// This currently looks at +/// +/// * `package.readme` and `package.license-file` pointing to paths outside package root +/// * symlinks targets reside outside package root /// /// This is required because those paths may link to a file outside the /// current package root, but still under the git workdir, affecting the /// final packaged `.crate` file. -fn dirty_symlinks( +fn dirty_files_outside_pkg_root( pkg: &Package, repo: &git2::Repository, src_files: &[PathEntry], ) -> CargoResult> { + let pkg_root = pkg.root(); let workdir = repo.workdir().unwrap(); + + let meta = pkg.manifest().metadata(); + let metadata_paths: Vec<_> = [&meta.license_file, &meta.readme] + .into_iter() + .filter_map(|p| p.as_deref()) + .map(|path| paths::normalize_path(&pkg_root.join(path))) + .collect(); + let mut dirty_symlinks = HashSet::new(); for rel_path in src_files .iter() .filter(|p| p.is_symlink_or_under_symlink()) - .map(|p| p.as_ref().as_path()) + .map(|p| p.as_ref()) + .chain(metadata_paths.iter()) // If inside package root. Don't bother checking git status. - .filter(|p| paths::strip_prefix_canonical(p, pkg.root()).is_err()) + .filter(|p| paths::strip_prefix_canonical(p, pkg_root).is_err()) // Handle files outside package root but under git workdir, .filter_map(|p| paths::strip_prefix_canonical(p, workdir).ok()) { diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index bb20f53e947..ae1c5d71024 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -1378,11 +1378,10 @@ fn dirty_file_outside_pkg_root_considered_dirty() { p.cargo("package --workspace --no-verify") .with_status(101) .with_stderr_data(str![[r#" -[ERROR] 5 files in the working directory contain changes that were not yet committed into git: +[ERROR] 4 files in the working directory contain changes that were not yet committed into git: LICENSE README.md -README.md lib.rs original-dir/file From 5f42cf2873f4f8dca3b5f6bcccfc7d58726ed380 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 02:26:01 +0000 Subject: [PATCH 299/525] chore(deps): update alpine docker tag to v3.21 --- crates/cargo-test-support/containers/sshd/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cargo-test-support/containers/sshd/Dockerfile b/crates/cargo-test-support/containers/sshd/Dockerfile index de491fea85e..4e178d09e46 100644 --- a/crates/cargo-test-support/containers/sshd/Dockerfile +++ b/crates/cargo-test-support/containers/sshd/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.20 +FROM alpine:3.21 RUN apk add --no-cache openssh git RUN ssh-keygen -A From fb55f9b3113c981477fb59885903949b9b40d124 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 02:26:09 +0000 Subject: [PATCH 300/525] chore(deps): update rust crate itertools to 0.14.0 --- Cargo.lock | 10 +++++----- Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ddb1eaee5b1..90e02006924 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -342,7 +342,7 @@ dependencies = [ "ignore", "im-rc", "indexmap 2.3.0", - "itertools 0.13.0", + "itertools 0.14.0", "jobserver", "lazycell", "libc", @@ -468,7 +468,7 @@ dependencies = [ "flate2", "git2", "glob", - "itertools 0.13.0", + "itertools 0.14.0", "pasetors", "regex", "serde", @@ -2183,9 +2183,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] @@ -4285,7 +4285,7 @@ dependencies = [ "anyhow", "cargo", "clap", - "itertools 0.13.0", + "itertools 0.14.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0a73f86fdb8..7d2c5008b6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,7 +60,7 @@ humantime = "2.1.0" ignore = "0.4.22" im-rc = "15.1.0" indexmap = "2.2.6" -itertools = "0.13.0" +itertools = "0.14.0" jobserver = "0.1.32" lazycell = "1.3.0" libc = "0.2.155" From 3dabdcdd20d6382e7efa2c01833efa20944f3a79 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 31 Dec 2024 21:55:45 -0500 Subject: [PATCH 301/525] perf(cargo-package): match certain path prefix with pathspec `check_repo_state` checks the entire git repo status. This is usually fine if you have only a few packages in a workspace. For huge monorepos, it may hit performance issues. For example, on awslabs/aws-sdk-rust@2cbd34d the workspace has roughly 434 members to publish. `git ls-files` reported us 204379 files in this Git repository. That means git may need to check status of all files 434 times. That would be `204379 * 434 = 88,700,486` checks! Moreover, the current algorithm is finding the intersection of `PathSource::list_files` and `git status`. It is an `O(n^2)` check. Let's assume files are evenly distributed into each package, so roughly 470 files per package. If we're unlucky to have some dirty files, say 100 files. We will have to do `470 * 100 = 47,000` times of path comparisons. Even worse, because we `git status` everything in the repo, we'll have to it for all members, even when those dirty files are not part of the current package in question. So it becomes `470 * 100 * 434 = 20,398,000`! Instead of comparing with the status of the entire repository, this patch use the magic pathspec[1] to tell git only reports paths that match a certain path prefix. This wouldn't help the `O(n^2)` algorithm, but at least it won't check dirty files outside the current package. Also, we don't `git status` against entire git worktree/index anymore. [1]: https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefpathspecapathspec --- src/cargo/ops/cargo_package/vcs.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/cargo/ops/cargo_package/vcs.rs b/src/cargo/ops/cargo_package/vcs.rs index 9f2f57284c4..04d7a9d5afd 100644 --- a/src/cargo/ops/cargo_package/vcs.rs +++ b/src/cargo/ops/cargo_package/vcs.rs @@ -1,6 +1,7 @@ //! Helpers to gather the VCS information for `cargo package`. use std::collections::HashSet; +use std::path::Path; use std::path::PathBuf; use anyhow::Context as _; @@ -173,7 +174,9 @@ fn git( // - ignored (in case the user has an `include` directive that // conflicts with .gitignore). let mut dirty_files = Vec::new(); - collect_statuses(repo, &mut dirty_files)?; + let pathspec = relative_pathspec(repo, pkg.root()); + collect_statuses(repo, &[pathspec.as_str()], &mut dirty_files)?; + // Include each submodule so that the error message can provide // specifically *which* files in a submodule are modified. status_submodules(repo, &mut dirty_files)?; @@ -263,12 +266,18 @@ fn dirty_files_outside_pkg_root( } /// Helper to collect dirty statuses for a single repo. -fn collect_statuses(repo: &git2::Repository, dirty_files: &mut Vec) -> CargoResult<()> { +fn collect_statuses( + repo: &git2::Repository, + pathspecs: &[&str], + dirty_files: &mut Vec, +) -> CargoResult<()> { let mut status_opts = git2::StatusOptions::new(); // Exclude submodules, as they are being handled manually by recursing // into each one so that details about specific files can be // retrieved. - status_opts + pathspecs + .iter() + .fold(&mut status_opts, git2::StatusOptions::pathspec) .exclude_submodules(true) .include_ignored(true) .include_untracked(true); @@ -300,8 +309,16 @@ fn status_submodules(repo: &git2::Repository, dirty_files: &mut Vec) -> // If its files are required, then the verification step should fail. if let Ok(sub_repo) = submodule.open() { status_submodules(&sub_repo, dirty_files)?; - collect_statuses(&sub_repo, dirty_files)?; + collect_statuses(&sub_repo, &[], dirty_files)?; } } Ok(()) } + +/// Use pathspec so git only matches a certain path prefix +fn relative_pathspec(repo: &git2::Repository, pkg_root: &Path) -> String { + let workdir = repo.workdir().unwrap(); + let relpath = pkg_root.strip_prefix(workdir).unwrap_or(Path::new("")); + // to unix separators + relpath.to_str().unwrap().replace('\\', "/") +} From 76ffbe0571f866b3723240c720cbb11cc2f6c273 Mon Sep 17 00:00:00 2001 From: Lin Yihai Date: Wed, 30 Oct 2024 21:10:53 +0800 Subject: [PATCH 302/525] fix: envs in config can trigger rebuild by custom build script with `rerun-if-env-changed`. --- src/cargo/core/compiler/fingerprint/mod.rs | 45 +++++++++++++++------- tests/testsuite/build_script_env.rs | 2 + 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/cargo/core/compiler/fingerprint/mod.rs b/src/cargo/core/compiler/fingerprint/mod.rs index 623e4c59b60..b35a592ab11 100644 --- a/src/cargo/core/compiler/fingerprint/mod.rs +++ b/src/cargo/core/compiler/fingerprint/mod.rs @@ -374,6 +374,7 @@ mod dirty_reason; use std::collections::hash_map::{Entry, HashMap}; use std::env; +use std::ffi::OsString; use std::fs; use std::fs::File; use std::hash::{self, Hash, Hasher}; @@ -509,7 +510,7 @@ pub fn prepare_target( // thunk we can invoke on a foreign thread to calculate this. let build_script_outputs = Arc::clone(&build_runner.build_script_outputs); let metadata = build_runner.get_run_build_script_metadata(unit); - let (gen_local, _overridden) = build_script_local_fingerprints(build_runner, unit); + let (gen_local, _overridden) = build_script_local_fingerprints(build_runner, unit)?; let output_path = build_runner.build_explicit_deps[unit] .build_script_output .clone(); @@ -801,15 +802,24 @@ pub enum StaleItem { impl LocalFingerprint { /// Read the environment variable of the given env `key`, and creates a new - /// [`LocalFingerprint::RerunIfEnvChanged`] for it. + /// [`LocalFingerprint::RerunIfEnvChanged`] for it. The `env_config` is used firstly + /// to check if the env var is set in the config system as some envs need to be overridden. + /// If not, it will fallback to `std::env::var`. /// - // TODO: This is allowed at this moment. Should figure out if it makes - // sense if permitting to read env from the config system. + // TODO: `std::env::var` is allowed at this moment. Should figure out + // if it makes sense if permitting to read env from the env snapshot. #[allow(clippy::disallowed_methods)] - fn from_env>(key: K) -> LocalFingerprint { + fn from_env>( + key: K, + env_config: &Arc>, + ) -> LocalFingerprint { let key = key.as_ref(); let var = key.to_owned(); - let val = env::var(key).ok(); + let val = if let Some(val) = env_config.get(key) { + val.to_str().map(ToOwned::to_owned) + } else { + env::var(key).ok() + }; LocalFingerprint::RerunIfEnvChanged { var, val } } @@ -1577,7 +1587,7 @@ fn calculate_run_custom_build( // the build script this means we'll be watching files and env vars. // Otherwise if we haven't previously executed it we'll just start watching // the whole crate. - let (gen_local, overridden) = build_script_local_fingerprints(build_runner, unit); + let (gen_local, overridden) = build_script_local_fingerprints(build_runner, unit)?; let deps = &build_runner.build_explicit_deps[unit]; let local = (gen_local)( deps, @@ -1671,7 +1681,7 @@ See https://doc.rust-lang.org/cargo/reference/build-scripts.html#rerun-if-change fn build_script_local_fingerprints( build_runner: &mut BuildRunner<'_, '_>, unit: &Unit, -) -> ( +) -> CargoResult<( Box< dyn FnOnce( &BuildDeps, @@ -1680,20 +1690,20 @@ fn build_script_local_fingerprints( + Send, >, bool, -) { +)> { assert!(unit.mode.is_run_custom_build()); // First up, if this build script is entirely overridden, then we just // return the hash of what we overrode it with. This is the easy case! if let Some(fingerprint) = build_script_override_fingerprint(build_runner, unit) { debug!("override local fingerprints deps {}", unit.pkg); - return ( + return Ok(( Box::new( move |_: &BuildDeps, _: Option<&dyn Fn() -> CargoResult>| { Ok(Some(vec![fingerprint])) }, ), true, // this is an overridden build script - ); + )); } // ... Otherwise this is a "real" build script and we need to return a real @@ -1705,6 +1715,7 @@ fn build_script_local_fingerprints( // obvious. let pkg_root = unit.pkg.root().to_path_buf(); let target_dir = target_root(build_runner); + let env_config = Arc::clone(build_runner.bcx.gctx.env_config()?); let calculate = move |deps: &BuildDeps, pkg_fingerprint: Option<&dyn Fn() -> CargoResult>| { if deps.rerun_if_changed.is_empty() && deps.rerun_if_env_changed.is_empty() { @@ -1734,11 +1745,16 @@ fn build_script_local_fingerprints( // Ok so now we're in "new mode" where we can have files listed as // dependencies as well as env vars listed as dependencies. Process // them all here. - Ok(Some(local_fingerprints_deps(deps, &target_dir, &pkg_root))) + Ok(Some(local_fingerprints_deps( + deps, + &target_dir, + &pkg_root, + &env_config, + ))) }; // Note that `false` == "not overridden" - (Box::new(calculate), false) + Ok((Box::new(calculate), false)) } /// Create a [`LocalFingerprint`] for an overridden build script. @@ -1769,6 +1785,7 @@ fn local_fingerprints_deps( deps: &BuildDeps, target_root: &Path, pkg_root: &Path, + env_config: &Arc>, ) -> Vec { debug!("new local fingerprints deps {:?}", pkg_root); let mut local = Vec::new(); @@ -1793,7 +1810,7 @@ fn local_fingerprints_deps( local.extend( deps.rerun_if_env_changed .iter() - .map(LocalFingerprint::from_env), + .map(|s| LocalFingerprint::from_env(s, env_config)), ); local diff --git a/tests/testsuite/build_script_env.rs b/tests/testsuite/build_script_env.rs index 426c815e28c..db48bcd8391 100644 --- a/tests/testsuite/build_script_env.rs +++ b/tests/testsuite/build_script_env.rs @@ -415,6 +415,7 @@ fn rerun_if_env_is_exsited_config() { p.cargo(r#"check --config 'env.FOO="bar"'"#) .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) @@ -445,6 +446,7 @@ fn rerun_if_env_newly_added_in_config() { p.cargo(r#"check --config 'env.FOO="foo"'"#) .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) From 729776b5890e3822374ef9a93152a4eccf381752 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 1 Jan 2025 10:14:31 -0600 Subject: [PATCH 303/525] fix(schema): Correct and update the JSON Schema Fixes #14999 --- .../cargo-util-schemas/manifest.schema.json | 696 +++++++++--------- crates/cargo-util-schemas/src/manifest/mod.rs | 2 +- 2 files changed, 349 insertions(+), 349 deletions(-) diff --git a/crates/cargo-util-schemas/manifest.schema.json b/crates/cargo-util-schemas/manifest.schema.json index 000f15f2300..37f84f4f792 100644 --- a/crates/cargo-util-schemas/manifest.schema.json +++ b/crates/cargo-util-schemas/manifest.schema.json @@ -33,15 +33,29 @@ } ] }, - "profile": { - "anyOf": [ - { - "$ref": "#/definitions/TomlProfiles" - }, - { - "type": "null" + "badges": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "string" } - ] + } + }, + "features": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } }, "lib": { "anyOf": [ @@ -134,18 +148,6 @@ "$ref": "#/definitions/InheritableDependency" } }, - "features": { - "type": [ - "object", - "null" - ], - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - } - }, "target": { "type": [ "object", @@ -155,26 +157,15 @@ "$ref": "#/definitions/TomlPlatform" } }, - "replace": { - "type": [ - "object", - "null" - ], - "additionalProperties": { - "$ref": "#/definitions/TomlDependency_for_String" - } - }, - "patch": { - "type": [ - "object", - "null" - ], - "additionalProperties": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/TomlDependency_for_String" + "lints": { + "anyOf": [ + { + "$ref": "#/definitions/InheritableLints" + }, + { + "type": "null" } - } + ] }, "workspace": { "anyOf": [ @@ -186,7 +177,17 @@ } ] }, - "badges": { + "profile": { + "anyOf": [ + { + "$ref": "#/definitions/TomlProfiles" + }, + { + "type": "null" + } + ] + }, + "patch": { "type": [ "object", "null" @@ -194,24 +195,23 @@ "additionalProperties": { "type": "object", "additionalProperties": { - "type": "string" + "$ref": "#/definitions/TomlDependency_for_String" } } }, - "lints": { - "anyOf": [ - { - "$ref": "#/definitions/InheritableLints" - }, - { - "type": "null" - } - ] + "replace": { + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/TomlDependency_for_String" + } } }, "definitions": { "TomlPackage": { - "description": "Represents the `package`/`project` sections of a `Cargo.toml`./n/nNote that the order of the fields matters, since this is the order they are serialized to a TOML file. For example, you cannot have values after the field `metadata`, since it is a table and values cannot appear after tables.", + "description": "Represents the `package`/`project` sections of a `Cargo.toml`.\n\nNote that the order of the fields matters, since this is the order they are serialized to a TOML file. For example, you cannot have values after the field `metadata`, since it is a table and values cannot appear after tables.", "type": "object", "required": [ "name" @@ -517,7 +517,7 @@ { "description": "The type that is used when not inheriting from a workspace.", "type": "string", - "pattern": "^(0|[1-9]//d*)//.(0|[1-9]//d*)//.(0|[1-9]//d*)(?:-((?:0|[1-9]//d*|//d*[a-zA-Z-][0-9a-zA-Z-]*)(?://.(?:0|[1-9]//d*|//d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?://+([0-9a-zA-Z-]+(?://.[0-9a-zA-Z-]+)*))?$" + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, { "description": "The type when inheriting from a workspace.", @@ -560,7 +560,7 @@ ] }, "StringOrVec": { - "description": "A StringOrVec can be parsed from either a TOML string or array, but is always stored as a vector.", + "description": "This can be parsed from either a TOML string or array, but is always stored as a vector.", "type": "array", "items": { "type": "string" @@ -655,133 +655,94 @@ } } }, - "TomlProfiles": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/TomlProfile" - } - }, - "TomlProfile": { + "TomlTarget": { "type": "object", "properties": { - "opt-level": { - "default": null, - "anyOf": [ - { - "$ref": "#/definitions/TomlOptLevel" - }, - { - "type": "null" - } - ] - }, - "lto": { - "default": null, - "anyOf": [ - { - "$ref": "#/definitions/StringOrBool" - }, - { - "type": "null" - } - ] - }, - "codegen-backend": { - "default": null, + "name": { "type": [ "string", "null" ] }, - "codegen-units": { - "default": null, + "crate-type": { "type": [ - "integer", + "array", "null" ], - "format": "uint32", - "minimum": 0.0 + "items": { + "type": "string" + } }, - "debug": { - "default": null, - "anyOf": [ - { - "$ref": "#/definitions/TomlDebugInfo" - }, - { - "type": "null" - } + "crate_type": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "path": { + "type": [ + "string", + "null" ] }, - "split-debuginfo": { - "default": null, + "filename": { "type": [ "string", "null" ] }, - "debug-assertions": { - "default": null, + "test": { "type": [ "boolean", "null" ] }, - "rpath": { - "default": null, + "doctest": { "type": [ "boolean", "null" ] }, - "panic": { - "default": null, + "bench": { "type": [ - "string", + "boolean", "null" ] }, - "overflow-checks": { - "default": null, + "doc": { "type": [ "boolean", "null" ] }, - "incremental": { - "default": null, + "doc-scrape-examples": { "type": [ "boolean", "null" ] }, - "dir-name": { - "default": null, + "proc-macro": { "type": [ - "string", + "boolean", "null" ] }, - "inherits": { - "default": null, + "proc_macro": { "type": [ - "string", + "boolean", "null" ] }, - "strip": { - "default": null, - "anyOf": [ - { - "$ref": "#/definitions/StringOrBool" - }, - { - "type": "null" - } + "harness": { + "type": [ + "boolean", + "null" ] }, - "rustflags": { - "default": null, + "required-features": { "type": [ "array", "null" @@ -790,207 +751,42 @@ "type": "string" } }, - "package": { - "default": null, + "edition": { "type": [ - "object", + "string", "null" - ], - "additionalProperties": { - "$ref": "#/definitions/TomlProfile" - } - }, - "build-override": { - "default": null, - "anyOf": [ - { - "$ref": "#/definitions/TomlProfile" - }, + ] + } + } + }, + "InheritableDependency": { + "anyOf": [ + { + "description": "The type that is used when not inheriting from a workspace.", + "allOf": [ { - "type": "null" + "$ref": "#/definitions/TomlDependency_for_String" } ] }, - "trim-paths": { - "description": "Unstable feature `-Ztrim-paths`.", - "default": null, - "anyOf": [ - { - "$ref": "#/definitions/TomlTrimPaths" - }, + { + "description": "The type when inheriting from a workspace.", + "allOf": [ { - "type": "null" + "$ref": "#/definitions/TomlInheritedDependency" } ] } - } - }, - "TomlOptLevel": { - "type": "string" - }, - "TomlDebugInfo": { - "type": "string", - "enum": [ - "None", - "LineDirectivesOnly", - "LineTablesOnly", - "Limited", - "Full" - ] - }, - "TomlTrimPaths": { - "anyOf": [ - { - "type": "array", - "items": { - "$ref": "#/definitions/TomlTrimPathsValue" - } - }, - { - "type": "null" - } - ] - }, - "TomlTrimPathsValue": { - "type": "string", - "enum": [ - "diagnostics", - "macro", - "object" - ] - }, - "TomlTarget": { - "type": "object", - "properties": { - "name": { - "type": [ - "string", - "null" - ] - }, - "crate-type": { - "type": [ - "array", - "null" - ], - "items": { - "type": "string" - } - }, - "crate_type": { - "type": [ - "array", - "null" - ], - "items": { - "type": "string" - } - }, - "path": { - "type": [ - "string", - "null" - ] - }, - "filename": { - "type": [ - "string", - "null" - ] - }, - "test": { - "type": [ - "boolean", - "null" - ] - }, - "doctest": { - "type": [ - "boolean", - "null" - ] - }, - "bench": { - "type": [ - "boolean", - "null" - ] - }, - "doc": { - "type": [ - "boolean", - "null" - ] - }, - "doc-scrape-examples": { - "type": [ - "boolean", - "null" - ] - }, - "proc-macro": { - "type": [ - "boolean", - "null" - ] - }, - "proc_macro": { - "type": [ - "boolean", - "null" - ] - }, - "harness": { - "type": [ - "boolean", - "null" - ] - }, - "required-features": { - "type": [ - "array", - "null" - ], - "items": { - "type": "string" - } - }, - "edition": { - "type": [ - "string", - "null" - ] - } - } - }, - "InheritableDependency": { - "anyOf": [ - { - "description": "The type that is used when not inheriting from a workspace.", - "allOf": [ - { - "$ref": "#/definitions/TomlDependency_for_String" - } - ] - }, - { - "description": "The type when inheriting from a workspace.", - "allOf": [ - { - "$ref": "#/definitions/TomlInheritedDependency" - } - ] - } - ] + ] }, "TomlDependency_for_String": { "anyOf": [ { - "description": "In the simple format, only a version is specified, eg. `package = /"/"`", + "description": "In the simple format, only a version is specified, eg. `package = \"\"`", "type": "string" }, { - "description": "The simple format is equivalent to a detailed dependency specifying only a version, eg. `package = { version = /"/" }`", + "description": "The simple format is equivalent to a detailed dependency specifying only a version, eg. `package = { version = \"\" }`", "allOf": [ { "$ref": "#/definitions/TomlDetailedDependency_for_String" @@ -1218,6 +1014,52 @@ } } }, + "InheritableLints": { + "type": "object", + "required": [ + "workspace" + ], + "properties": { + "workspace": { + "type": "boolean" + } + } + }, + "TomlLint": { + "anyOf": [ + { + "$ref": "#/definitions/TomlLintLevel" + }, + { + "$ref": "#/definitions/TomlLintConfig" + } + ] + }, + "TomlLintLevel": { + "type": "string", + "enum": [ + "forbid", + "deny", + "warn", + "allow" + ] + }, + "TomlLintConfig": { + "type": "object", + "required": [ + "level" + ], + "properties": { + "level": { + "$ref": "#/definitions/TomlLintLevel" + }, + "priority": { + "default": 0, + "type": "integer", + "format": "int8" + } + } + }, "TomlWorkspace": { "type": "object", "properties": { @@ -1306,7 +1148,7 @@ "string", "null" ], - "pattern": "^(0|[1-9]//d*)//.(0|[1-9]//d*)//.(0|[1-9]//d*)(?:-((?:0|[1-9]//d*|//d*[a-zA-Z-][0-9a-zA-Z-]*)(?://.(?:0|[1-9]//d*|//d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?://+([0-9a-zA-Z-]+(?://.[0-9a-zA-Z-]+)*))?$" + "pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$" }, "authors": { "type": [ @@ -1435,51 +1277,209 @@ } } }, - "TomlLint": { - "anyOf": [ - { - "$ref": "#/definitions/TomlLintLevel" + "TomlProfiles": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/TomlProfile" + } + }, + "TomlProfile": { + "type": "object", + "properties": { + "opt-level": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/TomlOptLevel" + }, + { + "type": "null" + } + ] }, - { - "$ref": "#/definitions/TomlLintConfig" + "lto": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/StringOrBool" + }, + { + "type": "null" + } + ] + }, + "codegen-backend": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "codegen-units": { + "default": null, + "type": [ + "integer", + "null" + ], + "format": "uint32", + "minimum": 0.0 + }, + "debug": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/TomlDebugInfo" + }, + { + "type": "null" + } + ] + }, + "split-debuginfo": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "debug-assertions": { + "default": null, + "type": [ + "boolean", + "null" + ] + }, + "rpath": { + "default": null, + "type": [ + "boolean", + "null" + ] + }, + "panic": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "overflow-checks": { + "default": null, + "type": [ + "boolean", + "null" + ] + }, + "incremental": { + "default": null, + "type": [ + "boolean", + "null" + ] + }, + "dir-name": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "inherits": { + "default": null, + "type": [ + "string", + "null" + ] + }, + "strip": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/StringOrBool" + }, + { + "type": "null" + } + ] + }, + "rustflags": { + "default": null, + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "package": { + "default": null, + "type": [ + "object", + "null" + ], + "additionalProperties": { + "$ref": "#/definitions/TomlProfile" + } + }, + "build-override": { + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/TomlProfile" + }, + { + "type": "null" + } + ] + }, + "trim-paths": { + "description": "Unstable feature `-Ztrim-paths`.", + "default": null, + "anyOf": [ + { + "$ref": "#/definitions/TomlTrimPaths" + }, + { + "type": "null" + } + ] } - ] + } }, - "TomlLintLevel": { + "TomlOptLevel": { + "type": "string" + }, + "TomlDebugInfo": { "type": "string", "enum": [ - "forbid", - "deny", - "warn", - "allow" + "None", + "LineDirectivesOnly", + "LineTablesOnly", + "Limited", + "Full" ] }, - "TomlLintConfig": { - "type": "object", - "required": [ - "level" - ], - "properties": { - "level": { - "$ref": "#/definitions/TomlLintLevel" + "TomlTrimPaths": { + "anyOf": [ + { + "type": "array", + "items": { + "$ref": "#/definitions/TomlTrimPathsValue" + } }, - "priority": { - "default": 0, - "type": "integer", - "format": "int8" + { + "type": "null" } - } + ] }, - "InheritableLints": { - "type": "object", - "required": [ - "workspace" - ], - "properties": { - "workspace": { - "type": "boolean" - } - } + "TomlTrimPathsValue": { + "type": "string", + "enum": [ + "diagnostics", + "macro", + "object" + ] } } } \ No newline at end of file diff --git a/crates/cargo-util-schemas/src/manifest/mod.rs b/crates/cargo-util-schemas/src/manifest/mod.rs index bfb23495c3e..efa125ce533 100644 --- a/crates/cargo-util-schemas/src/manifest/mod.rs +++ b/crates/cargo-util-schemas/src/manifest/mod.rs @@ -1787,5 +1787,5 @@ pub struct UnresolvedError; fn dump_manifest_schema() { let schema = schemars::schema_for!(crate::manifest::TomlManifest); let dump = serde_json::to_string_pretty(&schema).unwrap(); - snapbox::assert_data_eq!(dump, snapbox::file!("../../manifest.schema.json")); + snapbox::assert_data_eq!(dump, snapbox::file!("../../manifest.schema.json").raw()); } From 876f17d46b12b36b5ecbc40abfdad16161542095 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 1 Jan 2025 10:16:18 -0600 Subject: [PATCH 304/525] chore(ci): Ensure JSON schema gets updated --- .github/workflows/main.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9b7fa428136..5caebcee6da 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -205,6 +205,13 @@ jobs: - name: Fetch smoke test run: ci/fetch-smoke-test.sh + schema: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - run: rustup update stable && rustup default stable + - run: cargo test -p cargo-util-schemas -F unstable-schema + resolver: runs-on: ubuntu-latest steps: From 5465e6a451938dcf4d1e032909cfef7da1b7814c Mon Sep 17 00:00:00 2001 From: Rustin170506 Date: Tue, 17 Dec 2024 21:39:18 +0800 Subject: [PATCH 305/525] fix: remove unsupported embedded workspace check from `cargo pkgid` command Signed-off-by: Rustin170506 --- src/bin/cargo/commands/pkgid.rs | 7 ------- tests/testsuite/script.rs | 13 ++++++++++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/bin/cargo/commands/pkgid.rs b/src/bin/cargo/commands/pkgid.rs index 5fcf85b8fd9..aa98dc5c335 100644 --- a/src/bin/cargo/commands/pkgid.rs +++ b/src/bin/cargo/commands/pkgid.rs @@ -18,13 +18,6 @@ pub fn cli() -> Command { pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { let ws = args.workspace(gctx)?; - if ws.root_maybe().is_embedded() { - return Err(anyhow::format_err!( - "{} is unsupported by `cargo pkgid`", - ws.root_manifest().display() - ) - .into()); - } if args.is_present_with_zero_values("package") { print_available_packages(&ws)? } diff --git a/tests/testsuite/script.rs b/tests/testsuite/script.rs index 3c31c77f360..8db5fcc1279 100644 --- a/tests/testsuite/script.rs +++ b/tests/testsuite/script.rs @@ -1297,18 +1297,25 @@ fn cmd_verify_project_with_embedded() { .run(); } -#[cargo_test] +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] fn cmd_pkgid_with_embedded() { let p = cargo_test_support::project() .file("script.rs", ECHO_SCRIPT) .build(); + p.cargo("-Zscript script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .run(); + + // FIXME: It should be `path+[ROOTURL]/foo/script.rs#script@0.0.0`. p.cargo("-Zscript pkgid --manifest-path script.rs") .masquerade_as_nightly_cargo(&["script"]) - .with_status(101) + .with_stdout_data(str![[r#" +path+[ROOTURL]/foo#script@0.0.0 + +"#]]) .with_stderr_data(str![[r#" [WARNING] `package.edition` is unspecified, defaulting to `2024` -[ERROR] [ROOT]/foo/script.rs is unsupported by `cargo pkgid` "#]]) .run(); From feda97d9f27f3b70e7de266ffec05fc48cd60b23 Mon Sep 17 00:00:00 2001 From: Rustin170506 Date: Thu, 19 Dec 2024 22:33:42 +0800 Subject: [PATCH 306/525] test: add the no lock file case Signed-off-by: Rustin170506 --- tests/testsuite/script.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/testsuite/script.rs b/tests/testsuite/script.rs index 8db5fcc1279..bae2c2837ec 100644 --- a/tests/testsuite/script.rs +++ b/tests/testsuite/script.rs @@ -1321,6 +1321,23 @@ path+[ROOTURL]/foo#script@0.0.0 .run(); } +#[cargo_test] +fn cmd_pkgid_with_embedded_no_lock_file() { + let p = cargo_test_support::project() + .file("script.rs", ECHO_SCRIPT) + .build(); + + p.cargo("-Zscript pkgid --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_data(str![[r#" +[WARNING] `package.edition` is unspecified, defaulting to `2024` +[ERROR] a Cargo.lock must exist for this command + +"#]]) + .run(); +} + #[cargo_test] fn cmd_package_with_embedded() { let p = cargo_test_support::project() From 0c2bb4e5a8afbeba257dbe10123823d1e0f5541c Mon Sep 17 00:00:00 2001 From: Rustin170506 Date: Thu, 19 Dec 2024 22:43:48 +0800 Subject: [PATCH 307/525] test: add the dep case Signed-off-by: Rustin170506 --- tests/testsuite/script.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/testsuite/script.rs b/tests/testsuite/script.rs index bae2c2837ec..520ebbe556d 100644 --- a/tests/testsuite/script.rs +++ b/tests/testsuite/script.rs @@ -1338,6 +1338,39 @@ fn cmd_pkgid_with_embedded_no_lock_file() { .run(); } +#[cargo_test(nightly, reason = "edition2024 hasn't hit stable yet")] +fn cmd_pkgid_with_embedded_dep() { + Package::new("dep", "1.0.0").publish(); + let script = r#"#!/usr/bin/env cargo +--- +[dependencies] +dep = "1.0.0" +--- + +fn main() { + println!("Hello world!"); +}"#; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .run(); + + p.cargo("-Zscript pkgid --manifest-path script.rs -p dep") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout_data(str![[r#" +registry+https://github.com/rust-lang/crates.io-index#dep@1.0.0 + +"#]]) + .with_stderr_data(str![[r#" +[WARNING] `package.edition` is unspecified, defaulting to `2024` + +"#]]) + .run(); +} + #[cargo_test] fn cmd_package_with_embedded() { let p = cargo_test_support::project() From 13af53a5189c5ad663b03075a37468e253589b56 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 03:37:26 +0000 Subject: [PATCH 308/525] chore(deps): update rust crate thiserror to v2 --- Cargo.lock | 10 +++++----- Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90e02006924..4b185db5105 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -372,7 +372,7 @@ dependencies = [ "supports-unicode", "tar", "tempfile", - "thiserror 1.0.63", + "thiserror 2.0.3", "time", "toml", "toml_edit", @@ -395,7 +395,7 @@ dependencies = [ "serde", "serde_json", "snapbox", - "thiserror 1.0.63", + "thiserror 2.0.3", "time", "windows-sys 0.59.0", ] @@ -514,7 +514,7 @@ dependencies = [ "serde-value", "serde_json", "snapbox", - "thiserror 1.0.63", + "thiserror 2.0.3", "toml", "unicode-xid", "url", @@ -711,7 +711,7 @@ dependencies = [ "percent-encoding", "serde", "serde_json", - "thiserror 1.0.63", + "thiserror 2.0.3", "url", ] @@ -3109,7 +3109,7 @@ dependencies = [ "serde_json", "similar", "tempfile", - "thiserror 1.0.63", + "thiserror 2.0.3", "tracing", "tracing-subscriber", ] diff --git a/Cargo.toml b/Cargo.toml index 7d2c5008b6c..17ecfcff0fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -102,7 +102,7 @@ supports-unicode = "3.0.0" snapbox = { version = "0.6.20", features = ["diff", "dir", "term-svg", "regex", "json"] } tar = { version = "0.4.42", default-features = false } tempfile = "3.10.1" -thiserror = "1.0.63" +thiserror = "2.0.0" time = { version = "0.3.36", features = ["parsing", "formatting", "serde"] } toml = "0.8.19" toml_edit = { version = "0.22.20", features = ["serde"] } From b16514de3b794a05c0c95887e90d18b330ce3882 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 2 Jan 2025 14:52:57 -0600 Subject: [PATCH 309/525] refactor(manifest): Fully qualify manifest env vars This simplifies the macro and makes the content searchable --- src/cargo/core/manifest.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 88845ff26e0..726091d55b6 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -172,23 +172,21 @@ macro_rules! metadata_envs { pub fn should_track(key: &str) -> bool { let keys = [$($key),*]; - key.strip_prefix("CARGO_PKG_") - .map(|key| keys.iter().any(|k| *k == key)) - .unwrap_or_default() + keys.iter().any(|k| *k == key) } pub fn var<'a>(meta: &'a ManifestMetadata, key: &str) -> Option> { - key.strip_prefix("CARGO_PKG_").and_then(|key| match key { + match key { $($key => Some(Self::$field(meta)),)* _ => None, - }) + } } pub fn vars(meta: &ManifestMetadata) -> impl Iterator)> { [ $( ( - concat!("CARGO_PKG_", $key), + $key, Self::$field(meta), ), )* @@ -202,14 +200,14 @@ macro_rules! metadata_envs { // If these change we need to trigger a rebuild. // NOTE: The env var name will be prefixed with `CARGO_PKG_` metadata_envs! { - (description, "DESCRIPTION"), - (homepage, "HOMEPAGE"), - (repository, "REPOSITORY"), - (license, "LICENSE"), - (license_file, "LICENSE_FILE"), - (authors, "AUTHORS", |m: &ManifestMetadata| m.authors.join(":")), - (rust_version, "RUST_VERSION", |m: &ManifestMetadata| m.rust_version.as_ref().map(ToString::to_string).unwrap_or_default()), - (readme, "README"), + (description, "CARGO_PKG_DESCRIPTION"), + (homepage, "CARGO_PKG_HOMEPAGE"), + (repository, "CARGO_PKG_REPOSITORY"), + (license, "CARGO_PKG_LICENSE"), + (license_file, "CARGO_PKG_LICENSE_FILE"), + (authors, "CARGO_PKG_AUTHORS", |m: &ManifestMetadata| m.authors.join(":")), + (rust_version, "CARGO_PKG_RUST_VERSION", |m: &ManifestMetadata| m.rust_version.as_ref().map(ToString::to_string).unwrap_or_default()), + (readme, "CARGO_PKG_README"), } impl ManifestMetadata { From 711d6bef36ceec9ac7e7f7f953ee4bc16821b266 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 2 Jan 2025 15:03:11 -0600 Subject: [PATCH 310/525] refactor(manifest): Make env macro as minimal as possible Macros add a lot of code complexity. This tries to reduce it by making the macro do the bare minimum possible. This does cause some extra branching (unless its compiled out) but that shouldn't be prohibitive. --- src/cargo/core/manifest.rs | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 726091d55b6..b1a54a8e43f 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -156,42 +156,25 @@ macro_rules! get_metadata_env { }; } +struct MetadataEnvs; + macro_rules! metadata_envs { ( $( ($field:ident, $key:literal$(, $to_var:expr)?), )* ) => { - struct MetadataEnvs; impl MetadataEnvs { - $( - fn $field(meta: &ManifestMetadata) -> Cow<'_, str> { - get_metadata_env!(meta, $field$(, $to_var)?) - } - )* - - pub fn should_track(key: &str) -> bool { - let keys = [$($key),*]; - keys.iter().any(|k| *k == key) + fn keys() -> &'static [&'static str] { + &[$($key),*] } - pub fn var<'a>(meta: &'a ManifestMetadata, key: &str) -> Option> { + fn var<'a>(meta: &'a ManifestMetadata, key: &str) -> Option> { match key { - $($key => Some(Self::$field(meta)),)* + $($key => Some(get_metadata_env!(meta, $field$(, $to_var)?)),)* _ => None, } } - - pub fn vars(meta: &ManifestMetadata) -> impl Iterator)> { - [ - $( - ( - $key, - Self::$field(meta), - ), - )* - ].into_iter() - } } } } @@ -213,7 +196,8 @@ metadata_envs! { impl ManifestMetadata { /// Whether the given env var should be tracked by Cargo's dep-info. pub fn should_track(env_key: &str) -> bool { - MetadataEnvs::should_track(env_key) + let keys = MetadataEnvs::keys(); + keys.iter().any(|k| *k == env_key) } pub fn env_var<'a>(&'a self, env_key: &str) -> Option> { @@ -221,7 +205,9 @@ impl ManifestMetadata { } pub fn env_vars(&self) -> impl Iterator)> { - MetadataEnvs::vars(self) + MetadataEnvs::keys() + .iter() + .map(|k| (*k, MetadataEnvs::var(self, k).unwrap())) } } From 0414bb5ac1bc5bf55e79c58370da984b91184ba6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 2 Jan 2025 15:06:17 -0600 Subject: [PATCH 311/525] refactor(manifest): Keep struct/impl close for easier viewing --- src/cargo/core/manifest.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index b1a54a8e43f..0273d8fa4d3 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -147,6 +147,24 @@ pub struct ManifestMetadata { pub rust_version: Option, } +impl ManifestMetadata { + /// Whether the given env var should be tracked by Cargo's dep-info. + pub fn should_track(env_key: &str) -> bool { + let keys = MetadataEnvs::keys(); + keys.iter().any(|k| *k == env_key) + } + + pub fn env_var<'a>(&'a self, env_key: &str) -> Option> { + MetadataEnvs::var(self, env_key) + } + + pub fn env_vars(&self) -> impl Iterator)> { + MetadataEnvs::keys() + .iter() + .map(|k| (*k, MetadataEnvs::var(self, k).unwrap())) + } +} + macro_rules! get_metadata_env { ($meta:ident, $field:ident) => { $meta.$field.as_deref().unwrap_or_default().into() @@ -193,24 +211,6 @@ metadata_envs! { (readme, "CARGO_PKG_README"), } -impl ManifestMetadata { - /// Whether the given env var should be tracked by Cargo's dep-info. - pub fn should_track(env_key: &str) -> bool { - let keys = MetadataEnvs::keys(); - keys.iter().any(|k| *k == env_key) - } - - pub fn env_var<'a>(&'a self, env_key: &str) -> Option> { - MetadataEnvs::var(self, env_key) - } - - pub fn env_vars(&self) -> impl Iterator)> { - MetadataEnvs::keys() - .iter() - .map(|k| (*k, MetadataEnvs::var(self, k).unwrap())) - } -} - #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum TargetKind { Lib(Vec), From 3e9b28a0d6423789f7d7eed9a5b01b634a55326e Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 3 Jan 2025 14:27:19 -0500 Subject: [PATCH 312/525] chore: bump gix-lock to remove thiserror@1 from `cargo` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Other dev tools for cargo development are fine keeping v1. They are not shipped to end users. Before ``` cargo tree --workspace -i thiserror@1.0.63 thiserror v1.0.63 ├── cargo_metadata v0.19.0 │ └── capture v0.1.0 (/projects/cargo/benches/capture) ├── gix-lock v15.0.0 │ ├── gix v0.69.1 │ │ └── cargo v0.86.0 (/projects/cargo) │ │ ├── benchsuite v0.0.0 (/projects/cargo/benches/benchsuite) │ │ ├── resolver-tests v0.0.0 (/projects/cargo/crates/resolver-tests) │ │ ├── xtask-bump-check v0.0.0 (/projects/cargo/crates/xtask-bump-check) │ │ └── xtask-lint-docs v0.1.0 (/projects/cargo/crates/xtask-lint-docs) │ │ [dev-dependencies] │ │ └── cargo v0.86.0 (/projects/cargo) (*) │ ├── gix-index v0.37.0 │ │ ├── gix v0.69.1 (*) │ │ ├── gix-dir v0.11.0 │ │ │ └── gix v0.69.1 (*) │ │ └── gix-worktree v0.38.0 │ │ ├── gix v0.69.1 (*) │ │ └── gix-dir v0.11.0 (*) │ ├── gix-protocol v0.47.0 │ │ └── gix v0.69.1 (*) │ ├── gix-ref v0.49.1 │ │ ├── gix v0.69.1 (*) │ │ ├── gix-config v0.42.0 │ │ │ ├── gix v0.69.1 (*) │ │ │ └── gix-submodule v0.16.0 │ │ │ └── gix v0.69.1 (*) │ │ ├── gix-discover v0.37.0 │ │ │ ├── gix v0.69.1 (*) │ │ │ └── gix-dir v0.11.0 (*) │ │ └── gix-protocol v0.47.0 (*) │ └── gix-shallow v0.1.0 │ ├── gix v0.69.1 (*) │ └── gix-protocol v0.47.0 (*) ├── handlebars v6.2.0 │ └── mdman v0.0.0 (/projects/cargo/crates/mdman) ├── pest v2.7.9 │ ├── handlebars v6.2.0 (*) │ ├── pest_derive v2.7.9 (proc-macro) │ │ └── handlebars v6.2.0 (*) │ ├── pest_generator v2.7.9 │ │ └── pest_derive v2.7.9 (proc-macro) (*) │ └── pest_meta v2.7.9 │ └── pest_generator v2.7.9 (*) ├── varisat v0.2.2 │ └── resolver-tests v0.0.0 (/projects/cargo/crates/resolver-tests) ├── varisat-checker v0.2.2 │ └── varisat v0.2.2 (*) └── varisat-dimacs v0.2.2 ├── varisat v0.2.2 (*) └── varisat-checker v0.2.2 (*) ``` After ``` cargo tree --workspace -i thiserror@1.0.63 thiserror v1.0.63 ├── cargo_metadata v0.19.0 │ └── capture v0.1.0 (/projects/cargo/benches/capture) ├── handlebars v6.2.0 │ └── mdman v0.0.0 (/projects/cargo/crates/mdman) ├── pest v2.7.9 │ ├── handlebars v6.2.0 (*) │ ├── pest_derive v2.7.9 (proc-macro) │ │ └── handlebars v6.2.0 (*) │ ├── pest_generator v2.7.9 │ │ └── pest_derive v2.7.9 (proc-macro) (*) │ └── pest_meta v2.7.9 │ └── pest_generator v2.7.9 (*) ├── varisat v0.2.2 │ └── resolver-tests v0.0.0 (/projects/cargo/crates/resolver-tests) ├── varisat-checker v0.2.2 │ └── varisat v0.2.2 (*) └── varisat-dimacs v0.2.2 ├── varisat v0.2.2 (*) └── varisat-checker v0.2.2 (*) ``` --- Cargo.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b185db5105..68c1d24b178 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1529,13 +1529,13 @@ dependencies = [ [[package]] name = "gix-lock" -version = "15.0.0" +version = "15.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5102acdf4acae2644e38dbbd18cdfba9597a218f7d85f810fe5430207e03c2de" +checksum = "1cd3ab68a452db63d9f3ebdacb10f30dba1fa0d31ac64f4203d395ed1102d940" dependencies = [ "gix-tempfile", "gix-utils", - "thiserror 1.0.63", + "thiserror 2.0.3", ] [[package]] From 22690a32367b27910c805583025ea6fd59dc3293 Mon Sep 17 00:00:00 2001 From: Kornel Date: Sat, 4 Jan 2025 00:22:53 +0000 Subject: [PATCH 313/525] Make --allow-dirty imply --allow-staged --- src/bin/cargo/commands/fix.rs | 8 +++++--- src/cargo/ops/fix.rs | 2 +- src/doc/man/cargo-fix.md | 2 +- tests/testsuite/fix.rs | 6 +++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/bin/cargo/commands/fix.rs b/src/bin/cargo/commands/fix.rs index a4471d7b148..38bd75497a4 100644 --- a/src/bin/cargo/commands/fix.rs +++ b/src/bin/cargo/commands/fix.rs @@ -21,7 +21,7 @@ pub fn cli() -> Command { )) .arg(flag( "allow-dirty", - "Fix code even if the working directory is dirty", + "Fix code even if the working directory is dirty or has staged changes", )) .arg(flag( "allow-staged", @@ -86,6 +86,8 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { opts.filter = ops::CompileFilter::new_all_targets(); } + let allow_dirty = args.flag("allow-dirty"); + ops::fix( gctx, &ws, @@ -94,9 +96,9 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult { edition: args.flag("edition"), idioms: args.flag("edition-idioms"), compile_opts: opts, - allow_dirty: args.flag("allow-dirty"), + allow_dirty, + allow_staged: allow_dirty || args.flag("allow-staged"), allow_no_vcs: args.flag("allow-no-vcs"), - allow_staged: args.flag("allow-staged"), broken_code: args.flag("broken-code"), requested_lockfile_path: lockfile_path, }, diff --git a/src/cargo/ops/fix.rs b/src/cargo/ops/fix.rs index 1530e77f48e..fdd2b33f1df 100644 --- a/src/cargo/ops/fix.rs +++ b/src/cargo/ops/fix.rs @@ -242,7 +242,7 @@ fn check_version_control(gctx: &GlobalContext, opts: &FixOptions) -> CargoResult bail!( "the working directory of this package has uncommitted changes, and \ `cargo fix` can potentially perform destructive changes; if you'd \ - like to suppress this error pass `--allow-dirty`, `--allow-staged`, \ + like to suppress this error pass `--allow-dirty`, \ or commit the changes to these files:\n\ \n\ {}\n\ diff --git a/src/doc/man/cargo-fix.md b/src/doc/man/cargo-fix.md index 2c25720db48..efb116d69a8 100644 --- a/src/doc/man/cargo-fix.md +++ b/src/doc/man/cargo-fix.md @@ -93,7 +93,7 @@ Fix code even if a VCS was not detected. {{/option}} {{#option "`--allow-dirty`" }} -Fix code even if the working directory has changes. +Fix code even if the working directory has changes (including staged changes). {{/option}} {{#option "`--allow-staged`" }} diff --git a/tests/testsuite/fix.rs b/tests/testsuite/fix.rs index f475fe5ba21..cd2de1d75d4 100644 --- a/tests/testsuite/fix.rs +++ b/tests/testsuite/fix.rs @@ -635,7 +635,7 @@ fn warns_about_dirty_working_directory() { p.cargo("fix") .with_status(101) .with_stderr_data(str![[r#" -[ERROR] the working directory of this package has uncommitted changes, and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-dirty`, `--allow-staged`, or commit the changes to these files: +[ERROR] the working directory of this package has uncommitted changes, and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-dirty`, or commit the changes to these files: * src/lib.rs (dirty) @@ -656,7 +656,7 @@ fn warns_about_staged_working_directory() { p.cargo("fix") .with_status(101) .with_stderr_data(str![[r#" -[ERROR] the working directory of this package has uncommitted changes, and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-dirty`, `--allow-staged`, or commit the changes to these files: +[ERROR] the working directory of this package has uncommitted changes, and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-dirty`, or commit the changes to these files: * src/lib.rs (staged) @@ -677,7 +677,7 @@ fn errors_about_untracked_files() { p.cargo("fix") .with_status(101) .with_stderr_data(str![[r#" -[ERROR] the working directory of this package has uncommitted changes, and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-dirty`, `--allow-staged`, or commit the changes to these files: +[ERROR] the working directory of this package has uncommitted changes, and `cargo fix` can potentially perform destructive changes; if you'd like to suppress this error pass `--allow-dirty`, or commit the changes to these files: * Cargo.toml (dirty) * src/ (dirty) From 208f817c83f6bc93d888953564e2807074598554 Mon Sep 17 00:00:00 2001 From: Urgau Date: Thu, 2 Jan 2025 17:13:00 +0100 Subject: [PATCH 314/525] Add preparatory tests for `lib.test = false` check-cfg changes --- tests/testsuite/check_cfg.rs | 121 +++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/tests/testsuite/check_cfg.rs b/tests/testsuite/check_cfg.rs index 2e03254337a..0b3a8d45a91 100644 --- a/tests/testsuite/check_cfg.rs +++ b/tests/testsuite/check_cfg.rs @@ -317,6 +317,127 @@ fn well_known_names_values_doctest() { .run(); } +#[cargo_test] +fn test_false_lib() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [lib] + test = false + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -v") + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) + .run(); + + p.cargo("clean").run(); + p.cargo("test -v") + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) + .run(); + + p.cargo("clean").run(); + p.cargo("test --lib -v") + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) + .run(); +} + +#[cargo_test] +fn test_false_bins() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [[bin]] + name = "daemon" + test = false + path = "src/deamon.rs" + "#, + ) + .file("src/main.rs", "fn main() {}") + .file("src/deamon.rs", "fn main() {}") + .build(); + + p.cargo("check -v") + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) // for foo & deamon + .run(); +} + +#[cargo_test] +fn test_false_examples() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [lib] + test = false + + [[example]] + name = "daemon" + test = false + path = "src/deamon.rs" + "#, + ) + .file("src/lib.rs", "") + .file("src/deamon.rs", "fn main() {}") + .build(); + + p.cargo("check --examples -v") + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) + .run(); +} + +#[cargo_test(nightly, reason = "bench is nightly")] +fn test_false_benches() { + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.0" + edition = "2018" + + [[bench]] + name = "ben1" + test = false + path = "benches/ben1.rs" + "#, + ) + .file("src/lib.rs", "") + .file( + "benches/ben1.rs", + r#" + #![feature(test)] + extern crate test; + #[bench] fn run1(_ben: &mut test::Bencher) { } + "#, + ) + .build(); + + p.cargo("bench --bench ben1 -v") + .with_stderr_contains(x!("rustc" => "cfg" of "docsrs,test")) + .run(); +} + #[cargo_test] fn features_doc() { let p = project() From 973cd5338e31ecb266c5f97df3ce8149c2f205b1 Mon Sep 17 00:00:00 2001 From: Kornel Date: Sat, 4 Jan 2025 14:37:39 +0000 Subject: [PATCH 315/525] Update autogenerated files --- src/doc/man/generated_txt/cargo-fix.txt | 3 +- src/doc/src/commands/cargo-fix.md | 2 +- src/etc/man/cargo-fix.1 | 2 +- .../testsuite/cargo_fix/help/stdout.term.svg | 110 +++++++++--------- 4 files changed, 60 insertions(+), 57 deletions(-) diff --git a/src/doc/man/generated_txt/cargo-fix.txt b/src/doc/man/generated_txt/cargo-fix.txt index 9b056183afa..f15544bfa29 100644 --- a/src/doc/man/generated_txt/cargo-fix.txt +++ b/src/doc/man/generated_txt/cargo-fix.txt @@ -84,7 +84,8 @@ OPTIONS Fix code even if a VCS was not detected. --allow-dirty - Fix code even if the working directory has changes. + Fix code even if the working directory has changes (including staged + changes). --allow-staged Fix code even if the working directory has staged changes. diff --git a/src/doc/src/commands/cargo-fix.md b/src/doc/src/commands/cargo-fix.md index eefcd8a51b9..1a05599b488 100644 --- a/src/doc/src/commands/cargo-fix.md +++ b/src/doc/src/commands/cargo-fix.md @@ -89,7 +89,7 @@ edition.
--allow-dirty
-
Fix code even if the working directory has changes.
+
Fix code even if the working directory has changes (including staged changes).
--allow-staged
diff --git a/src/etc/man/cargo-fix.1 b/src/etc/man/cargo-fix.1 index b96c1e46ad1..f9896b8025a 100644 --- a/src/etc/man/cargo-fix.1 +++ b/src/etc/man/cargo-fix.1 @@ -103,7 +103,7 @@ Fix code even if a VCS was not detected. .sp \fB\-\-allow\-dirty\fR .RS 4 -Fix code even if the working directory has changes. +Fix code even if the working directory has changes (including staged changes). .RE .sp \fB\-\-allow\-staged\fR diff --git a/tests/testsuite/cargo_fix/help/stdout.term.svg b/tests/testsuite/cargo_fix/help/stdout.term.svg index 3268041f100..1c860989650 100644 --- a/tests/testsuite/cargo_fix/help/stdout.term.svg +++ b/tests/testsuite/cargo_fix/help/stdout.term.svg @@ -1,4 +1,4 @@ - + *, ? and []. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or -double quotes around each pattern. - - -Selecting more than one package with this option is unstable and available only +double quotes around each pattern.

+

Selecting more than one package with this option is unstable and available only on the -[nightly channel](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html) -and requires the `-Z package-workspace` flag to enable. -See for more information. +nightly channel +and requires the -Z package-workspace flag to enable. +See https://github.com/rust-lang/cargo/issues/10948 for more information. +

--workspace
Publish all members in the workspace.

diff --git a/src/etc/man/cargo-publish.1 b/src/etc/man/cargo-publish.1 index 613a1ecdbab..6f9fd22d43a 100644 --- a/src/etc/man/cargo-publish.1 +++ b/src/etc/man/cargo-publish.1 @@ -113,12 +113,13 @@ SPEC format. This flag may be specified multiple times and supports common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally expanding glob patterns before Cargo handles them, you must use single quotes or double quotes around each pattern. -.RE +.sp Selecting more than one package with this option is unstable and available only on the -[nightly channel](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html) -and requires the `-Z package-workspace` flag to enable. -See for more information. +\fInightly channel\fR +and requires the \fB\-Z package\-workspace\fR flag to enable. +See for more information. +.RE .sp \fB\-\-workspace\fR .RS 4 From 280bc1f68bdd9609aba1feb86e03ba909e90852d Mon Sep 17 00:00:00 2001 From: Orion Gonzalez Date: Mon, 17 Feb 2025 22:19:32 +0100 Subject: [PATCH 461/525] docs: Improve comments --- src/cargo/ops/fix.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cargo/ops/fix.rs b/src/cargo/ops/fix.rs index 1d5d5a94009..727201bab84 100644 --- a/src/cargo/ops/fix.rs +++ b/src/cargo/ops/fix.rs @@ -4,27 +4,27 @@ //! diagnostics with suggested fixes that can be applied to the files on the //! filesystem, and validate that those changes didn't break anything. //! -//! Cargo begins by launching a `LockServer` thread in the background to +//! Cargo begins by launching a [`LockServer`] thread in the background to //! listen for network connections to coordinate locking when multiple targets //! are built simultaneously. It ensures each package has only one fix running //! at once. //! -//! The `RustfixDiagnosticServer` is launched in a background thread (in +//! The [`RustfixDiagnosticServer`] is launched in a background thread (in //! `JobQueue`) to listen for network connections to coordinate displaying //! messages to the user on the console (so that multiple processes don't try //! to print at the same time). //! //! Cargo begins a normal `cargo check` operation with itself set as a proxy -//! for rustc by setting `primary_unit_rustc` in the build config. When +//! for rustc by setting `BuildConfig::primary_unit_rustc` in the build config. When //! cargo launches rustc to check a crate, it is actually launching itself. -//! The `FIX_ENV_INTERNAL` environment variable is set so that cargo knows it is in -//! fix-proxy-mode. +//! The `FIX_ENV_INTERNAL` environment variable is set to the value of the [`LockServer`]'s +//! address so that cargo knows it is in fix-proxy-mode. //! //! Each proxied cargo-as-rustc detects it is in fix-proxy-mode (via `FIX_ENV_INTERNAL` //! environment variable in `main`) and does the following: //! -//! - Acquire a lock from the `LockServer` from the master cargo process. -//! - Launches the real rustc (`rustfix_and_fix`), looking at the JSON output +//! - Acquire a lock from the [`LockServer`] from the master cargo process. +//! - Launches the real rustc ([`rustfix_and_fix`]), looking at the JSON output //! for suggested fixes. //! - Uses the `rustfix` crate to apply the suggestions to the files on the //! file system. From 1f34b007e8710d6ce23c7ca8c5dfdc1e44c2a412 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 18 Feb 2025 12:20:00 -0600 Subject: [PATCH 462/525] test(add): Show behavior with unrecognized feature with large list --- .../in/Cargo.toml | 51 +++++++++++ .../in/src/lib.rs | 0 .../mod.rs | 35 ++++++++ .../out/Cargo.toml | 51 +++++++++++ .../stderr.term.svg | 50 +++++++++++ .../in/Cargo.toml | 12 +++ .../in/src/lib.rs | 0 .../mod.rs | 35 ++++++++ .../out/Cargo.toml | 12 +++ .../stderr.term.svg | 84 +++++++++++++++++++ tests/testsuite/cargo_add/mod.rs | 2 + 11 files changed, 332 insertions(+) create mode 100644 tests/testsuite/cargo_add/features_error_activated_over_limit/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/features_error_activated_over_limit/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/features_error_activated_over_limit/mod.rs create mode 100644 tests/testsuite/cargo_add/features_error_activated_over_limit/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/features_error_activated_over_limit/stderr.term.svg create mode 100644 tests/testsuite/cargo_add/features_error_deactivated_over_limit/in/Cargo.toml create mode 100644 tests/testsuite/cargo_add/features_error_deactivated_over_limit/in/src/lib.rs create mode 100644 tests/testsuite/cargo_add/features_error_deactivated_over_limit/mod.rs create mode 100644 tests/testsuite/cargo_add/features_error_deactivated_over_limit/out/Cargo.toml create mode 100644 tests/testsuite/cargo_add/features_error_deactivated_over_limit/stderr.term.svg diff --git a/tests/testsuite/cargo_add/features_error_activated_over_limit/in/Cargo.toml b/tests/testsuite/cargo_add/features_error_activated_over_limit/in/Cargo.toml new file mode 100644 index 00000000000..8624bcb417b --- /dev/null +++ b/tests/testsuite/cargo_add/features_error_activated_over_limit/in/Cargo.toml @@ -0,0 +1,51 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" +edition = "2015" + +[dependencies.your-face] +version = "99999.0.0" +features = [ + "eyes000", + "eyes001", + "eyes002", + "eyes003", + "eyes004", + "eyes005", + "eyes006", + "eyes007", + "eyes008", + "eyes009", + "eyes010", + "eyes011", + "eyes012", + "eyes013", + "eyes014", + "eyes015", + "eyes016", + "eyes017", + "eyes018", + "eyes019", + "eyes020", + "eyes021", + "eyes022", + "eyes023", + "eyes024", + "eyes025", + "eyes026", + "eyes027", + "eyes028", + "eyes029", + "eyes030", + "eyes031", + "eyes032", + "eyes033", + "eyes034", + "eyes035", + "eyes036", + "eyes037", + "eyes038", + "eyes039", +] diff --git a/tests/testsuite/cargo_add/features_error_activated_over_limit/in/src/lib.rs b/tests/testsuite/cargo_add/features_error_activated_over_limit/in/src/lib.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testsuite/cargo_add/features_error_activated_over_limit/mod.rs b/tests/testsuite/cargo_add/features_error_activated_over_limit/mod.rs new file mode 100644 index 00000000000..5be109b8746 --- /dev/null +++ b/tests/testsuite/cargo_add/features_error_activated_over_limit/mod.rs @@ -0,0 +1,35 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::current_dir; +use cargo_test_support::file; +use cargo_test_support::prelude::*; +use cargo_test_support::str; +use cargo_test_support::Project; + +#[cargo_test] +fn case() { + const MANY_FEATURES_COUNT: usize = 50; + + cargo_test_support::registry::init(); + let mut test_package = + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package"); + for i in 0..MANY_FEATURES_COUNT { + test_package.feature(format!("eyes{i:03}").as_str(), &[]); + } + test_package.publish(); + + let project = Project::from_template(current_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + let features = "eees100,eees101"; + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line(format!("your-face --features {features}").as_str()) + .current_dir(cwd) + .assert() + .failure() + .stdout_eq(str![""]) + .stderr_eq(file!["stderr.term.svg"]); + + assert_ui().subset_matches(current_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/features_error_activated_over_limit/out/Cargo.toml b/tests/testsuite/cargo_add/features_error_activated_over_limit/out/Cargo.toml new file mode 100644 index 00000000000..8624bcb417b --- /dev/null +++ b/tests/testsuite/cargo_add/features_error_activated_over_limit/out/Cargo.toml @@ -0,0 +1,51 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" +edition = "2015" + +[dependencies.your-face] +version = "99999.0.0" +features = [ + "eyes000", + "eyes001", + "eyes002", + "eyes003", + "eyes004", + "eyes005", + "eyes006", + "eyes007", + "eyes008", + "eyes009", + "eyes010", + "eyes011", + "eyes012", + "eyes013", + "eyes014", + "eyes015", + "eyes016", + "eyes017", + "eyes018", + "eyes019", + "eyes020", + "eyes021", + "eyes022", + "eyes023", + "eyes024", + "eyes025", + "eyes026", + "eyes027", + "eyes028", + "eyes029", + "eyes030", + "eyes031", + "eyes032", + "eyes033", + "eyes034", + "eyes035", + "eyes036", + "eyes037", + "eyes038", + "eyes039", +] diff --git a/tests/testsuite/cargo_add/features_error_activated_over_limit/stderr.term.svg b/tests/testsuite/cargo_add/features_error_activated_over_limit/stderr.term.svg new file mode 100644 index 00000000000..6042124bc75 --- /dev/null +++ b/tests/testsuite/cargo_add/features_error_activated_over_limit/stderr.term.svg @@ -0,0 +1,50 @@ + + + + + + + Updating `dummy-registry` index + + Adding your-face v99999.0.0 to dependencies + + error: unrecognized features for crate your-face: eees100, eees101 + + disabled features: + + eyes040, eyes041, eyes042, eyes043, eyes044, eyes045, eyes046, eyes047, eyes048 + + eyes049 + + enabled features: + + eyes000, eyes001, eyes002, eyes003, eyes004, eyes005, eyes006, eyes007, eyes008 + + eyes009, eyes010, eyes011, eyes012, eyes013, eyes014, eyes015, eyes016, eyes017 + + eyes018, eyes019, eyes020, eyes021, eyes022, eyes023, eyes024, eyes025, eyes026 + + eyes027, eyes028, eyes029, eyes030, eyes031, eyes032, eyes033, eyes034, eyes035 + + eyes036, eyes037, eyes038, eyes039 + + + + + + diff --git a/tests/testsuite/cargo_add/features_error_deactivated_over_limit/in/Cargo.toml b/tests/testsuite/cargo_add/features_error_deactivated_over_limit/in/Cargo.toml new file mode 100644 index 00000000000..25f3db9b042 --- /dev/null +++ b/tests/testsuite/cargo_add/features_error_deactivated_over_limit/in/Cargo.toml @@ -0,0 +1,12 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" +edition = "2015" + +[dependencies.your-face] +version = "99999.0.0" +features = [ + "eyes000", +] diff --git a/tests/testsuite/cargo_add/features_error_deactivated_over_limit/in/src/lib.rs b/tests/testsuite/cargo_add/features_error_deactivated_over_limit/in/src/lib.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testsuite/cargo_add/features_error_deactivated_over_limit/mod.rs b/tests/testsuite/cargo_add/features_error_deactivated_over_limit/mod.rs new file mode 100644 index 00000000000..9724b2c5119 --- /dev/null +++ b/tests/testsuite/cargo_add/features_error_deactivated_over_limit/mod.rs @@ -0,0 +1,35 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::current_dir; +use cargo_test_support::file; +use cargo_test_support::prelude::*; +use cargo_test_support::str; +use cargo_test_support::Project; + +#[cargo_test] +fn case() { + const MANY_FEATURES_COUNT: usize = 200; + + cargo_test_support::registry::init(); + let mut test_package = + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package"); + for i in 0..MANY_FEATURES_COUNT { + test_package.feature(format!("eyes{i:03}").as_str(), &[]); + } + test_package.publish(); + + let project = Project::from_template(current_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + let features = "eees100,eees101"; + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line(format!("your-face --features {features}").as_str()) + .current_dir(cwd) + .assert() + .failure() + .stdout_eq(str![""]) + .stderr_eq(file!["stderr.term.svg"]); + + assert_ui().subset_matches(current_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/features_error_deactivated_over_limit/out/Cargo.toml b/tests/testsuite/cargo_add/features_error_deactivated_over_limit/out/Cargo.toml new file mode 100644 index 00000000000..25f3db9b042 --- /dev/null +++ b/tests/testsuite/cargo_add/features_error_deactivated_over_limit/out/Cargo.toml @@ -0,0 +1,12 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" +edition = "2015" + +[dependencies.your-face] +version = "99999.0.0" +features = [ + "eyes000", +] diff --git a/tests/testsuite/cargo_add/features_error_deactivated_over_limit/stderr.term.svg b/tests/testsuite/cargo_add/features_error_deactivated_over_limit/stderr.term.svg new file mode 100644 index 00000000000..e0ceba02230 --- /dev/null +++ b/tests/testsuite/cargo_add/features_error_deactivated_over_limit/stderr.term.svg @@ -0,0 +1,84 @@ + + + + + + + Updating `dummy-registry` index + + Adding your-face v99999.0.0 to dependencies + + error: unrecognized features for crate your-face: eees100, eees101 + + disabled features: + + eyes001, eyes002, eyes003, eyes004, eyes005, eyes006, eyes007, eyes008, eyes009 + + eyes010, eyes011, eyes012, eyes013, eyes014, eyes015, eyes016, eyes017, eyes018 + + eyes019, eyes020, eyes021, eyes022, eyes023, eyes024, eyes025, eyes026, eyes027 + + eyes028, eyes029, eyes030, eyes031, eyes032, eyes033, eyes034, eyes035, eyes036 + + eyes037, eyes038, eyes039, eyes040, eyes041, eyes042, eyes043, eyes044, eyes045 + + eyes046, eyes047, eyes048, eyes049, eyes050, eyes051, eyes052, eyes053, eyes054 + + eyes055, eyes056, eyes057, eyes058, eyes059, eyes060, eyes061, eyes062, eyes063 + + eyes064, eyes065, eyes066, eyes067, eyes068, eyes069, eyes070, eyes071, eyes072 + + eyes073, eyes074, eyes075, eyes076, eyes077, eyes078, eyes079, eyes080, eyes081 + + eyes082, eyes083, eyes084, eyes085, eyes086, eyes087, eyes088, eyes089, eyes090 + + eyes091, eyes092, eyes093, eyes094, eyes095, eyes096, eyes097, eyes098, eyes099 + + eyes100, eyes101, eyes102, eyes103, eyes104, eyes105, eyes106, eyes107, eyes108 + + eyes109, eyes110, eyes111, eyes112, eyes113, eyes114, eyes115, eyes116, eyes117 + + eyes118, eyes119, eyes120, eyes121, eyes122, eyes123, eyes124, eyes125, eyes126 + + eyes127, eyes128, eyes129, eyes130, eyes131, eyes132, eyes133, eyes134, eyes135 + + eyes136, eyes137, eyes138, eyes139, eyes140, eyes141, eyes142, eyes143, eyes144 + + eyes145, eyes146, eyes147, eyes148, eyes149, eyes150, eyes151, eyes152, eyes153 + + eyes154, eyes155, eyes156, eyes157, eyes158, eyes159, eyes160, eyes161, eyes162 + + eyes163, eyes164, eyes165, eyes166, eyes167, eyes168, eyes169, eyes170, eyes171 + + eyes172, eyes173, eyes174, eyes175, eyes176, eyes177, eyes178, eyes179, eyes180 + + eyes181, eyes182, eyes183, eyes184, eyes185, eyes186, eyes187, eyes188, eyes189 + + eyes190, eyes191, eyes192, eyes193, eyes194, eyes195, eyes196, eyes197, eyes198 + + eyes199 + + enabled features: + + eyes000 + + + + + + diff --git a/tests/testsuite/cargo_add/mod.rs b/tests/testsuite/cargo_add/mod.rs index 35ab71346c6..a2881cfbca2 100644 --- a/tests/testsuite/cargo_add/mod.rs +++ b/tests/testsuite/cargo_add/mod.rs @@ -25,6 +25,8 @@ mod features; mod features_activated_over_limit; mod features_deactivated_over_limit; mod features_empty; +mod features_error_activated_over_limit; +mod features_error_deactivated_over_limit; mod features_multiple_occurrences; mod features_preserve; mod features_spaced_values; From 4a0b8948dda8006655efc162c340ef9e6dfa33ab Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 18 Feb 2025 12:14:40 -0600 Subject: [PATCH 463/525] refactor(add): Pull out feature count for reuse --- src/cargo/ops/cargo_add/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cargo/ops/cargo_add/mod.rs b/src/cargo/ops/cargo_add/mod.rs index 85497718168..a89788e5c65 100644 --- a/src/cargo/ops/cargo_add/mod.rs +++ b/src/cargo/ops/cargo_add/mod.rs @@ -43,6 +43,8 @@ use crate::CargoResult; use crate::GlobalContext; use crate_spec::CrateSpec; +const MAX_FEATURE_PRINTS: usize = 30; + /// Information on what dependencies should be added #[derive(Clone, Debug)] pub struct AddOptions<'a> { @@ -1114,7 +1116,6 @@ fn print_dep_table_msg(shell: &mut Shell, dep: &DependencyUI) -> CargoResult<()> writeln!(stderr, "{prefix}Features{suffix}:")?; - const MAX_FEATURE_PRINTS: usize = 30; let total_activated = activated.len(); let total_deactivated = deactivated.len(); From 524d123332aea97440ff7b472baf47058ccc1eba Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 18 Feb 2025 12:44:13 -0600 Subject: [PATCH 464/525] fix(add): Focus on error, rather than large feature lists Inspired by #11100 and previous work to collapse feature lists down. --- src/cargo/ops/cargo_add/mod.rs | 64 +++++++++++-------- .../stderr.term.svg | 16 +---- .../stderr.term.svg | 56 ++-------------- 3 files changed, 43 insertions(+), 93 deletions(-) diff --git a/src/cargo/ops/cargo_add/mod.rs b/src/cargo/ops/cargo_add/mod.rs index a89788e5c65..a88354a3694 100644 --- a/src/cargo/ops/cargo_add/mod.rs +++ b/src/cargo/ops/cargo_add/mod.rs @@ -168,36 +168,44 @@ pub fn add(workspace: &Workspace<'_>, options: &AddOptions<'_>) -> CargoResult<( write!(message, "no features available for crate {}", dep.name)?; } else { if !deactivated.is_empty() { - writeln!( - message, - "disabled features:\n {}", - deactivated - .iter() - .map(|s| s.to_string()) - .coalesce(|x, y| if x.len() + y.len() < 78 { - Ok(format!("{x}, {y}")) - } else { - Err((x, y)) - }) - .into_iter() - .format("\n ") - )? + if deactivated.len() <= MAX_FEATURE_PRINTS { + writeln!( + message, + "disabled features:\n {}", + deactivated + .iter() + .map(|s| s.to_string()) + .coalesce(|x, y| if x.len() + y.len() < 78 { + Ok(format!("{x}, {y}")) + } else { + Err((x, y)) + }) + .into_iter() + .format("\n ") + )?; + } else { + writeln!(message, "{} disabled features available", deactivated.len())?; + } } if !activated.is_empty() { - writeln!( - message, - "enabled features:\n {}", - activated - .iter() - .map(|s| s.to_string()) - .coalesce(|x, y| if x.len() + y.len() < 78 { - Ok(format!("{x}, {y}")) - } else { - Err((x, y)) - }) - .into_iter() - .format("\n ") - )? + if deactivated.len() + activated.len() <= MAX_FEATURE_PRINTS { + writeln!( + message, + "enabled features:\n {}", + activated + .iter() + .map(|s| s.to_string()) + .coalesce(|x, y| if x.len() + y.len() < 78 { + Ok(format!("{x}, {y}")) + } else { + Err((x, y)) + }) + .into_iter() + .format("\n ") + )?; + } else { + writeln!(message, "{} enabled features available", activated.len())?; + } } } anyhow::bail!(message.trim().to_owned()); diff --git a/tests/testsuite/cargo_add/features_error_activated_over_limit/stderr.term.svg b/tests/testsuite/cargo_add/features_error_activated_over_limit/stderr.term.svg index 6042124bc75..21b4d42f539 100644 --- a/tests/testsuite/cargo_add/features_error_activated_over_limit/stderr.term.svg +++ b/tests/testsuite/cargo_add/features_error_activated_over_limit/stderr.term.svg @@ -1,4 +1,4 @@ - +
+/// +/// Note: For index files, `dep_name` must have had `to_lowercase` called on it. +/// +///
+/// /// [1]: https://docs.rs/cargo/latest/cargo/sources/registry/index.html#the-format-of-the-index /// [2]: https://github.com/rust-lang/crates.io-index pub fn make_dep_path(dep_name: &str, prefix_only: bool) -> String { diff --git a/src/cargo/sources/registry/index/mod.rs b/src/cargo/sources/registry/index/mod.rs index 87d5b0f9555..bfbf67db7ca 100644 --- a/src/cargo/sources/registry/index/mod.rs +++ b/src/cargo/sources/registry/index/mod.rs @@ -584,19 +584,19 @@ impl Summaries { ) -> Poll>> { // This is the file we're loading from cache or the index data. // See module comment in `registry/mod.rs` for why this is structured the way it is. - let name = &name.to_lowercase(); - let relative = make_dep_path(&name, false); + let lowered_name = &name.to_lowercase(); + let relative = make_dep_path(&lowered_name, false); let mut cached_summaries = None; let mut index_version = None; - if let Some(contents) = cache_manager.get(name) { + if let Some(contents) = cache_manager.get(lowered_name) { match Summaries::parse_cache(contents) { Ok((s, v)) => { cached_summaries = Some(s); index_version = Some(v); } Err(e) => { - tracing::debug!("failed to parse {name:?} cache: {e}"); + tracing::debug!("failed to parse {lowered_name:?} cache: {e}"); } } } @@ -609,7 +609,7 @@ impl Summaries { return Poll::Ready(Ok(cached_summaries)); } LoadResponse::NotFound => { - cache_manager.invalidate(name); + cache_manager.invalidate(lowered_name); return Poll::Ready(Ok(None)); } LoadResponse::Data { @@ -658,7 +658,7 @@ impl Summaries { // Once we have our `cache_bytes` which represents the `Summaries` we're // about to return, write that back out to disk so future Cargo // invocations can use it. - cache_manager.put(name, &cache_bytes); + cache_manager.put(lowered_name, &cache_bytes); // If we've got debug assertions enabled read back in the cached values // and assert they match the expected result. From 19d307170fac2b7a60593d2161de3c05c393fa57 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 21 Feb 2025 08:24:39 -0600 Subject: [PATCH 480/525] fix(package): Fix lookups to capitalized workspace member's index entry --- src/cargo/ops/cargo_package/mod.rs | 3 ++- tests/testsuite/package.rs | 8 +------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/cargo/ops/cargo_package/mod.rs b/src/cargo/ops/cargo_package/mod.rs index 6c920eec4e3..5e5f4ff191d 100644 --- a/src/cargo/ops/cargo_package/mod.rs +++ b/src/cargo/ops/cargo_package/mod.rs @@ -1068,7 +1068,8 @@ impl<'a> TmpRegistry<'a> { v: Some(2), })?; - let file = cargo_util::registry::make_dep_path(package.name().as_str(), false); + let file = + cargo_util::registry::make_dep_path(&package.name().as_str().to_lowercase(), false); let mut dst = self.index_path().open_rw_exclusive_create( file, self.gctx, diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index c8df0a2788f..4d981890ffb 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -6478,19 +6478,13 @@ fn workspace_with_capitalized_member() { p.cargo("package -Zpackage-workspace --no-verify") .masquerade_as_nightly_cargo(&["package-workspace"]) .replace_crates_io(reg.index_url()) - .with_status(101) .with_stderr_data( str![[r#" [PACKAGING] main v0.0.1 ([ROOT]/foo/main) [UPDATING] crates.io index [PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) [PACKAGING] DEP v0.1.0 ([ROOT]/foo/dep) -[ERROR] failed to prepare local package for uploading - -Caused by: - no matching package named `DEP` found - location searched: crates.io index - required by package `main v0.0.1 ([ROOT]/foo/main)` +[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) "#]] .unordered(), From 4213e921caf8f34ded54588ce22b643da501d687 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 21 Feb 2025 08:39:40 -0600 Subject: [PATCH 481/525] test(package): Show behavior with renamed members --- tests/testsuite/package.rs | 112 +++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index 4d981890ffb..3edd5fe27bf 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -6492,6 +6492,118 @@ fn workspace_with_capitalized_member() { .run(); } +#[cargo_test] +fn workspace_with_renamed_member() { + let reg = registry::init(); + + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["crates/*"] + "#, + ) + .file( + "crates/val-json/Cargo.toml", + r#" + [package] + name = "obeli-sk-val-json" + version = "0.16.2" + edition = "2015" + authors = [] + license = "MIT" + description = "main" + repository = "bar" + + [dependencies] + "#, + ) + .file("crates/val-json/src/lib.rs", "pub fn foo() {}") + .file( + "crates/concepts/Cargo.toml", + r#" + [package] + name = "obeli-sk-concepts" + version = "0.16.2" + edition = "2015" + authors = [] + license = "MIT" + description = "main" + repository = "bar" + + [dependencies] + val-json = { package = "obeli-sk-val-json", path = "../val-json", version = "0.16.2" } + "#, + ) + .file( + "crates/concepts/src/lib.rs", + "pub fn foo() { val_json::foo() }", + ) + .file( + "crates/utils/Cargo.toml", + r#" + [package] + name = "obeli-sk-utils" + version = "0.16.2" + edition = "2015" + authors = [] + license = "MIT" + description = "main" + repository = "bar" + + [dependencies] + concepts = { package = "obeli-sk-concepts", path = "../concepts", version = "0.16.2" } + val-json = { package = "obeli-sk-val-json", path = "../val-json", version = "0.16.2" } + "#, + ) + .file( + "crates/utils/src/lib.rs", + "pub fn foo() { val_json::foo(); concepts::foo(); }", + ) + .build(); + + p.cargo("package -Zpackage-workspace") + .masquerade_as_nightly_cargo(&["package-workspace"]) + .replace_crates_io(reg.index_url()) + .with_status(101) + .with_stderr_data( + str![[r#" +[UPDATING] crates.io index +[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[PACKAGING] obeli-sk-val-json v0.16.2 ([ROOT]/foo/crates/val-json) +[PACKAGING] obeli-sk-concepts v0.16.2 ([ROOT]/foo/crates/concepts) +[PACKAGING] obeli-sk-utils v0.16.2 ([ROOT]/foo/crates/utils) +[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[VERIFYING] obeli-sk-val-json v0.16.2 ([ROOT]/foo/crates/val-json) +[COMPILING] obeli-sk-val-json v0.16.2 ([ROOT]/foo/target/package/obeli-sk-val-json-0.16.2) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s +[VERIFYING] obeli-sk-concepts v0.16.2 ([ROOT]/foo/crates/concepts) +[UNPACKING] obeli-sk-val-json v0.16.2 (registry `[ROOT]/foo/target/package/tmp-registry`) +[COMPILING] obeli-sk-val-json v0.16.2 +[COMPILING] obeli-sk-concepts v0.16.2 ([ROOT]/foo/target/package/obeli-sk-concepts-0.16.2) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s +[VERIFYING] obeli-sk-utils v0.16.2 ([ROOT]/foo/crates/utils) +[UNPACKING] obeli-sk-concepts v0.16.2 (registry `[ROOT]/foo/target/package/tmp-registry`) +[COMPILING] obeli-sk-val-json v0.16.2 +[COMPILING] obeli-sk-concepts v0.16.2 +error[E0433]: failed to resolve: use of undeclared crate or module `val_json` + --> [ROOT]/home/.cargo/registry/src/-[HASH]/obeli-sk-concepts-0.16.2/src/lib.rs:1:16 + | +1 | pub fn foo() { val_json::foo() } + | ^^^^^^^^ use of undeclared crate or module `val_json` + +For more information about this error, try `rustc --explain E0433`. +[ERROR] could not compile `obeli-sk-concepts` (lib) due to 1 previous error +[ERROR] failed to verify package tarball + +"#]] + .unordered(), + ) + .run(); +} + #[cargo_test] fn registry_not_in_publish_list() { let p = project() From 9c26bf0edf78b1c7b936e3d8c85c62fb219e92b4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Feb 2025 14:54:48 +0000 Subject: [PATCH 482/525] chore(deps): update msrv (3 versions) to v1.83 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a3ac43e8235..e40b5fae6c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ exclude = [ ] [workspace.package] -rust-version = "1.82" # MSRV:3 +rust-version = "1.83" # MSRV:3 edition = "2021" license = "MIT OR Apache-2.0" homepage = "https://github.com/rust-lang/cargo" From 88f858a5b5bb7b8ca7ba2daa22037e2cf9907de6 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 21 Feb 2025 09:11:33 -0600 Subject: [PATCH 483/525] refactor(bump): Visual group related operations --- crates/xtask-bump-check/src/xtask.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/xtask-bump-check/src/xtask.rs b/crates/xtask-bump-check/src/xtask.rs index 60dbc753856..d83e0db6337 100644 --- a/crates/xtask-bump-check/src/xtask.rs +++ b/crates/xtask-bump-check/src/xtask.rs @@ -114,7 +114,6 @@ fn bump_check(args: &clap::ArgMatches, gctx: &cargo::util::GlobalContext) -> Car let base_commit = get_base_commit(gctx, args, &repo)?; let head_commit = get_head_commit(args, &repo)?; let referenced_commit = get_referenced_commit(&repo, &base_commit)?; - let changed_members = changed(&ws, &repo, &base_commit, &head_commit)?; let status = |msg: &str| gctx.shell().status(STATUS, msg); let crates_not_check_against_channels = [ @@ -135,9 +134,8 @@ fn bump_check(args: &clap::ArgMatches, gctx: &cargo::util::GlobalContext) -> Car status(&format!("head commit `{}`", head_commit.id()))?; let mut needs_bump = Vec::new(); - + let changed_members = changed(&ws, &repo, &base_commit, &head_commit)?; check_crates_io(&ws, &changed_members, &mut needs_bump)?; - if let Some(referenced_commit) = referenced_commit.as_ref() { status(&format!("compare against `{}`", referenced_commit.id()))?; for referenced_member in checkout_ws(&ws, &repo, referenced_commit)?.members() { @@ -157,7 +155,6 @@ fn bump_check(args: &clap::ArgMatches, gctx: &cargo::util::GlobalContext) -> Car } } } - if !needs_bump.is_empty() { needs_bump.sort(); needs_bump.dedup(); From f035814fe2a87823c5ac05fc3d036bb1ed9edd49 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 21 Feb 2025 09:13:36 -0600 Subject: [PATCH 484/525] chore(ci): Visually group output in Github --- ci/validate-version-bump.sh | 5 ++++- crates/xtask-bump-check/src/xtask.rs | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/ci/validate-version-bump.sh b/ci/validate-version-bump.sh index 94896711f18..5436c3f78d9 100755 --- a/ci/validate-version-bump.sh +++ b/ci/validate-version-bump.sh @@ -19,4 +19,7 @@ head_sha=$(git rev-parse "${HEAD_SHA:-HEAD}") echo "Base revision is $base_sha" echo "Head revision is $head_sha" -cargo bump-check --base-rev "$base_sha" --head-rev "$head_sha" +echo "::group::Building xtask" +cargo bump-check --help +echo "::endgroup::" +cargo bump-check --github --base-rev "$base_sha" --head-rev "$head_sha" diff --git a/crates/xtask-bump-check/src/xtask.rs b/crates/xtask-bump-check/src/xtask.rs index d83e0db6337..a35a3f449b0 100644 --- a/crates/xtask-bump-check/src/xtask.rs +++ b/crates/xtask-bump-check/src/xtask.rs @@ -10,6 +10,8 @@ //! but forgot to bump its version. //! ``` +#![allow(clippy::print_stdout)] // Fine for build utilities + use std::collections::HashMap; use std::fmt::Write; use std::fs; @@ -56,6 +58,7 @@ pub fn cli() -> clap::Command { .arg(flag("locked", "Require Cargo.lock to be up-to-date").global(true)) .arg(flag("offline", "Run without accessing the network").global(true)) .arg(multi_opt("config", "KEY=VALUE", "Override a configuration value").global(true)) + .arg(flag("github", "Group output using GitHub's syntax")) .arg( Arg::new("unstable-features") .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details") @@ -114,6 +117,7 @@ fn bump_check(args: &clap::ArgMatches, gctx: &cargo::util::GlobalContext) -> Car let base_commit = get_base_commit(gctx, args, &repo)?; let head_commit = get_head_commit(args, &repo)?; let referenced_commit = get_referenced_commit(&repo, &base_commit)?; + let github = args.get_flag("github"); let status = |msg: &str| gctx.shell().status(STATUS, msg); let crates_not_check_against_channels = [ @@ -134,6 +138,9 @@ fn bump_check(args: &clap::ArgMatches, gctx: &cargo::util::GlobalContext) -> Car status(&format!("head commit `{}`", head_commit.id()))?; let mut needs_bump = Vec::new(); + if github { + println!("::group::Checking for bumps of changed packages"); + } let changed_members = changed(&ws, &repo, &base_commit, &head_commit)?; check_crates_io(&ws, &changed_members, &mut needs_bump)?; if let Some(referenced_commit) = referenced_commit.as_ref() { @@ -166,18 +173,30 @@ fn bump_check(args: &clap::ArgMatches, gctx: &cargo::util::GlobalContext) -> Car msg.push_str("\nPlease bump at least one patch version in each corresponding Cargo.toml."); anyhow::bail!(msg) } + if github { + println!("::endgroup::"); + } // Even when we test against baseline-rev, we still need to make sure a // change doesn't violate SemVer rules against crates.io releases. The // possibility of this happening is nearly zero but no harm to check twice. + if github { + println!("::group::SemVer Checks against crates.io"); + } let mut cmd = ProcessBuilder::new("cargo"); cmd.arg("semver-checks") .arg("check-release") .arg("--workspace"); gctx.shell().status("Running", &cmd)?; cmd.exec()?; + if github { + println!("::endgroup::"); + } if let Some(referenced_commit) = referenced_commit.as_ref() { + if github { + println!("::group::SemVer Checks against {}", referenced_commit.id()); + } let mut cmd = ProcessBuilder::new("cargo"); cmd.arg("semver-checks") .arg("--workspace") @@ -189,6 +208,9 @@ fn bump_check(args: &clap::ArgMatches, gctx: &cargo::util::GlobalContext) -> Car } gctx.shell().status("Running", &cmd)?; cmd.exec()?; + if github { + println!("::endgroup::"); + } } status("no version bump needed for member crates.")?; From 05228f76f46c8c8e810d195aa25e0ab48fb3b04a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 21 Feb 2025 09:50:36 -0600 Subject: [PATCH 485/525] chore(ci): Check against beta first Most breaking changes should be against beta and it would be good to have the context of whether we deviated from beta before checking against stable. --- crates/xtask-bump-check/src/xtask.rs | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/xtask-bump-check/src/xtask.rs b/crates/xtask-bump-check/src/xtask.rs index a35a3f449b0..18b9baa3c6d 100644 --- a/crates/xtask-bump-check/src/xtask.rs +++ b/crates/xtask-bump-check/src/xtask.rs @@ -177,22 +177,6 @@ fn bump_check(args: &clap::ArgMatches, gctx: &cargo::util::GlobalContext) -> Car println!("::endgroup::"); } - // Even when we test against baseline-rev, we still need to make sure a - // change doesn't violate SemVer rules against crates.io releases. The - // possibility of this happening is nearly zero but no harm to check twice. - if github { - println!("::group::SemVer Checks against crates.io"); - } - let mut cmd = ProcessBuilder::new("cargo"); - cmd.arg("semver-checks") - .arg("check-release") - .arg("--workspace"); - gctx.shell().status("Running", &cmd)?; - cmd.exec()?; - if github { - println!("::endgroup::"); - } - if let Some(referenced_commit) = referenced_commit.as_ref() { if github { println!("::group::SemVer Checks against {}", referenced_commit.id()); @@ -213,6 +197,22 @@ fn bump_check(args: &clap::ArgMatches, gctx: &cargo::util::GlobalContext) -> Car } } + // Even when we test against baseline-rev, we still need to make sure a + // change doesn't violate SemVer rules against crates.io releases. The + // possibility of this happening is nearly zero but no harm to check twice. + if github { + println!("::group::SemVer Checks against crates.io"); + } + let mut cmd = ProcessBuilder::new("cargo"); + cmd.arg("semver-checks") + .arg("check-release") + .arg("--workspace"); + gctx.shell().status("Running", &cmd)?; + cmd.exec()?; + if github { + println!("::endgroup::"); + } + status("no version bump needed for member crates.")?; Ok(()) From 44970a825d11b42ac8a2028e66a8e8553e1d667d Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Thu, 20 Feb 2025 06:23:08 -0500 Subject: [PATCH 486/525] Fix #15099 Overwrite `$CARGO` when the current exe is detected to be a cargo binary. See: https://github.com/rust-lang/cargo/issues/15099#issuecomment-2666737150 --- src/cargo/util/context/mod.rs | 20 +++++++- tests/testsuite/cargo_command.rs | 5 +- tests/testsuite/freshness.rs | 83 ++++++++++++++++++++++++++++++++ tests/testsuite/test.rs | 22 ++++----- 4 files changed, 116 insertions(+), 14 deletions(-) diff --git a/src/cargo/util/context/mod.rs b/src/cargo/util/context/mod.rs index 0b388978d69..f55bc1bfab1 100644 --- a/src/cargo/util/context/mod.rs +++ b/src/cargo/util/context/mod.rs @@ -493,9 +493,25 @@ impl GlobalContext { paths::resolve_executable(&argv0) } + // Determines whether `path` is a cargo binary. + // See: https://github.com/rust-lang/cargo/issues/15099#issuecomment-2666737150 + fn is_cargo(path: &Path) -> bool { + path.file_stem() == Some(OsStr::new("cargo")) + } + + let from_current_exe = from_current_exe(); + if from_current_exe.as_deref().is_ok_and(is_cargo) { + return from_current_exe; + } + + let from_argv = from_argv(); + if from_argv.as_deref().is_ok_and(is_cargo) { + return from_argv; + } + let exe = from_env() - .or_else(|_| from_current_exe()) - .or_else(|_| from_argv()) + .or(from_current_exe) + .or(from_argv) .context("couldn't get the path to cargo executable")?; Ok(exe) }) diff --git a/tests/testsuite/cargo_command.rs b/tests/testsuite/cargo_command.rs index 785b7d9c123..5e8356447a8 100644 --- a/tests/testsuite/cargo_command.rs +++ b/tests/testsuite/cargo_command.rs @@ -391,10 +391,13 @@ fn cargo_subcommand_env() { .canonicalize() .unwrap(); let envtest_bin = envtest_bin.to_str().unwrap(); + // Previously, `$CARGO` would be left at `envtest_bin`. However, with the + // fix for #15099, `$CARGO` is now overwritten with the path to the current + // exe when it is detected to be a cargo binary. cargo_process("envtest") .env("PATH", &path) .env(cargo::CARGO_ENV, &envtest_bin) - .with_stdout_data(format!("{}\n", envtest_bin).raw().raw()) + .with_stdout_data(format!("{}\n", cargo.display()).raw()) .run(); } diff --git a/tests/testsuite/freshness.rs b/tests/testsuite/freshness.rs index 7ad417e194e..8429f991171 100644 --- a/tests/testsuite/freshness.rs +++ b/tests/testsuite/freshness.rs @@ -1,5 +1,6 @@ //! Tests for fingerprinting (rebuild detection). +use std::env::consts::EXE_SUFFIX; use std::fs::{self, OpenOptions}; use std::io; use std::io::prelude::*; @@ -3182,3 +3183,85 @@ fn use_mtime_cache_in_cargo_home() { "#]]) .run(); } + +#[cargo_test] +fn overwrite_cargo_environment_variable() { + // If passed arguments `arg1 arg2 ...`, this program runs them as a command. + // If passed no arguments, this program simply prints `$CARGO`. + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file( + "src/main.rs", + r#" + fn main() { + let mut args = std::env::args().skip(1); + if let Some(arg1) = args.next() { + let status = std::process::Command::new(arg1) + .args(args) + .status() + .unwrap(); + assert!(status.success()); + } else { + eprintln!("{}", std::env::var("CARGO").unwrap()); + } + } + "#, + ) + .build(); + + // Create two other cargo binaries in the project root, one with the wrong + // name and one with the right name. + let cargo_exe = cargo_test_support::cargo_exe(); + let wrong_name_path = p.root().join(format!("wrong_name{EXE_SUFFIX}")); + let other_cargo_path = p.root().join(cargo_exe.file_name().unwrap()); + std::fs::hard_link(&cargo_exe, &wrong_name_path).unwrap(); + std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap(); + + // The output of each of the following commands should be `path-to-cargo`: + // ``` + // cargo run + // cargo run -- cargo run + // cargo run -- wrong_name run + // ``` + + let cargo = cargo_exe.display().to_string(); + let wrong_name = wrong_name_path.display().to_string(); + let stderr_cargo = format!( + "{}[EXE]\n", + cargo_exe + .canonicalize() + .unwrap() + .with_extension("") + .to_str() + .unwrap() + ); + + for cmd in [ + "run", + &format!("run -- {cargo} run"), + &format!("run -- {wrong_name} run"), + ] { + p.cargo(cmd).with_stderr_contains(&stderr_cargo).run(); + } + + // The output of the following command should be `path-to-other-cargo`: + // ``` + // cargo run -- other_cargo run + // ``` + + let other_cargo = other_cargo_path.display().to_string(); + let stderr_other_cargo = format!( + "{}[EXE]\n", + other_cargo_path + .canonicalize() + .unwrap() + .with_extension("") + .to_str() + .unwrap() + .replace(p.root().parent().unwrap().to_str().unwrap(), "[ROOT]") + ); + + p.cargo(&format!("run -- {other_cargo} run")) + .with_stderr_contains(stderr_other_cargo) + .run(); +} diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index e0ec17e43ec..d2eb46307e1 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -3909,22 +3909,22 @@ test env_test ... ok .run(); // Check that `cargo test` propagates the environment's $CARGO - let rustc = cargo_util::paths::resolve_executable("rustc".as_ref()) - .unwrap() - .canonicalize() - .unwrap(); - let stderr_rustc = format!( + let cargo_exe = cargo_test_support::cargo_exe(); + let other_cargo_path = p.root().join(cargo_exe.file_name().unwrap()); + std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap(); + let stderr_other_cargo = format!( "{}[EXE]", - rustc + other_cargo_path + .canonicalize() + .unwrap() .with_extension("") .to_str() .unwrap() - .replace(rustc_host, "[HOST_TARGET]") + .replace(p.root().parent().unwrap().to_str().unwrap(), "[ROOT]") ); - p.cargo("test --lib -- --nocapture") - // we use rustc since $CARGO is only used if it points to a path that exists - .env(cargo::CARGO_ENV, rustc) - .with_stderr_contains(stderr_rustc) + p.process(other_cargo_path) + .args(&["test", "--lib", "--", "--nocapture"]) + .with_stderr_contains(stderr_other_cargo) .with_stdout_data(str![[r#" ... test env_test ... ok From 80331b1ea678498e6a36d865ae3d612fed0d4db4 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sat, 22 Feb 2025 10:05:57 -0500 Subject: [PATCH 487/525] chore: dont check cargo-util semver until 1.86 is released This fixes the current confusing failures in our CI pipeline: * https://github.com/rust-lang/cargo/actions/runs/13465687015/job/37630870984 * https://github.com/rust-lang/cargo/actions/runs/13469881475/job/37642079118 CI job failed because of this major SemVer breakage: ```diff -pub fn strip_prefix_canonical>( - path: P, - base: P, +pub fn strip_prefix_canonical( + path: impl AsRef, + base: impl AsRef, ) -> Result { ``` While cargo-util does have that change violating SemVer, it is unlikely people use it with turbo-fish syntax. And cargo-util is essentially for internal use. See: * https://rust-lang.zulipchat.com/#narrow/channel/246057-t-cargo/topic/check-version-bump.20failure * https://forge.rust-lang.org/policies/crate-ownership.html#internal-use --- crates/xtask-bump-check/src/xtask.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/xtask-bump-check/src/xtask.rs b/crates/xtask-bump-check/src/xtask.rs index 18b9baa3c6d..6bb112cf2cb 100644 --- a/crates/xtask-bump-check/src/xtask.rs +++ b/crates/xtask-bump-check/src/xtask.rs @@ -206,7 +206,18 @@ fn bump_check(args: &clap::ArgMatches, gctx: &cargo::util::GlobalContext) -> Car let mut cmd = ProcessBuilder::new("cargo"); cmd.arg("semver-checks") .arg("check-release") + // Don't check cargo-util util 1.86 is released. + // While it does have a SemVer breakage, + // it is unlikely people use it with turbo-fish syntax. + // And cargo-util is essentially for internal use. + // + // See: + // + // * https://rust-lang.zulipchat.com/#narrow/channel/246057-t-cargo/topic/check-version-bump.20failure + // * https://forge.rust-lang.org/policies/crate-ownership.html#internal-use + .args(&["--exclude", "cargo-util"]) .arg("--workspace"); + gctx.shell().status("Running", &cmd)?; cmd.exec()?; if github { From e820df0ca868701b12570d165c5d8729f472727a Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sat, 22 Feb 2025 10:36:48 -0500 Subject: [PATCH 488/525] chore: sever-check build-rs against beta channel --- crates/xtask-bump-check/src/xtask.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/xtask-bump-check/src/xtask.rs b/crates/xtask-bump-check/src/xtask.rs index 18b9baa3c6d..58e4e3770bf 100644 --- a/crates/xtask-bump-check/src/xtask.rs +++ b/crates/xtask-bump-check/src/xtask.rs @@ -184,7 +184,6 @@ fn bump_check(args: &clap::ArgMatches, gctx: &cargo::util::GlobalContext) -> Car let mut cmd = ProcessBuilder::new("cargo"); cmd.arg("semver-checks") .arg("--workspace") - .args(&["--exclude", "build-rs"]) // FIXME: Remove once 1.84 is stable. .arg("--baseline-rev") .arg(referenced_commit.id().to_string()); for krate in crates_not_check_against_channels { From e0ad2373a74976ed4f4bfc2e0329e06f7872471b Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sun, 23 Feb 2025 00:15:17 -0500 Subject: [PATCH 489/525] chore: depend on openssl-sys to correctly pin its version --- Cargo.lock | 9 +++++---- Cargo.toml | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fc1492e97b6..e4b64d61f55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -351,6 +351,7 @@ dependencies = [ "memchr", "opener", "openssl", + "openssl-sys", "os_info", "pasetors", "pathdiff", @@ -2824,18 +2825,18 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.4.1+3.4.0" +version = "111.28.2+1.1.1w" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa4eac4138c62414b5622d1b31c5c304f34b406b013c079c2bbc652fdd6678c" +checksum = "bb1830e20a48a975ca898ca8c1d036a36c3c6c5cb7dabc1c216706587857920f" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.105" +version = "0.9.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" +checksum = "db7e971c2c2bba161b2d2fdf37080177eff520b3bc044787c7f1f5f9e78d869b" dependencies = [ "cc", "libc", diff --git a/Cargo.toml b/Cargo.toml index e40b5fae6c1..f1389217e20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -234,6 +234,7 @@ cargo-credential-macos-keychain.workspace = true [target.'cfg(not(windows))'.dependencies] openssl = { workspace = true, optional = true } +openssl-sys = { workspace = true, optional = true } # HACK: for pinning to openssl v1. [target.'cfg(windows)'.dependencies] cargo-credential-wincred.workspace = true From 7f94b33b847b9c0e8ca39fa1ca0719317e3b3ae6 Mon Sep 17 00:00:00 2001 From: utnim2 Date: Sun, 23 Feb 2025 16:13:23 +0530 Subject: [PATCH 490/525] feat: add completions for `--manifest-path` --- src/cargo/util/command_prelude.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index e11a902a635..bc1d1a6ef60 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -10,6 +10,7 @@ use crate::util::important_paths::find_root_manifest_for_wd; use crate::util::interning::InternedString; use crate::util::is_rustup; use crate::util::restricted_names; +use crate::util::toml::is_embedded; use crate::util::{ print_available_benches, print_available_binaries, print_available_examples, print_available_packages, print_available_tests, @@ -325,7 +326,21 @@ pub trait CommandExt: Sized { self._arg( opt("manifest-path", "Path to Cargo.toml") .value_name("PATH") - .help_heading(heading::MANIFEST_OPTIONS), + .help_heading(heading::MANIFEST_OPTIONS) + .add(clap_complete::engine::ArgValueCompleter::new( + clap_complete::engine::PathCompleter::any().filter(|path: &Path| { + if path.file_name() == Some(OsStr::new("Cargo.toml")) { + return true; + } + if is_embedded(path) { + return true; + } + if path.is_file() && path.extension().is_none() { + return true; + } + false + }), + )), ) } From a6fdc01cea9774018eac3221fd9f1a8a5b0292b9 Mon Sep 17 00:00:00 2001 From: Samuel Moelius Date: Sun, 23 Feb 2025 17:37:39 -0500 Subject: [PATCH 491/525] Move test to cargo_command.rs --- tests/testsuite/cargo_command.rs | 84 ++++++++++++++++++++++++++++++++ tests/testsuite/freshness.rs | 83 ------------------------------- 2 files changed, 84 insertions(+), 83 deletions(-) diff --git a/tests/testsuite/cargo_command.rs b/tests/testsuite/cargo_command.rs index 5e8356447a8..54fda2cfc7d 100644 --- a/tests/testsuite/cargo_command.rs +++ b/tests/testsuite/cargo_command.rs @@ -573,3 +573,87 @@ fn full_did_you_mean() { "#]]) .run(); } + +#[cargo_test] +fn overwrite_cargo_environment_variable() { + // If passed arguments `arg1 arg2 ...`, this program runs them as a command. + // If passed no arguments, this program simply prints `$CARGO`. + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) + .file( + "src/main.rs", + r#" + fn main() { + let mut args = std::env::args().skip(1); + if let Some(arg1) = args.next() { + let status = std::process::Command::new(arg1) + .args(args) + .status() + .unwrap(); + assert!(status.success()); + } else { + eprintln!("{}", std::env::var("CARGO").unwrap()); + } + } + "#, + ) + .build(); + + // Create two other cargo binaries in the project root, one with the wrong + // name and one with the right name. + let cargo_exe = cargo_test_support::cargo_exe(); + let wrong_name_path = p + .root() + .join(format!("wrong_name{}", env::consts::EXE_SUFFIX)); + let other_cargo_path = p.root().join(cargo_exe.file_name().unwrap()); + std::fs::hard_link(&cargo_exe, &wrong_name_path).unwrap(); + std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap(); + + // The output of each of the following commands should be `path-to-cargo`: + // ``` + // cargo run + // cargo run -- cargo run + // cargo run -- wrong_name run + // ``` + + let cargo = cargo_exe.display().to_string(); + let wrong_name = wrong_name_path.display().to_string(); + let stderr_cargo = format!( + "{}[EXE]\n", + cargo_exe + .canonicalize() + .unwrap() + .with_extension("") + .to_str() + .unwrap() + ); + + for cmd in [ + "run", + &format!("run -- {cargo} run"), + &format!("run -- {wrong_name} run"), + ] { + p.cargo(cmd).with_stderr_contains(&stderr_cargo).run(); + } + + // The output of the following command should be `path-to-other-cargo`: + // ``` + // cargo run -- other_cargo run + // ``` + + let other_cargo = other_cargo_path.display().to_string(); + let stderr_other_cargo = format!( + "{}[EXE]\n", + other_cargo_path + .canonicalize() + .unwrap() + .with_extension("") + .to_str() + .unwrap() + .replace(p.root().parent().unwrap().to_str().unwrap(), "[ROOT]") + ); + + p.cargo(&format!("run -- {other_cargo} run")) + .with_stderr_contains(stderr_other_cargo) + .run(); +} diff --git a/tests/testsuite/freshness.rs b/tests/testsuite/freshness.rs index 8429f991171..7ad417e194e 100644 --- a/tests/testsuite/freshness.rs +++ b/tests/testsuite/freshness.rs @@ -1,6 +1,5 @@ //! Tests for fingerprinting (rebuild detection). -use std::env::consts::EXE_SUFFIX; use std::fs::{self, OpenOptions}; use std::io; use std::io::prelude::*; @@ -3183,85 +3182,3 @@ fn use_mtime_cache_in_cargo_home() { "#]]) .run(); } - -#[cargo_test] -fn overwrite_cargo_environment_variable() { - // If passed arguments `arg1 arg2 ...`, this program runs them as a command. - // If passed no arguments, this program simply prints `$CARGO`. - let p = project() - .file("Cargo.toml", &basic_manifest("foo", "1.0.0")) - .file( - "src/main.rs", - r#" - fn main() { - let mut args = std::env::args().skip(1); - if let Some(arg1) = args.next() { - let status = std::process::Command::new(arg1) - .args(args) - .status() - .unwrap(); - assert!(status.success()); - } else { - eprintln!("{}", std::env::var("CARGO").unwrap()); - } - } - "#, - ) - .build(); - - // Create two other cargo binaries in the project root, one with the wrong - // name and one with the right name. - let cargo_exe = cargo_test_support::cargo_exe(); - let wrong_name_path = p.root().join(format!("wrong_name{EXE_SUFFIX}")); - let other_cargo_path = p.root().join(cargo_exe.file_name().unwrap()); - std::fs::hard_link(&cargo_exe, &wrong_name_path).unwrap(); - std::fs::hard_link(&cargo_exe, &other_cargo_path).unwrap(); - - // The output of each of the following commands should be `path-to-cargo`: - // ``` - // cargo run - // cargo run -- cargo run - // cargo run -- wrong_name run - // ``` - - let cargo = cargo_exe.display().to_string(); - let wrong_name = wrong_name_path.display().to_string(); - let stderr_cargo = format!( - "{}[EXE]\n", - cargo_exe - .canonicalize() - .unwrap() - .with_extension("") - .to_str() - .unwrap() - ); - - for cmd in [ - "run", - &format!("run -- {cargo} run"), - &format!("run -- {wrong_name} run"), - ] { - p.cargo(cmd).with_stderr_contains(&stderr_cargo).run(); - } - - // The output of the following command should be `path-to-other-cargo`: - // ``` - // cargo run -- other_cargo run - // ``` - - let other_cargo = other_cargo_path.display().to_string(); - let stderr_other_cargo = format!( - "{}[EXE]\n", - other_cargo_path - .canonicalize() - .unwrap() - .with_extension("") - .to_str() - .unwrap() - .replace(p.root().parent().unwrap().to_str().unwrap(), "[ROOT]") - ); - - p.cargo(&format!("run -- {other_cargo} run")) - .with_stderr_contains(stderr_other_cargo) - .run(); -} From 4ce639da9f62594cf88409d0939ba72c04d19d87 Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Sun, 2 Feb 2025 15:46:45 +0900 Subject: [PATCH 492/525] Added target-dir tests These ares are in preparation to split target-dir into artifact-dir and build-dir --- tests/testsuite/build_dir.rs | 575 +++++++++++++++++++++++++++++++++++ tests/testsuite/main.rs | 1 + 2 files changed, 576 insertions(+) create mode 100644 tests/testsuite/build_dir.rs diff --git a/tests/testsuite/build_dir.rs b/tests/testsuite/build_dir.rs new file mode 100644 index 00000000000..602c8080204 --- /dev/null +++ b/tests/testsuite/build_dir.rs @@ -0,0 +1,575 @@ +//! Tests for `build.build-dir` config property. +//! +//! The testing strategy for build-dir functionality is primarily checking if directories / files +//! are in the expected locations. +//! The rational is that other tests will verify each individual feature, while the tests in this +//! file verify the files saved to disk are in the correct locations according to the `build-dir` +//! configuration. +//! +//! Tests check if directories match some "layout" by using [`assert_build_dir_layout`] and +//! [`assert_artifact_dir_layout`]. + +use std::path::PathBuf; + +use cargo_test_support::prelude::*; +use cargo_test_support::project; +use std::env::consts::{DLL_PREFIX, DLL_SUFFIX, EXE_SUFFIX}; + +#[cargo_test] +fn verify_build_dir_is_disabled_by_feature_flag() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file( + ".cargo/config.toml", + r#" + [build] + build-dir = "build-dir" + "#, + ) + .build(); + + p.cargo("build") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_build_dir_layout(p.root().join("target"), "debug"); + assert_exists(&p.root().join(format!("target/debug/foo{EXE_SUFFIX}"))); + assert_exists(&p.root().join("target/debug/foo.d")); + assert_not_exists(&p.root().join("build-dir")); +} + +#[cargo_test] +fn binary_with_debug() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file( + ".cargo/config.toml", + r#" + [build] + target-dir = "target-dir" + "#, + ) + .build(); + + p.cargo("build") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_build_dir_layout(p.root().join("target-dir"), "debug"); + assert_exists_patterns_with_base_dir( + &p.root(), + &[ + &format!("target-dir/debug/deps/foo*{EXE_SUFFIX}"), + "target-dir/debug/deps/foo*.d", + ], + ); + assert_not_exists(&p.root().join("target")); +} + +#[cargo_test] +fn binary_with_release() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file( + ".cargo/config.toml", + r#" + [build] + target-dir = "target-dir" + "#, + ) + .build(); + + p.cargo("build --release") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_build_dir_layout(p.root().join("target-dir"), "release"); + assert_exists(&p.root().join(format!("target-dir/release/foo{EXE_SUFFIX}"))); + assert_exists_patterns_with_base_dir( + &p.root(), + &[ + &format!("target-dir/release/deps/foo*{EXE_SUFFIX}"), + "target-dir/release/deps/foo*.d", + ], + ); + assert_not_exists(&p.root().join("target")); +} + +#[cargo_test] +fn libs() { + // https://doc.rust-lang.org/reference/linkage.html#r-link.staticlib + let (staticlib_prefix, staticlib_suffix) = + if cfg!(target_os = "windows") && cfg!(target_env = "msvc") { + ("", ".lib") + } else { + ("lib", ".a") + }; + + // (crate-type, list of final artifacts) + let lib_types = [ + ("lib", ["libfoo.rlib", "libfoo.d"]), + ( + "dylib", + [ + &format!("{DLL_PREFIX}foo{DLL_SUFFIX}"), + &format!("{DLL_PREFIX}foo.d"), + ], + ), + ( + "cdylib", + [ + &format!("{DLL_PREFIX}foo{DLL_SUFFIX}"), + &format!("{DLL_PREFIX}foo.d"), + ], + ), + ( + "staticlib", + [ + &format!("{staticlib_prefix}foo{staticlib_suffix}"), + &format!("{staticlib_prefix}foo.d"), + ], + ), + ]; + + for (lib_type, expected_files) in lib_types { + let p = project() + .file("src/lib.rs", r#"fn foo() { println!("Hello, World!") }"#) + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + edition = "2015" + + [lib] + crate-type = ["{lib_type}"] + "# + ), + ) + .file( + ".cargo/config.toml", + r#" + [build] + target-dir = "target-dir" + "#, + ) + .build(); + + p.cargo("build") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_build_dir_layout(p.root().join("target-dir"), "debug"); + assert_exists_patterns_with_base_dir(&p.root().join("target-dir/debug"), &expected_files); + assert_not_exists(&p.root().join("target")); + } +} + +#[cargo_test] +fn should_default_to_target() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .build(); + + p.cargo("build") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_build_dir_layout(p.root().join("target"), "debug"); + assert_exists(&p.root().join(format!("target/debug/foo{EXE_SUFFIX}"))); +} + +#[cargo_test] +fn should_respect_env_var() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .build(); + + p.cargo("build") + .env("CARGO_TARGET_DIR", "target-dir") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_build_dir_layout(p.root().join("target-dir"), "debug"); + assert_exists(&p.root().join(format!("target-dir/debug/foo{EXE_SUFFIX}"))); + assert_not_exists(&p.root().join("target")); +} + +#[cargo_test] +fn build_script_should_output_to_target_dir() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file( + "build.rs", + r#" + fn main() { + std::fs::write( + format!("{}/foo.txt", std::env::var("OUT_DIR").unwrap()), + "Hello, world!", + ) + .unwrap(); + } + "#, + ) + .file( + ".cargo/config.toml", + r#" + [build] + target-dir = "target-dir" + "#, + ) + .build(); + + p.cargo("build") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_build_dir_layout(p.root().join("target-dir"), "debug"); + assert_exists_patterns_with_base_dir( + &p.root(), + &[ + &format!("target-dir/debug/build/foo-*/build-script-build{EXE_SUFFIX}"), + "target-dir/debug/build/foo-*/out/foo.txt", // Verify OUT_DIR + ], + ); +} + +#[cargo_test] +fn cargo_tmpdir_should_output_to_target_dir() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file( + "tests/foo.rs", + r#" + #[test] + fn test() { + std::fs::write( + format!("{}/foo.txt", env!("CARGO_TARGET_TMPDIR")), + "Hello, world!", + ) + .unwrap(); + } + "#, + ) + .file( + ".cargo/config.toml", + r#" + [build] + target-dir = "target-dir" + "#, + ) + .build(); + + p.cargo("test") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_build_dir_layout(p.root().join("target-dir"), "debug"); + assert_exists(&p.root().join(format!("target-dir/tmp/foo.txt"))); +} + +#[cargo_test] +fn examples_should_output_to_target_dir() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file("examples/foo.rs", r#"fn main() { }"#) + .file( + ".cargo/config.toml", + r#" + [build] + target-dir = "target-dir" + "#, + ) + .build(); + + p.cargo("build --examples") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_build_dir_layout(p.root().join("target-dir"), "debug"); + assert_exists_patterns_with_base_dir( + &p.root(), + &[ + &format!("target-dir/debug/examples/foo{EXE_SUFFIX}"), + "target-dir/debug/examples/foo.d", + &format!("target-dir/debug/examples/foo*{EXE_SUFFIX}"), + "target-dir/debug/examples/foo*.d", + ], + ); +} + +#[cargo_test] +fn benches_should_output_to_target_dir() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file("benches/foo.rs", r#"fn main() { }"#) + .file( + ".cargo/config.toml", + r#" + [build] + target-dir = "target-dir" + "#, + ) + .build(); + + p.cargo("build --bench=foo") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_build_dir_layout(p.root().join("target-dir"), "debug"); + assert_exists_patterns_with_base_dir( + &p.root(), + &[ + &format!("target-dir/debug/deps/foo*{EXE_SUFFIX}"), + "target-dir/debug/deps/foo*.d", + ], + ); +} + +#[cargo_test] +fn cargo_doc_should_output_to_target_dir() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file( + ".cargo/config.toml", + r#" + [build] + target-dir = "target-dir" + "#, + ) + .build(); + + p.cargo("doc") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + let docs_dir = p.root().join("target-dir/doc"); + + assert_exists(&docs_dir); + assert_exists(&docs_dir.join("foo/index.html")); +} + +#[cargo_test] +fn cargo_package_should_output_to_target_dir() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file( + ".cargo/config.toml", + r#" + [build] + target-dir = "target-dir" + "#, + ) + .build(); + + p.cargo("package") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_build_dir_layout(p.root().join("target-dir"), "debug"); + + let package_dir = p.root().join("target-dir/package"); + assert_exists(&package_dir); + assert_exists(&package_dir.join("foo-0.0.1.crate")); + assert!(package_dir.join("foo-0.0.1.crate").is_file()); + assert_exists(&package_dir.join("foo-0.0.1")); + assert!(package_dir.join("foo-0.0.1").is_dir()); +} + +#[cargo_test] +fn cargo_clean_should_clean_the_target_dir() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file( + ".cargo/config.toml", + r#" + [build] + target-dir = "target-dir" + "#, + ) + .build(); + + p.cargo("build") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_build_dir_layout(p.root().join("target-dir"), "debug"); + + p.cargo("clean") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert!(!p.root().join("target-dir").exists()); +} + +#[cargo_test] +fn timings_report_should_output_to_target_dir() { + let p = project() + .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#) + .file( + ".cargo/config.toml", + r#" + [build] + target-dir = "target-dir" + "#, + ) + .build(); + + p.cargo("build --timings") + .masquerade_as_nightly_cargo(&[]) + .enable_mac_dsym() + .run(); + + assert_exists(&p.root().join("target-dir/cargo-timings/cargo-timing.html")); +} + +#[cargo_test( + nightly, + reason = "-Zfuture-incompat-test requires nightly (permanently)" +)] +fn future_incompat_should_output_to_target_dir() { + let p = project() + .file("src/main.rs", r#"fn main() { let x = 1; }"#) + .file( + ".cargo/config.toml", + r#" + [build] + target-dir = "target-dir" + "#, + ) + .build(); + + p.cargo("check") + .arg("--future-incompat-report") + .env("RUSTFLAGS", "-Zfuture-incompat-test") + .run(); + + assert_exists(&p.root().join("target-dir/.future-incompat-report.json")); +} + +#[track_caller] +fn assert_build_dir_layout(path: PathBuf, profile: &str) { + assert_dir_layout(path, profile, true); +} + +#[allow(dead_code)] +#[track_caller] +fn assert_artifact_dir_layout(path: PathBuf, profile: &str) { + assert_dir_layout(path, profile, false); +} + +#[track_caller] +fn assert_dir_layout(path: PathBuf, profile: &str, is_build_dir: bool) { + println!("checking if {path:?} is a build directory ({is_build_dir})"); + // For things that are in both `target` and the build directory we only check if they are + // present if `is_build_dir` is true. + if is_build_dir { + assert_eq!( + is_build_dir, + path.join(profile).is_dir(), + "Expected {:?} to exist and be a directory", + path.join(profile) + ); + } + + let error_message = |dir: &str| { + if is_build_dir { + format!("`{dir}` dir was expected but not found") + } else { + format!("`{dir}` dir was not expected but was found") + } + }; + + if is_build_dir { + assert_exists(&path.join(".rustc_info.json")); + } else { + assert_not_exists(&path.join(".rustc_info.json")); + } + + assert_eq!( + is_build_dir, + path.join(profile).join("deps").is_dir(), + "{}", + error_message("deps") + ); + assert_eq!( + is_build_dir, + path.join(profile).join("build").is_dir(), + "{}", + error_message("build") + ); + assert_eq!( + is_build_dir, + path.join(profile).join("incremental").is_dir(), + "{}", + error_message("incremental") + ); + assert_eq!( + is_build_dir, + path.join(profile).join(".fingerprint").is_dir(), + "{}", + error_message(".fingerprint") + ); +} + +#[track_caller] +fn assert_exists(path: &PathBuf) { + assert!( + path.exists(), + "Expected `{}` to exist but was not found.", + path.display() + ); +} + +#[track_caller] +fn assert_not_exists(path: &PathBuf) { + assert!( + !path.exists(), + "Expected `{}` to NOT exist but was found.", + path.display() + ); +} + +#[track_caller] +fn assert_exists_patterns_with_base_dir(base: &PathBuf, patterns: &[&str]) { + let root = base.to_str().unwrap(); + let p: Vec<_> = patterns.iter().map(|p| format!("{root}/{p}")).collect(); + let p: Vec<&str> = p.iter().map(|v| v.as_str()).collect(); + assert_exists_patterns(&p); +} + +#[track_caller] +fn assert_exists_patterns(patterns: &[&str]) { + for p in patterns { + assert_exists_pattern(p); + } +} + +#[track_caller] +fn assert_exists_pattern(pattern: &str) { + use glob::glob; + + let mut z = glob(pattern).unwrap(); + + assert!( + z.next().is_some(), + "Expected `{pattern}` to match existing file but was not found.", + ) +} diff --git a/tests/testsuite/main.rs b/tests/testsuite/main.rs index 385b3690b63..a54209639cb 100644 --- a/tests/testsuite/main.rs +++ b/tests/testsuite/main.rs @@ -11,6 +11,7 @@ mod bad_manifest_path; mod bench; mod binary_name; mod build; +mod build_dir; mod build_plan; mod build_script; mod build_script_env; From 1b82e1b0bf351bcff84c2904c90d676fd6ed1fb4 Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Sat, 15 Feb 2025 13:46:21 +0900 Subject: [PATCH 493/525] Added build-dir unstable feature flag --- src/cargo/core/features.rs | 2 + tests/testsuite/cargo/z_help/stdout.term.svg | 76 ++++++++++---------- 2 files changed, 41 insertions(+), 37 deletions(-) diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 64d37016b2a..7cf4225e5ac 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -759,6 +759,7 @@ unstable_cli_options!( avoid_dev_deps: bool = ("Avoid installing dev-dependencies if possible"), binary_dep_depinfo: bool = ("Track changes to dependency artifacts"), bindeps: bool = ("Allow Cargo packages to depend on bin, cdylib, and staticlib crates, and use the artifacts built by those crates"), + build_dir: bool = ("Enable the `build.build-dir` option in .cargo/config.toml file"), #[serde(deserialize_with = "deserialize_comma_separated_list")] build_std: Option> = ("Enable Cargo to compile the standard library itself as part of a crate graph compilation"), #[serde(deserialize_with = "deserialize_comma_separated_list")] @@ -1264,6 +1265,7 @@ impl CliUnstable { "avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?, "binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?, "bindeps" => self.bindeps = parse_empty(k, v)?, + "build-dir" => self.build_dir = parse_empty(k, v)?, "build-std" => self.build_std = Some(parse_list(v)), "build-std-features" => self.build_std_features = Some(parse_list(v)), "cargo-lints" => self.cargo_lints = parse_empty(k, v)?, diff --git a/tests/testsuite/cargo/z_help/stdout.term.svg b/tests/testsuite/cargo/z_help/stdout.term.svg index 45700298477..e0a828959fd 100644 --- a/tests/testsuite/cargo/z_help/stdout.term.svg +++ b/tests/testsuite/cargo/z_help/stdout.term.svg @@ -1,4 +1,4 @@ - +