CI: Apply a consistent Cargo profile (#5133)

## Linked issue

Related:
https://github.com/ankitects/anki/pull/5102#issuecomment-4923922894

## Summary / motivation


This adds a new Cargo profile (`ci`) for use in all Rust build commands
on CI. The goal is to reduce unnecessary recompilation of the same
crates in dev/release profiles.

## Steps to reproduce (before)

View the logs of the last CI run on main and notice that some crates are
getting compiled with the `release` profile, e.g. compilation ends with
"Finished `release` profile [optimized]".

## How to test (after)

View the logs of the last CI run in this PR and confirm all Rust
compilation commands end with "Finished `ci` profile [unoptimized]",
indicating that only a single profile is being used.

### Checklist (minimum)

- [x] I ran `./ninja check` or an equivalent relevant check locally.
- [ ] I added or updated tests when the change is non-trivial or
behavior changed.

## Scope

- [x] This PR is focused on one change (no unrelated edits).
This commit is contained in:
Abdo
2026-08-12 10:17:29 +03:00
committed by GitHub
parent 054ce44868
commit 3c9b1daf35
12 changed files with 121 additions and 41 deletions
+24 -13
View File
@@ -138,11 +138,13 @@ jobs:
if: github.event_name == 'pull_request'
run: test -d out/coverage && mv out/coverage out/coverage-baseline || true
- name: Build, lint, and test
- name: Build
run: just build
- name: Lint and test
env:
ONLINE_TESTS: "1"
run: |
just build
just lint
just test --coverage
@@ -270,9 +272,11 @@ jobs:
- name: Symlink node_modules
run: ln -sf out/node_modules .
- name: Build, lint, and test
- name: Build
run: just build
- name: Lint and test
run: |
just build
just lint
just test
@@ -343,9 +347,11 @@ jobs:
- name: Symlink node_modules
run: ln -sf out/node_modules .
- name: Build, lint, and test
- name: Build
run: just build
- name: Lint and test
run: |
just build
just lint
just test
@@ -415,10 +421,11 @@ jobs:
- name: Symlink node_modules
run: ln -sf out/node_modules .
- name: Build, lint, and test
- name: Build
run: just build
- name: Lint and test
run: |
just build
just wheels
just lint
just test
@@ -495,9 +502,11 @@ jobs:
cargo-llvm-cov@0.8.4
cargo-nextest@0.9.99
- name: Build, lint, and test
- name: Build
run: just build
- name: Lint and test
run: |
just build
just lint
just test
@@ -568,9 +577,11 @@ jobs:
cargo-llvm-cov@0.8.4
cargo-nextest@0.9.99
- name: Build, lint, and test
- name: Build
run: just build
- name: Lint and test
run: |
just build
just lint
just test
+5
View File
@@ -175,3 +175,8 @@ debug = 0
[profile.release-lto]
inherits = "release"
lto = true
[profile.ci]
inherits = "dev"
# TODO: consider setting once caching issues are fixed (#5318)
# incremental = false
+1 -2
View File
@@ -5,7 +5,6 @@ use std::env;
use anyhow::Result;
use ninja_gen::action::BuildAction;
use ninja_gen::build::BuildProfile;
use ninja_gen::build::FilesHandle;
use ninja_gen::cargo::CargoBuild;
use ninja_gen::cargo::CargoClippy;
@@ -238,7 +237,7 @@ pub fn check_minilints(build: &mut Build) -> Result<()> {
outputs: &[RustOutput::Binary("minilints")],
target: None,
extra_args: "-p minilints",
release_override: Some(BuildProfile::Debug),
release_override: Some(build.build_profile.for_build_tools()),
},
)
}
+37 -2
View File
@@ -3,6 +3,7 @@
use std::collections::HashMap;
use std::collections::HashSet;
use std::fmt::Display;
use std::fmt::Write;
use anyhow::Result;
@@ -356,16 +357,50 @@ pub enum BuildProfile {
Debug,
Release,
ReleaseWithLto,
Ci,
}
impl Display for BuildProfile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let name = match self {
BuildProfile::Debug => "dev",
BuildProfile::Release => "release",
BuildProfile::ReleaseWithLto => "release-lto",
BuildProfile::Ci => "ci",
};
f.write_str(name)
}
}
impl BuildProfile {
fn from_env() -> Self {
pub fn from_env() -> Self {
match std::env::var("RELEASE").unwrap_or_default().as_str() {
"1" => Self::Release,
"2" => Self::ReleaseWithLto,
_ => Self::Debug,
_ => match std::env::var("CI").unwrap_or_default().as_str() {
"true" => Self::Ci,
_ => Self::Debug,
},
}
}
/// The profile to build helper tools like configure/minilints with:
/// never optimized, but otherwise matching the main profile so that
/// dependency builds are shared.
pub fn for_build_tools(self) -> Self {
match self {
Self::Release | Self::ReleaseWithLto => Self::Debug,
other => other,
}
}
pub fn as_cargo_arg(self) -> String {
format!("--profile {}", self)
}
pub fn as_nextest_arg(self) -> String {
format!("--cargo-profile {}", self)
}
}
pub trait FilesHandle {
+7 -11
View File
@@ -72,6 +72,7 @@ fn profile_output_dir(profile: BuildProfile) -> &'static str {
BuildProfile::Debug => "debug",
BuildProfile::Release => "release",
BuildProfile::ReleaseWithLto => "release-lto",
BuildProfile::Ci => "ci",
}
}
@@ -93,7 +94,7 @@ impl BuildAction for CargoBuild<'_> {
let release_build = self
.release_override
.unwrap_or_else(|| build.build_profile());
let release_arg = profile_arg_for_cargo(release_build).unwrap_or_default();
let release_arg = release_build.as_cargo_arg();
let target_arg = if let Some(target) = self.target {
format!("--target {target}")
} else {
@@ -126,14 +127,6 @@ impl BuildAction for CargoBuild<'_> {
}
}
fn profile_arg_for_cargo(profile: BuildProfile) -> Option<&'static str> {
match profile {
BuildProfile::Debug => None,
BuildProfile::Release => Some("--release"),
BuildProfile::ReleaseWithLto => Some("--profile release-lto"),
}
}
fn setup_flags(build: &mut Build) -> Result<()> {
build.once_only("cargo_flags_and_pool", |build| {
build.variable("cargo_flags", "--locked");
@@ -151,10 +144,11 @@ pub struct CargoTest {
impl BuildAction for CargoTest {
fn command(&self) -> &str {
"cargo nextest run --color=always --failure-output=final --status-level=none $cargo_flags"
"cargo nextest run --color=always --failure-output=final --status-level=none $profile_arg $cargo_flags"
}
fn files(&mut self, build: &mut impl FilesHandle) {
build.add_variable("profile_arg", build.build_profile().as_nextest_arg());
build.add_inputs("", &self.inputs);
if !running_on_ci() {
build.add_inputs("", inputs![":cargo-nextest"]);
@@ -186,10 +180,12 @@ pub struct CargoClippy {
impl BuildAction for CargoClippy {
fn command(&self) -> &str {
"cargo clippy $cargo_flags --tests -- -Dclippy::dbg_macro -Dwarnings"
"cargo clippy $release_arg $cargo_flags --tests -- -Dclippy::dbg_macro -Dwarnings"
}
fn files(&mut self, build: &mut impl FilesHandle) {
let release_arg = build.build_profile().as_cargo_arg();
build.add_variable("release_arg", release_arg);
build.add_inputs(
"",
inputs![&self.inputs, "Cargo.lock", "rust-toolchain.toml"],
+1 -2
View File
@@ -4,7 +4,6 @@
use anyhow::Result;
use crate::action::BuildAction;
use crate::build::BuildProfile;
use crate::build::FilesHandle;
use crate::cargo::CargoBuild;
use crate::cargo::RustOutput;
@@ -34,7 +33,7 @@ impl BuildAction for ConfigureBuild {
outputs: &[RustOutput::Binary("configure")],
target: None,
extra_args: "-p configure",
release_override: Some(BuildProfile::Debug),
release_override: Some(build.build_profile.for_build_tools()),
},
)?;
Ok(())
+10 -1
View File
@@ -9,9 +9,17 @@ use anyhow::Result;
use itertools::Itertools;
use crate::archives::with_exe;
use crate::build::BuildProfile;
use crate::input::space_separated;
use crate::Build;
fn profile_output_dir(profile: BuildProfile) -> &'static str {
match profile {
BuildProfile::Ci => "ci",
_ => "release",
}
}
impl Build {
pub fn render(&self) -> String {
let mut buf = String::new();
@@ -25,7 +33,8 @@ impl Build {
writeln!(&mut buf, "builddir = {}", self.buildroot.as_str()).unwrap();
writeln!(
&mut buf,
"runner = $builddir/rust/release/{}",
"runner = $builddir/rust/{}/{}",
profile_output_dir(self.build_profile),
with_exe("runner")
)
.unwrap();
+9 -1
View File
@@ -153,11 +153,19 @@ fn setup_build_root() -> Utf8PathBuf {
fn bootstrap_build() {
let status = Command::new("cargo")
.args(["run", "-p", "configure"])
.args(["run", "-p", "configure", "--profile", bootstrap_profile()])
.status();
assert!(status.expect("ninja").success());
}
fn bootstrap_profile() -> &'static str {
if env::var("RELEASE").is_err() && env::var("CI").as_deref() == Ok("true") {
"ci"
} else {
"dev"
}
}
fn maybe_update_buildhash(build_root: &Utf8Path) {
// only updated on release builds
let path = build_root.join("buildhash");
+9 -3
View File
@@ -8,11 +8,17 @@ else
out="$BUILD_ROOT"
fi
export CARGO_TARGET_DIR=$out/rust
export RECONFIGURE_KEY="${MAC_X86};${LIN_ARM64};${SOURCEMAP};${HMR}"
export RECONFIGURE_KEY="${MAC_X86};${LIN_ARM64};${SOURCEMAP};${HMR};${CI}"
if [ "$CI" = "true" ] && [ -z "$RELEASE" ]; then
runner_profile=ci
else
runner_profile=release
fi
if [ "$SKIP_RUNNER_BUILD" = "1" ]; then
echo "Runner not rebuilt."
else
cargo build -p runner --release
cargo build -p runner --profile $runner_profile
fi
exec $out/rust/release/runner build -- $*
exec $out/rust/$runner_profile/runner build -- $*
+7 -2
View File
@@ -14,19 +14,24 @@ export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-out/rust}"
if [[ "${CI:-}" == "true" ]]; then
# prebuilt binary shouldve been installed
cargo_cmd="cargo"
profile=ci
else
cargo_cmd="$LLVMCOVPATH/cargo-llvm-cov"
test -x "$cargo_cmd" || cargo install cargo-llvm-cov --version 0.8.4 --locked --root out
test -x "$LLVMCOVPATH/cargo-nextest" || cargo install cargo-nextest \
--version 0.9.99 --locked --no-default-features --features default-no-update --root out
profile=dev
fi
export PATH="$LLVMCOVPATH:$PATH"
ANKI_TEST_MODE=1 "$cargo_cmd" llvm-cov nextest --workspace --locked --json --summary-only \
ANKI_TEST_MODE=1 "$cargo_cmd" llvm-cov nextest --workspace --locked \
--cargo-profile "$profile" \
--json --summary-only \
--output-path $outdir/coverage-summary.json --fail-under-lines 64
if [ "$html" = "--html" ]; then
ANKI_TEST_MODE=1 "$cargo_cmd" llvm-cov report --html --output-dir $outdir
ANKI_TEST_MODE=1 "$cargo_cmd" llvm-cov report --profile "$profile" \
--html --output-dir $outdir
echo "Rust coverage report: $outdir/html/index.html"
fi
+6 -2
View File
@@ -19,6 +19,7 @@ if "%CARGO_TARGET_DIR%"=="" set "CARGO_TARGET_DIR=out\rust"
if "%CI%"=="true" (
rem prebuilt binary shouldve been installed earlier
set "CARGO_CMD=cargo"
set PROFILE=ci
) else (
if not exist %LLVMCOVPATH% mkdir %LLVMCOVPATH%
if not exist %LLVMCOVPATH%\cargo-llvm-cov.exe (
@@ -28,14 +29,17 @@ if "%CI%"=="true" (
cargo install cargo-nextest --version 0.9.99 --locked --no-default-features --features default-no-update --root out || exit /b 1
)
set "CARGO_CMD=%LLVMCOVPATH%\cargo-llvm-cov.exe"
set PROFILE=dev
)
set "PATH=%LLVMCOVPATH%;%PATH%"
set "ANKI_TEST_MODE=1"
"%CARGO_CMD%" llvm-cov nextest --workspace --locked --json --summary-only ^
"%CARGO_CMD%" llvm-cov nextest --workspace --locked ^
--cargo-profile %PROFILE% ^
--json --summary-only ^
--output-path %outdir%\coverage-summary.json --fail-under-lines 64 || exit /b 1
if "%1"=="--html" (
"%CARGO_CMD%" llvm-cov report --html --output-dir %outdir% || exit /b 1
"%CARGO_CMD%" llvm-cov report --profile %PROFILE% --html --output-dir %outdir% || exit /b 1
echo Rust coverage report: %outdir%\html\index.html
)
+5 -2
View File
@@ -1,5 +1,8 @@
@echo off
set CARGO_TARGET_DIR=%~dp0..\out\rust
set "RECONFIGURE_KEY=%SOURCEMAP%;%HMR%;%CI%"
set RUNNER_PROFILE=release
if "%CI%"=="true" if "%RELEASE%"=="" set RUNNER_PROFILE=ci
REM separate build+run steps so build env doesn't leak into subprocesses
cargo build -p runner --release || exit /b 1
out\rust\release\runner build %* || exit /b 1
cargo build -p runner --profile %RUNNER_PROFILE% || exit /b 1
out\rust\%RUNNER_PROFILE%\runner build %* || exit /b 1