diff --git a/.cargo/config.toml b/.cargo/config.toml index c5b6fcd9d41..209d15c760b 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -8,6 +8,7 @@ # CI scripts: # - .github/buildomat/build-and-test.sh # - .github/buildomat/jobs/clippy.sh +# - .github/buildomat/jobs/check-features.sh # - .github/workflows/rust.yml # [build] diff --git a/.github/buildomat/jobs/check-features.sh b/.github/buildomat/jobs/check-features.sh new file mode 100644 index 00000000000..7e8ffc79859 --- /dev/null +++ b/.github/buildomat/jobs/check-features.sh @@ -0,0 +1,40 @@ +#!/bin/bash +#: +#: name = "check-features (helios)" +#: variety = "basic" +#: target = "helios-2.0" +#: rust_toolchain = true +#: output_rules = [] + +# Run cargo check on illumos with feature-specifics like `no-default-features` +# or `feature-powerset`. + +set -o errexit +set -o pipefail +set -o xtrace + +cargo --version +rustc --version + +# NOTE: This version should be in sync with the recommended version in +# ./dev-tools/xtask/src/check-features.rs. +CARGO_HACK_VERSION='0.6.28' + +# +# Set up our PATH for use with this workspace. +# +source ./env.sh + +banner prerequisites +ptime -m bash ./tools/install_builder_prerequisites.sh -y + +banner check +export CARGO_INCREMENTAL=0 +ptime -m cargo check --workspace --bins --tests --no-default-features +RUSTDOCFLAGS="--document-private-items -D warnings" ptime -m cargo doc --workspace --no-deps --no-default-features + +# +# Check the feature set with the `cargo xtask check-features` command. +# +banner hack +ptime -m timeout 2h cargo xtask check-features --version "$CARGO_HACK_VERSION" --exclude-features image-trampoline,image-standard diff --git a/.github/buildomat/jobs/clippy.sh b/.github/buildomat/jobs/clippy.sh index 71aa04c907b..4040691b72a 100755 --- a/.github/buildomat/jobs/clippy.sh +++ b/.github/buildomat/jobs/clippy.sh @@ -10,7 +10,7 @@ # (that we want to check) is conditionally-compiled on illumos only. # # Note that `cargo clippy` includes `cargo check, so this ends up checking all -# of our code. +# of our (default) code. set -o errexit set -o pipefail diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2ef27831083..cc307fec595 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -53,7 +53,7 @@ jobs: run: cargo run --bin omicron-package -- -t default check # Note that `cargo clippy` includes `cargo check, so this ends up checking all - # of our code. + # of our (default) code. clippy-lint: runs-on: ubuntu-22.04 env: @@ -82,6 +82,40 @@ jobs: - name: Run Clippy Lints run: cargo xtask clippy + check-features: + runs-on: ubuntu-22.04 + env: + CARGO_INCREMENTAL: 0 + CARGO_HACK_VERSION: 0.6.28 + steps: + # This repo is unstable and unnecessary: https://github.com/microsoft/linux-package-repositories/issues/34 + - name: Disable packages.microsoft.com repo + run: sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + ref: ${{ github.event.pull_request.head.sha }} # see omicron#4461 + - uses: Swatinem/rust-cache@23bce251a8cd2ffc3c1075eaa2367cf899916d84 # v2.7.3 + if: ${{ github.ref != 'refs/heads/main' }} + - name: Report cargo version + run: cargo --version + - name: Update PATH + run: source "./env.sh"; echo "PATH=$PATH" >> "$GITHUB_ENV" + - name: Print PATH + run: echo $PATH + - name: Print GITHUB_ENV + run: cat "$GITHUB_ENV" + - name: Install Pre-Requisites + run: ./tools/install_builder_prerequisites.sh -y + - name: Run `cargo check` for no-default-features + run: cargo check --workspace --bins --tests --no-default-features + # Uses manifest for install + - uses: taiki-e/install-action@v2 + with: + tool: cargo-hack@${{ env.CARGO_HACK_VERSION }} + - name: Run Check on Features (Feature-Powerset, No-Dev-Deps) + timeout-minutes: 120 # 2 hours + run: cargo xtask check-features --no-install --exclude-features image-trampoline,image-standard + # This is just a test build of docs. Publicly available docs are built via # the separate "rustdocs" repo. build-docs: diff --git a/dev-tools/xtask/src/check_features.rs b/dev-tools/xtask/src/check_features.rs new file mode 100644 index 00000000000..5f690e08502 --- /dev/null +++ b/dev-tools/xtask/src/check_features.rs @@ -0,0 +1,134 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +//! Subcommand: cargo xtask check-features + +use anyhow::{bail, Context, Result}; +use clap::Parser; +use std::process::Command; + +/// The default version of `cargo-hack` to install. +/// We use a patch-floating version to avoid breaking the build when a new +/// version is released (locally). +const FLOAT_VERSION: &str = "~0.6.28"; + +#[derive(Parser)] +pub struct Args { + /// Features to exclude from the check. + #[clap(long)] + exclude_features: Option>, + /// Depth of the feature powerset to check. + #[clap(long)] + depth: Option, + /// Error format passed to `cargo hack check`. + #[clap(long, value_name = "FMT")] + message_format: Option, + /// Do not install `cargo-hack` before running the check. + #[clap(long, default_value_t = false)] + no_install: bool, + /// Version of `cargo-hack` to install. + #[clap(long)] + version: Option, +} + +/// Run `cargo hack check`. +pub fn run_cmd(args: Args) -> Result<()> { + if !args.no_install { + install_cargo_hack(args.version).unwrap(); + } + + let cargo = + std::env::var("CARGO").unwrap_or_else(|_| String::from("cargo")); + let mut command = Command::new(&cargo); + + command.args(&["hack", "check"]); + + if let Some(features) = args.exclude_features { + let ex = format!("--exclude-features={}", features.join(",")); + command.arg(ex); + } + + if let Some(depth) = args.depth { + let depth = format!("depth={}", depth); + command.arg(depth); + } + + // Pass along the `--message-format` flag if it was provided. + if let Some(fmt) = args.message_format { + command.args(["--message-format", &fmt]); + } + + command + // Make sure we check everything. + .arg("--workspace") + .arg("--bins") + // We want to check the feature powerset. + .arg("--feature-powerset") + .arg("--no-dev-deps") + .arg("--exclude-no-default-features"); + + eprintln!( + "running: {:?} {}", + &cargo, + command + .get_args() + .map(|arg| format!("{:?}", arg.to_str().unwrap())) + .collect::>() + .join(" ") + ); + + let exit_status = command + .spawn() + .context("failed to spawn child process")? + .wait() + .context("failed to wait for child process")?; + + if !exit_status.success() { + bail!("check-features failed: {}", exit_status); + } + + Ok(()) +} + +/// Install `cargo-hack` at the specified version or the default version. +fn install_cargo_hack(version: Option) -> Result<()> { + let cargo = + std::env::var("CARGO").unwrap_or_else(|_| String::from("cargo")); + + let mut command = Command::new(&cargo); + + if let Some(version) = version { + command.args(&["install", "cargo-hack", "--version", &version]); + } else { + command.args(&[ + "install", + "cargo-hack", + "--locked", + "--version", + FLOAT_VERSION, + ]); + } + + eprintln!( + "running: {:?} {}", + &cargo, + command + .get_args() + .map(|arg| format!("{:?}", arg.to_str().unwrap())) + .collect::>() + .join(" ") + ); + + let exit_status = command + .spawn() + .expect("failed to spawn child process") + .wait() + .expect("failed to wait for child process"); + + if !exit_status.success() { + bail!("cargo-hack install failed: {}", exit_status); + } + + Ok(()) +} diff --git a/dev-tools/xtask/src/main.rs b/dev-tools/xtask/src/main.rs index 3d8acceb3d6..77e7c790a6c 100644 --- a/dev-tools/xtask/src/main.rs +++ b/dev-tools/xtask/src/main.rs @@ -10,6 +10,7 @@ use anyhow::{Context, Result}; use cargo_metadata::Metadata; use clap::{Parser, Subcommand}; +mod check_features; mod check_workspace_deps; mod clippy; mod download; @@ -38,6 +39,8 @@ enum Cmds { /// Run Argon2 hash with specific parameters (quick performance check) Argon2(external::External), + /// Check that all features are flagged correctly + CheckFeatures(check_features::Args), /// Check that dependencies are not duplicated in any packages in the /// workspace CheckWorkspaceDeps, @@ -86,6 +89,7 @@ async fn main() -> Result<()> { external.cargo_args(["--release"]).exec_example("argon2") } Cmds::Clippy(args) => clippy::run_cmd(args), + Cmds::CheckFeatures(args) => check_features::run_cmd(args), Cmds::CheckWorkspaceDeps => check_workspace_deps::run_cmd(), Cmds::Download(args) => download::run_cmd(args).await, #[cfg(target_os = "illumos")]