From 4b7f1dfee3958d0bae58d62f9a6fdf1ff496d5a1 Mon Sep 17 00:00:00 2001 From: David Koloski Date: Thu, 22 Feb 2024 12:59:04 -0500 Subject: [PATCH] Convert shell scripts to Rust (#891) `cargo.sh` is now a thin wrapper around a new `cargo-zerocopy` tool located in the `tools` directory. README generation is now done by a similar `generate-readme` tool. A wrapper script for running `cargo-zerocopy` on Windows has been added as `win-cargo.bat`, effectively allowing the test suites to be run on Windows. --- README.md | 4 +- cargo.sh | 133 +--------------- ci/check_readme.sh | 2 +- generate-readme.sh | 50 ------ src/lib.rs | 12 +- src/macro_util.rs | 14 +- src/util.rs | 4 +- tools/Cargo.toml | 25 +++ tools/cargo-zerocopy/Cargo.toml | 18 +++ tools/cargo-zerocopy/src/main.rs | 253 ++++++++++++++++++++++++++++++ tools/generate-readme/Cargo.toml | 18 +++ tools/generate-readme/src/main.rs | 51 ++++++ win-cargo.bat | 16 ++ 13 files changed, 404 insertions(+), 196 deletions(-) delete mode 100755 generate-readme.sh create mode 100644 tools/Cargo.toml create mode 100644 tools/cargo-zerocopy/Cargo.toml create mode 100644 tools/cargo-zerocopy/src/main.rs create mode 100644 tools/generate-readme/Cargo.toml create mode 100644 tools/generate-readme/src/main.rs create mode 100644 win-cargo.bat diff --git a/README.md b/README.md index c9e8e41a50..117e6d42c3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - # zerocopy diff --git a/cargo.sh b/cargo.sh index 424fd94bbc..9a083b9157 100755 --- a/cargo.sh +++ b/cargo.sh @@ -1,6 +1,4 @@ -#!/bin/bash -# -# Copyright 2023 The Fuchsia Authors +# Copyright 2024 The Fuchsia Authors # # Licensed under a BSD-style license , Apache License, Version 2.0 # , or the MIT @@ -8,128 +6,7 @@ # This file may not be copied, modified, or distributed except according to # those terms. -# This script is a thin wrapper around Cargo that provides human-friendly -# toolchain names which are automatically translated to the toolchain versions -# we have pinned in CI. -# -# cargo.sh --version # looks up the version for the named toolchain -# cargo.sh + [...] # runs cargo commands with the named toolchain -# cargo.sh +all [...] # runs cargo commands with each toolchain -# -# The meta-toolchain "all" instructs this script to run the provided command -# once for each "major" toolchain (msrv, stable, nightly). This does not include -# any toolchain which is listed in the `package.metadata.build-rs` Cargo.toml -# section. -# -# A common task that is especially annoying to perform by hand is to update -# trybuild's stderr files. Using this script: -# -# TRYBUILD=overwrite ./cargo.sh +all test --workspace - -set -eo pipefail - -function print-usage-and-exit { - echo "Usage:" >&2 - echo " $0 --version " >&2 - echo " $0 + [...]" >&2 - echo " $0 +all [...]" >&2 - exit 1 -} - -[[ $# -gt 0 ]] || print-usage-and-exit - -function pkg-meta { - # NOTE(#547): We set `CARGO_TARGET_DIR` here because `cargo metadata` - # sometimes causes the `cargo-metadata` crate to be rebuilt from source using - # the default toolchain. This has the effect of clobbering any existing build - # artifacts from whatever toolchain the user has specified (e.g., `+nightly`), - # causing the subsequent `cargo` invocation to rebuild unnecessarily. By - # specifying a separate build directory here, we ensure that this never - # clobbers the build artifacts used by the later `cargo` invocation. - # - # In CI, make sure to use the default stable toolchain. If we're testing on - # our MSRV, then we also have our MSRV toolchain installed. As of this - # writing, our MSRV is low enough that the correspoding Rust toolchain's Cargo - # doesn't know about the `rust-version` field, and so if we were to use Cargo - # with that toolchain, `pkg-meta` would return `null` when asked to retrieve - # the `rust-version` field. This also requires `RUSTFLAGS=''` to override any - # unstable `RUSTFLAGS` set by the caller. - RUSTFLAGS='' CARGO_TARGET_DIR=target/cargo-sh cargo +stable metadata --format-version 1 | jq -r ".packages[] | select(.name == \"zerocopy\").$1" -} - -function lookup-version { - VERSION="$1" - case "$VERSION" in - msrv) - pkg-meta rust_version - ;; - stable) - pkg-meta 'metadata.ci."pinned-stable"' - ;; - nightly) - pkg-meta 'metadata.ci."pinned-nightly"' - ;; - *) - TOOLCHAIN=$(pkg-meta "metadata.\"build-rs\".\"${VERSION}\"") - if [ "$TOOLCHAIN" != "null" ]; then - echo "$TOOLCHAIN" - else - echo "Unrecognized toolchain name: '$VERSION' (options are 'msrv', 'stable', 'nightly', and any value in Cargo.toml's 'metadata.build-rs' table)" >&2 - return 1 - fi - ;; - esac -} - -function get-rustflags { - [ "$1" == nightly ] && echo "--cfg __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS" -} - -function prompt { - PROMPT="$1" - YES="$2" - while true; do - read -p "$PROMPT " yn - case "$yn" in - [Yy]) $YES; return $?; ;; - [Nn]) return 1; ;; - *) break; ;; - esac - done -} - -case "$1" in - # cargo.sh --version - --version) - [[ $# -eq 2 ]] || print-usage-and-exit - lookup-version "$2" - ;; - # cargo.sh +all [...] - +all) - echo "[cargo.sh] warning: running the same command for each toolchain (msrv, stable, nightly)" >&2 - for toolchain in msrv stable nightly; do - echo "[cargo.sh] running with toolchain: $toolchain" >&2 - $0 "+$toolchain" ${@:2} - done - exit 0 - ;; - # cargo.sh + [...] - +*) - TOOLCHAIN="$(lookup-version ${1:1})" - - cargo "+$TOOLCHAIN" version &>/dev/null && \ - rustup "+$TOOLCHAIN" component list | grep '^rust-src (installed)$' >/dev/null || { - echo "[cargo.sh] missing either toolchain '$TOOLCHAIN' or component 'rust-src'" >&2 - # If we're running in a GitHub action, then it's better to bail than to - # hang waiting for input we're never going to get. - [ -z ${GITHUB_RUN_ID+x} ] || exit 1 - prompt "[cargo.sh] would you like to install toolchain '$TOOLCHAIN' and component 'rust-src' via 'rustup'?" \ - "rustup toolchain install $TOOLCHAIN -c rust-src" - } || exit 1 - - RUSTFLAGS="$(get-rustflags ${1:1}) $RUSTFLAGS" cargo "+$TOOLCHAIN" ${@:2} - ;; - *) - print-usage-and-exit - ;; -esac +# Build `cargo-zerocopy` without any RUSTFLAGS set in the environment +env -u RUSTFLAGS cargo +stable build --manifest-path tools/Cargo.toml -p cargo-zerocopy -q +# Thin wrapper around the `cargo-zerocopy` binary in `tools/cargo-zerocopy` +./tools/target/debug/cargo-zerocopy $@ diff --git a/ci/check_readme.sh b/ci/check_readme.sh index f6c3a1fc66..8bb614acca 100755 --- a/ci/check_readme.sh +++ b/ci/check_readme.sh @@ -6,5 +6,5 @@ set -eo pipefail # suppress all errors from it. cargo install cargo-readme --version 3.2.0 -q -diff <(./generate-readme.sh) README.md +diff <(cargo -q run --manifest-path tools/Cargo.toml -p generate-readme) README.md exit $? diff --git a/generate-readme.sh b/generate-readme.sh deleted file mode 100755 index be0dc929af..0000000000 --- a/generate-readme.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash -# -# Copyright 2022 The Fuchsia Authors -# -# Licensed under a BSD-style license , Apache License, Version 2.0 -# , or the MIT -# license , at your option. -# This file may not be copied, modified, or distributed except according to -# those terms. - -set -eo pipefail - -COPYRIGHT_HEADER=$(mktemp) -BODY=$(mktemp) -DISCLAIMER_FOOTER=$(mktemp) - -cat > $COPYRIGHT_HEADER <<'EOF' - - -EOF - -# This uses the `cargo readme` tool, which you can install via `cargo install -# cargo-readme --version 3.2.0`. -# -# The `sed` command is used to strip code links like: -# -# /// Here is a link to [`Vec`]. -# -# These links don't work in a Markdown file, and so we remove the `[` and `]` -# characters to convert them to non-link code snippets. -cargo readme --no-license | sed 's/\[\(`[^`]*`\)]/\1/g' > $BODY - -cat > $DISCLAIMER_FOOTER <<'EOF' - -## Disclaimer - -Disclaimer: Zerocopy is not an officially supported Google product. -EOF - -cat $COPYRIGHT_HEADER $BODY $DISCLAIMER_FOOTER diff --git a/src/lib.rs b/src/lib.rs index 555922d70d..8f14ce5d35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,7 +10,7 @@ // After updating the following doc comment, make sure to run the following // command to update `README.md` based on its contents: // -// ./generate-readme.sh > README.md +// cargo -q run --manifest-path tools/Cargo.toml -p generate-readme > README.md //! *Need more out of zerocopy? //! Submit a [customer request issue][customer-request-issue]!* @@ -164,7 +164,7 @@ variant_size_differences )] #![cfg_attr( - __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS, + __INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS, deny(fuzzy_provenance_casts, lossy_provenance_casts) )] #![deny( @@ -233,7 +233,7 @@ )] #![cfg_attr(doc_cfg, feature(doc_cfg))] #![cfg_attr( - __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS, + __INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS, feature(layout_for_ptr, strict_provenance) )] @@ -318,9 +318,9 @@ pub use crate::pointer::{Maybe, MaybeAligned, Ptr}; use crate::util::polyfills::NonNullExt as _; #[rustversion::nightly] -#[cfg(all(test, not(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)))] +#[cfg(all(test, not(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)))] const _: () = { - #[deprecated = "some tests may be skipped due to missing RUSTFLAGS=\"--cfg __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS\""] + #[deprecated = "some tests may be skipped due to missing RUSTFLAGS=\"--cfg __INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS\""] const _WARNING: () = (); #[warn(deprecated)] _WARNING @@ -6243,7 +6243,7 @@ mod tests { } #[test] - #[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] + #[cfg(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] fn test_validate_rust_layout() { use core::ptr::NonNull; diff --git a/src/macro_util.rs b/src/macro_util.rs index c892eb3223..3744375ef3 100644 --- a/src/macro_util.rs +++ b/src/macro_util.rs @@ -21,7 +21,7 @@ use core::{marker::PhantomData, mem::ManuallyDrop}; // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this // `cfg` when `size_of_val_raw` is stabilized. -#[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] +#[cfg(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] use core::ptr::{self, NonNull}; /// A compile-time check that should be one particular value. @@ -70,7 +70,7 @@ const _64K: usize = 1 << 16; // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this // `cfg` when `size_of_val_raw` is stabilized. -#[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] +#[cfg(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] #[repr(C, align(65536))] struct Aligned64kAllocation([u8; _64K]); @@ -82,7 +82,7 @@ struct Aligned64kAllocation([u8; _64K]); /// allocation with size and alignment 2^16, and to have valid provenance. // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this // `cfg` when `size_of_val_raw` is stabilized. -#[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] +#[cfg(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] pub const ALIGNED_64K_ALLOCATION: NonNull<[u8]> = { const REF: &Aligned64kAllocation = &Aligned64kAllocation([0; _64K]); let ptr: *const Aligned64kAllocation = REF; @@ -111,7 +111,7 @@ pub const ALIGNED_64K_ALLOCATION: NonNull<[u8]> = { /// `trailing_field_offset!` produces code which is valid in a `const` context. // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this // `cfg` when `size_of_val_raw` is stabilized. -#[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] +#[cfg(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. #[macro_export] macro_rules! trailing_field_offset { @@ -211,7 +211,7 @@ macro_rules! trailing_field_offset { /// `align_of!` produces code which is valid in a `const` context. // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this // `cfg` when `size_of_val_raw` is stabilized. -#[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] +#[cfg(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. #[macro_export] macro_rules! align_of { @@ -477,7 +477,7 @@ mod tests { // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove // this `cfg` when `size_of_val_raw` is stabilized. #[allow(clippy::decimal_literal_representation)] - #[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] + #[cfg(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] #[test] fn test_trailing_field_offset() { assert_eq!(mem::align_of::(), _64K); @@ -579,7 +579,7 @@ mod tests { // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove // this `cfg` when `size_of_val_raw` is stabilized. #[allow(clippy::decimal_literal_representation)] - #[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] + #[cfg(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] #[test] fn test_align_of_dst() { // Test that `align_of!` correctly computes the alignment of DSTs. diff --git a/src/util.rs b/src/util.rs index 6d93ec74c5..a471c535da 100644 --- a/src/util.rs +++ b/src/util.rs @@ -35,7 +35,7 @@ impl AsAddress for *const T { // `.addr()` instead of `as usize` once it's stable, and get rid of this // `allow`. Currently, `as usize` is the only way to accomplish this. #[allow(clippy::as_conversions)] - #[cfg_attr(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS, allow(lossy_provenance_casts))] + #[cfg_attr(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS, allow(lossy_provenance_casts))] return self.cast::<()>() as usize; } } @@ -311,7 +311,7 @@ mod proofs { // Restricted to nightly since we use the unstable `usize::next_multiple_of` // in our model implementation. - #[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] + #[cfg(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] #[kani::proof] fn prove_padding_needed_for() { fn model_impl(len: usize, align: NonZeroUsize) -> usize { diff --git a/tools/Cargo.toml b/tools/Cargo.toml new file mode 100644 index 0000000000..4190765f80 --- /dev/null +++ b/tools/Cargo.toml @@ -0,0 +1,25 @@ +# Copyright 2018 The Fuchsia Authors +# +# Licensed under a BSD-style license , Apache License, Version 2.0 +# , or the MIT +# license , at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. + +[workspace] +members = [ + "cargo-zerocopy", + "generate-readme", +] +resolver = "2" + +[workspace.package] +edition = "2021" +version = "0.0.0" +authors = ["Joshua Liebow-Feeser "] +license = "BSD-2-Clause OR Apache-2.0 OR MIT" +publish = false + +[workspace.dependencies] +regex = "1" +serde_json = "1" diff --git a/tools/cargo-zerocopy/Cargo.toml b/tools/cargo-zerocopy/Cargo.toml new file mode 100644 index 0000000000..c4025bb6b3 --- /dev/null +++ b/tools/cargo-zerocopy/Cargo.toml @@ -0,0 +1,18 @@ +# Copyright 2024 The Fuchsia Authors +# +# Licensed under a BSD-style license , Apache License, Version 2.0 +# , or the MIT +# license , at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. + +[package] +edition.workspace = true +name = "cargo-zerocopy" +version.workspace = true +authors.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +serde_json.workspace = true diff --git a/tools/cargo-zerocopy/src/main.rs b/tools/cargo-zerocopy/src/main.rs new file mode 100644 index 0000000000..703b706521 --- /dev/null +++ b/tools/cargo-zerocopy/src/main.rs @@ -0,0 +1,253 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license , Apache License, Version 2.0 +// , or the MIT +// license , at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +// This script is a thin wrapper around Cargo that provides human-friendly +// toolchain names which are automatically translated to the toolchain versions +// we have pinned in CI. +// +// cargo-zerocopy --version # looks up the version for the named toolchain +// cargo-zerocopy + [...] # runs cargo commands with the named toolchain +// cargo-zerocopy +all [...] # runs cargo commands with each toolchain +// +// The meta-toolchain "all" instructs this script to run the provided command +// once for each "major" toolchain (msrv, stable, nightly). This does not +// include any toolchain which is listed in the `package.metadata.build-rs` +// Cargo.toml section. +// +// A common task that is especially annoying to perform by hand is to update +// trybuild's stderr files. Using this script: +// +// TRYBUILD=overwrite ./cargo-zerocopy +all test --workspace + +use std::{ + env, fmt, + io::{self, Read as _}, + process::{self, Command}, +}; + +use serde_json::{Map, Value}; + +#[derive(Debug)] +enum Error { + NoArguments, + UnrecognizedArgument(String), + MissingToolchainVersion, + UnrecognizedToolchain(String), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::NoArguments => write!(f, "No arguments provided"), + Self::UnrecognizedArgument(arg) => write!(f, "Unrecognized argument: '{arg}'"), + Self::MissingToolchainVersion => write!(f, "No toolchain version specified after '--version'"), + Self::UnrecognizedToolchain(name) => write!(f, "Unrecognized toolchain name: `{name}` (options are 'msrv', 'stable', and 'nightly')") + } + } +} + +impl std::error::Error for Error {} + +struct Versions { + msrv: String, + stable: String, + nightly: String, + build_rs: Map, +} + +impl Versions { + fn get(&self, name: &str) -> Result<&str, Error> { + Ok(match name { + "msrv" => &self.msrv, + "stable" => &self.stable, + "nightly" => &self.nightly, + _ => self + .build_rs + .get(name) + .ok_or(Error::UnrecognizedToolchain(name.to_string())) + .map(|value| value.as_str().unwrap())?, + }) + } +} + +fn get_toolchain_versions() -> Versions { + // NOTE(#547): We set `CARGO_TARGET_DIR` here because `cargo metadata` + // sometimes causes the `cargo-metadata` crate to be rebuilt from source + // using the default toolchain. This has the effect of clobbering any + // existing build artifacts from whatever toolchain the user has specified + // (e.g., `+nightly`), causing the subsequent `cargo` invocation to rebuild + // unnecessarily. By specifying a separate build directory here, we ensure + // that this never clobbers the build artifacts used by the later `cargo` + // invocation. + // + // In CI, make sure to use the default stable toolchain. If we're testing on + // our MSRV, then we also have our MSRV toolchain installed. As of this + // writing, our MSRV is low enough that the correspoding Rust toolchain's + // Cargo doesn't know about the `rust-version` field, and so if we were to + // use Cargo with that toolchain, `pkg-meta` would return `null` when asked + // to retrieve the `rust-version` field. This also requires `RUSTFLAGS=''` + // to override any unstable `RUSTFLAGS` set by the caller. + let output = rustup( + ["run", "stable", "cargo", "metadata", "--format-version", "1"], + Some(("CARGO_TARGET_DIR", "target/cargo-zerocopy")), + ) + .env_remove("RUSTFLAGS") + .output() + .unwrap(); + + let json = serde_json::from_slice::(&output.stdout).unwrap(); + let packages = json.as_object().unwrap().get("packages").unwrap(); + packages + .as_array() + .unwrap() + .iter() + .filter_map(|p| { + let package = p.as_object().unwrap(); + if package.get("name").unwrap().as_str() == Some("zerocopy") { + let metadata = package.get("metadata").unwrap().as_object().unwrap(); + let ci = metadata.get("ci").unwrap().as_object().unwrap(); + Some(Versions { + msrv: package.get("rust_version").unwrap().as_str().unwrap().to_string(), + stable: ci.get("pinned-stable").unwrap().as_str().unwrap().to_string(), + nightly: ci.get("pinned-nightly").unwrap().as_str().unwrap().to_string(), + build_rs: metadata.get("build-rs").unwrap().as_object().unwrap().clone(), + }) + } else { + None + } + }) + .next() + .unwrap() +} + +fn is_toolchain_installed(versions: &Versions, name: &str) -> Result { + let version = versions.get(name)?; + let output = rustup(["run", version, "cargo", "version"], None).output().unwrap(); + if output.status.success() { + let output = rustup([&format!("+{version}"), "component", "list"], None).output().unwrap(); + let stdout = String::from_utf8(output.stdout).unwrap(); + Ok(stdout.contains("rust-src (installed)")) + } else { + Ok(false) + } +} + +fn install_toolchain_or_exit(versions: &Versions, name: &str) -> Result<(), Error> { + eprintln!("[cargo-zerocopy] missing either toolchain '{name}' or component 'rust-src'"); + if env::vars().any(|v| v.0 == "GITHUB_RUN_ID") { + // If we're running in a GitHub action, then it's better to bail than to + // hang waiting for input we're never going to get. + process::exit(1); + } + + loop { + eprint!("[cargo-zerocopy] would you like to install toolchain '{name}' and component 'rust-src' via 'rustup' (y/n)? "); + let mut input = [0]; + io::stdin().read_exact(&mut input).unwrap(); + match input[0] as char { + 'y' | 'Y' => break, + 'n' | 'N' => process::exit(1), + _ => (), + } + } + + let version = versions.get(name)?; + rustup(["toolchain", "install", &version, "-c", "rust-src"], None).status().unwrap(); + + Ok(()) +} + +fn get_rustflags(name: &str) -> &'static str { + if name == "nightly" { + "--cfg __INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS " + } else { + "" + } +} + +fn rustup<'a>(args: impl IntoIterator, env: Option<(&str, &str)>) -> Command { + let mut cmd = Command::new("rustup"); + // It's important to set `RUSTUP_TOOLCHAIN` to override any value set while + // running this program. That variable overrides any `+` CLI + // argument. + cmd.args(args).env("RUSTUP_TOOLCHAIN", ""); + if let Some((name, val)) = env { + cmd.env(name, val); + } + cmd +} + +fn delegate_cargo() -> Result<(), Error> { + let mut args = env::args(); + let this = args.next().unwrap(); + let versions = get_toolchain_versions(); + + match args.next().as_deref() { + None => Err(Error::NoArguments), + Some("--version") => { + let name = args.next().ok_or(Error::MissingToolchainVersion)?; + println!("{}", versions.get(&name)?); + Ok(()) + } + Some("+all") => { + eprintln!("[cargo-zerocopy] warning: running the same command for each toolchain (msrv, stable, nightly)"); + let args = args.collect::>(); + + for toolchain in ["msrv", "stable", "nightly"] { + eprintln!("[cargo-zerocopy] running with toolchain: {toolchain}"); + Command::new(this.clone()) + .arg(format!("+{toolchain}")) + .args(args.clone()) + .status() + .unwrap(); + } + Ok(()) + } + Some(arg) => { + if let Some(name) = arg.strip_prefix('+') { + let version = versions.get(name)?; + + if !is_toolchain_installed(&versions, name)? { + install_toolchain_or_exit(&versions, name)?; + } + + let env_rustflags = env::vars() + .filter_map(|(k, v)| if k == "RUSTFLAGS" { Some(v) } else { None }) + .next() + .unwrap_or_default(); + + let rustflags = format!("{}{}", get_rustflags(name), env_rustflags); + rustup(["run", &version, "cargo"], Some(("RUSTFLAGS", &rustflags))) + .args(args) + .status() + .unwrap(); + + Ok(()) + } else { + Err(Error::UnrecognizedArgument(arg.to_string())) + } + } + } +} + +fn print_usage() { + let name = env::args().next().unwrap(); + + eprintln!("Usage:"); + eprintln!(" {} --version ", name); + eprintln!(" {} + [...]", name); + eprintln!(" {} +all [...]", name); +} + +fn main() { + if let Err(e) = delegate_cargo() { + eprintln!("Error: {e}"); + print_usage(); + process::exit(1); + } +} diff --git a/tools/generate-readme/Cargo.toml b/tools/generate-readme/Cargo.toml new file mode 100644 index 0000000000..10dc0d18f3 --- /dev/null +++ b/tools/generate-readme/Cargo.toml @@ -0,0 +1,18 @@ +# Copyright 2024 The Fuchsia Authors +# +# Licensed under a BSD-style license , Apache License, Version 2.0 +# , or the MIT +# license , at your option. +# This file may not be copied, modified, or distributed except according to +# those terms. + +[package] +edition.workspace = true +name = "generate-readme" +version.workspace = true +authors.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +regex.workspace = true diff --git a/tools/generate-readme/src/main.rs b/tools/generate-readme/src/main.rs new file mode 100644 index 0000000000..8945caea7b --- /dev/null +++ b/tools/generate-readme/src/main.rs @@ -0,0 +1,51 @@ +// Copyright 2024 The Fuchsia Authors +// +// Licensed under a BSD-style license , Apache License, Version 2.0 +// , or the MIT +// license , at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +use std::process::Command; + +use regex::{Captures, Regex}; + +const COPYRIGHT_HEADER: &str = "\ +\ +"; + +const DISCLAIMER_FOOTER: &str = "\ +## Disclaimer + +Disclaimer: Zerocopy is not an officially supported Google product.\ +"; + +fn main() { + // This uses the `cargo readme` tool, which you can install via `cargo install + // cargo-readme --version 3.2.0`. + let output = Command::new("cargo").args(["readme", "--no-license"]).output().unwrap(); + + let readme = String::from_utf8(output.stdout).unwrap(); + + // This regex is used to strip code links like: + // + // /// Here is a link to [`Vec`]. + // + // These links don't work in a Markdown file, and so we remove the `[` and `]` + // characters to convert them to non-link code snippets. + let body = Regex::new(r"\[(`[^`]*`)\]") + .unwrap() + .replace_all(&readme, |caps: &Captures| caps[1].to_string()); + + println!("{}\n\n{}\n{}", COPYRIGHT_HEADER, body, DISCLAIMER_FOOTER); +} diff --git a/win-cargo.bat b/win-cargo.bat new file mode 100644 index 0000000000..c0e3b04471 --- /dev/null +++ b/win-cargo.bat @@ -0,0 +1,16 @@ +@rem Copyright 2024 The Fuchsia Authors + +@rem Licensed under a BSD-style license , Apache License, Version 2.0 +@rem , or the MIT +@rem license , at your option. +@rem This file may not be copied, modified, or distributed except according to +@rem those terms. + +@rem Build `cargo-zerocopy` without any RUSTFLAGS set in the environment +@set TEMP_RUSTFLAGS=%RUSTFLAGS% +@set RUSTFLAGS= +@cargo +stable build --manifest-path tools/Cargo.toml -p cargo-zerocopy -q +@set RUSTFLAGS=%TEMP_RUSTFLAGS% +@set TEMP_RUSTFLAGS= +@rem Thin wrapper around the `cargo-zerocopy` binary in `tools/cargo-zerocopy` +@tools\target\debug\cargo-zerocopy %*