diff --git a/Cargo.lock b/Cargo.lock index 0834cb961d0..7d2abf48a8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -252,6 +252,20 @@ dependencies = [ "serde", ] +[[package]] +name = "build-rs" +version = "0.2.0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "build-rs-test-lib" +version = "0.0.0" +dependencies = [ + "build-rs", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -3760,9 +3774,9 @@ checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" diff --git a/Cargo.toml b/Cargo.toml index ee8eecdfeb5..c2cbff88b5d 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 new file mode 100644 index 00000000000..f93b4e69428 --- /dev/null +++ b/crates/build-rs-test-lib/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "build-rs-test-lib" +version = "0.0.0" +edition.workspace = true +publish = false + +[features] +unstable = ["build-rs/unstable"] + +[build-dependencies] +build-rs.workspace = true diff --git a/crates/build-rs-test-lib/build.rs b/crates/build-rs-test-lib/build.rs new file mode 100644 index 00000000000..a6065ef0385 --- /dev/null +++ b/crates/build-rs-test-lib/build.rs @@ -0,0 +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()); +} diff --git a/crates/build-rs-test-lib/src/lib.rs b/crates/build-rs-test-lib/src/lib.rs new file mode 100644 index 00000000000..871cc4ee586 --- /dev/null +++ b/crates/build-rs-test-lib/src/lib.rs @@ -0,0 +1,4 @@ +#[test] +fn test() { + assert!(cfg!(did_run_build_script)); +} diff --git a/crates/build-rs/Cargo.toml b/crates/build-rs/Cargo.toml new file mode 100644 index 00000000000..e2e8eb9fccf --- /dev/null +++ b/crates/build-rs/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "build-rs" +version = "0.2.0" +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 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/allow_use.rs b/crates/build-rs/src/allow_use.rs new file mode 100644 index 00000000000..15e9787aa4b --- /dev/null +++ b/crates/build-rs/src/allow_use.rs @@ -0,0 +1,46 @@ +use std::{process::Command, sync::OnceLock}; + +fn rust_version_minor() -> u32 { + static VERSION_MINOR: OnceLock = OnceLock::new(); + *VERSION_MINOR.get_or_init(|| { + crate::input::cargo_pkg_rust_version() + .split('.') + .nth(1) + // 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/crates/build-rs/src/ident.rs b/crates/build-rs/src/ident.rs new file mode 100644 index 00000000000..c84e1fee3f8 --- /dev/null +++ b/crates/build-rs/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/crates/build-rs/src/input.rs b/crates/build-rs/src/input.rs new file mode 100644 index 00000000000..1b7adc36cd4 --- /dev/null +++ b/crates/build-rs/src/input.rs @@ -0,0 +1,591 @@ +//! 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 crate::ident::{is_ascii_ident, is_crate_name, is_feature_name}; +use std::{ + env, + fmt::Display, + path::PathBuf, + str::{self, FromStr}, +}; + +macro_rules! missing { + ($key:expr) => { + panic!("cargo environment variable `{}` is missing", $key) + }; +} + +macro_rules! invalid { + ($key:expr, $err:expr) => { + panic!("cargo environment variable `{}` is invalid: {}", $key, $err) + }; +} + +#[track_caller] +fn 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)) +} + +#[track_caller] +fn get_path(key: &str) -> PathBuf { + get_opt_path(key).unwrap_or_else(|| missing!(key)) +} + +#[track_caller] +fn get_opt_str(key: &str) -> Option { + let var = env::var_os(key)?; + match str::from_utf8(var.as_encoded_bytes()) { + Ok(s) => Some(s.to_owned()), + Err(err) => invalid!(key, err), + } +} + +#[track_caller] +fn get_str(key: &str) -> String { + get_opt_str(key).unwrap_or_else(|| missing!(key)) +} + +#[track_caller] +fn get_num(key: &str) -> T +where + T::Err: Display, +{ + let val = get_str(key); + match val.parse() { + Ok(num) => num, + Err(err) => invalid!(key, err), + } +} + +#[track_caller] +fn get_opt_cfg(cfg: &str) -> (String, Option>) { + if !is_ascii_ident(cfg) { + panic!("invalid configuration option {cfg:?}") + } + let cfg = cfg.to_uppercase().replace('-', "_"); + let key = format!("CARGO_CFG_{cfg}"); + let Some(var) = env::var_os(&key) else { + return (key, None); + }; + let val = str::from_utf8(var.as_encoded_bytes()).unwrap_or_else(|err| invalid!(key, err)); + (key, Some(val.split(',').map(str::to_owned).collect())) +} + +#[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.82.0 reference + +/// Path to the `cargo` binary performing the build. +#[track_caller] +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. +#[track_caller] +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 +#[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) +} + +/// 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/stable/reference/conditional-compilation.html +#[track_caller] +pub fn cargo_cfg(cfg: &str) -> Option> { + let (_, val) = get_opt_cfg(cfg); + val +} + +pub use self::cfg::*; +mod cfg { + use super::*; + + // those disabled with #[cfg(any())] don't seem meaningfully useful + // but we list all cfg that are default known to check-cfg + + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_clippy() -> bool { + get_bool("CARGO_CFG_CLIPPY") + } + + /// If we are compiling with debug assertions enabled. + #[track_caller] + pub fn cargo_cfg_debug_assertions() -> bool { + get_bool("CARGO_CFG_DEBUG_ASSERTIONS") + } + + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_doc() -> bool { + get_bool("CARGO_CFG_DOC") + } + + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_docsrs() -> bool { + get_bool("CARGO_CFG_DOCSRS") + } + + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_doctest() -> bool { + get_bool("CARGO_CFG_DOCTEST") + } + + /// 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") + } + + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_miri() -> bool { + get_bool("CARGO_CFG_MIRI") + } + + /// 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") + } + + /// 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") + } + + /// 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") + } + + /// 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") + } + + #[cfg(any())] + #[track_caller] + pub fn cargo_cfg_rustfmt() -> bool { + get_bool("CARGO_CFG_RUSTFMT") + } + + /// 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 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 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") + } + + /// 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 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") +} + +/// 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 +#[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") +} + +/// 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 +#[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") +} + +/// `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 +#[track_caller] +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 +#[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}"); + get_opt_str(&key) +} + +/// 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") +} + +/// 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") +} + +/// 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 +#[track_caller] +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 +#[track_caller] +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/stable/cargo/reference/config.html#buildrustflags +#[track_caller] +pub fn cargo_encoded_rustflags() -> Vec { + get_str("CARGO_ENCODED_RUSTFLAGS") + .split('\x1f') + .map(str::to_owned) + .collect() +} + +/// 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(':') + .map(str::to_owned) + .collect() +} + +/// 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/crates/build-rs/src/lib.rs b/crates/build-rs/src/lib.rs new file mode 100644 index 00000000000..56a0df3e820 --- /dev/null +++ b/crates/build-rs/src/lib.rs @@ -0,0 +1,39 @@ +//! 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 + +#[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#"
"# + ) + }; +} + +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/crates/build-rs/src/output.rs b/crates/build-rs/src/output.rs new file mode 100644 index 00000000000..318185a040e --- /dev/null +++ b/crates/build-rs/src/output.rs @@ -0,0 +1,408 @@ +//! 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: + +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() { + 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. +#[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"); + }; + 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 +#[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"); + }; + 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 +#[track_caller] +pub fn rustc_link_arg(flag: &str) { + if flag.contains([' ', '\n']) { + panic!("cannot emit rustc-link-arg: invalid flag {flag:?}"); + } + emit("rustc-link-arg", flag); +} + +/// The `rustc-link-arg-bin` instruction tells Cargo to pass the +/// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building +/// the binary target with name `BIN`. Its usage is highly platform specific. It +/// 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 !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)); +} + +/// The `rustc-link-arg-bins` instruction tells Cargo to pass the +/// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building +/// the binary target. Its usage is highly platform specific. It is useful to set +/// 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 {flag:?}"); + } + emit("rustc-link-arg-bins", flag); +} + +/// 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 {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. +#[track_caller] +pub fn rustc_link_arg_examples(flag: &str) { + if flag.contains([' ', '\n']) { + panic!("cannot emit rustc-link-arg-examples: invalid flag {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. +#[track_caller] +pub fn rustc_link_arg_benches(flag: &str) { + if flag.contains([' ', '\n']) { + panic!("cannot emit rustc-link-arg-benches: invalid flag {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 `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 `-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][-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 +#[track_caller] +pub fn rustc_link_lib(lib: &str) { + 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[: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 {kind:?}"); + } + if lib.contains([' ', '\n']) { + panic!("cannot emit rustc-link-lib: invalid lib {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 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][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]: 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"); + }; + if path.contains('\n') { + panic!("cannot emit rustc-link-search: path contains newline"); + } + emit("rustc-link-search", path); +} + +/// 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 {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`]. +#[track_caller] +pub fn rustc_flags(flags: &str) { + 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][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 +/// 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 +/// `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 +#[track_caller] +pub fn rustc_cfg(key: &str) { + 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. 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) { + 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 +/// [`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. +/// +/// 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 {key:?}"); + } + if value.contains('\n') { + panic!("cannot emit rustc-env: invalid value {value:?}"); + } + emit("rustc-env", format_args!("{key}={value}")); +} + +/// The `rustc-cdylib-link-arg` instruction tells Cargo to pass the +/// [`-C link-arg=FLAG` option][link-arg] to the compiler, but only when building +/// a `cdylib` library target. Its usage is highly platform specific. It is useful +/// 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 {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” +/// 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"); + } + emit("warning", message); +} + +/// Metadata, used by `links` scripts. +#[track_caller] +pub fn metadata(key: &str, val: &str) { + if !is_ascii_ident(key) { + panic!("cannot emit metadata: invalid key {key:?}"); + } + if val.contains('\n') { + panic!("cannot emit metadata: invalid value {val:?}"); + } + + if allow_use::double_colon_directives() { + emit("metadata", format_args!("{}={}", key, val)); + } else { + emit(key, val); + } +} 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 { 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)