From 5448ed7d68b9d2df1958b96549ffd7fd6ba46999 Mon Sep 17 00:00:00 2001 From: CAD97 Date: Sun, 13 Mar 2022 22:35:32 -0500 Subject: [PATCH 01/21] 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 02/21] 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 03/21] 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 04/21] 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 05/21] 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 401f77a2979f80b0df2b6b475237d5729dbbbc8b Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 6 Nov 2024 15:31:35 -0600 Subject: [PATCH 06/21] 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 07/21] 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 08/21] 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 09/21] 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 10/21] 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 11/21] 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 12/21] 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 13/21] 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 14/21] 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 15/21] 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 16/21] 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 17/21] 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 18/21] 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 19/21] 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 20/21] 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 21/21] 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)