diff --git a/src/cargo/core/compiler/build_config.rs b/src/cargo/core/compiler/build_config.rs index 044cf4a74..58438aeba 100644 --- a/src/cargo/core/compiler/build_config.rs +++ b/src/cargo/core/compiler/build_config.rs @@ -115,6 +115,18 @@ impl BuildConfig { (None, _) => false, }; + let timing_outputs = match (cfg.analysis.as_ref(), gctx.cli_unstable().build_analysis) { + // Enable HTML output to pretend we are persisting timing data for now. + (Some(analysis), true) if analysis.enabled => vec![TimingOutput::Html], + (Some(_), false) => { + gctx.shell().warn( + "ignoring 'build.analysis' config, pass `-Zbuild-analysis` to enable it", + )?; + Vec::new() + } + _ => Vec::new(), + }; + Ok(BuildConfig { requested_kinds, jobs, @@ -130,7 +142,7 @@ impl BuildConfig { rustfix_diagnostic_server: Rc::new(RefCell::new(None)), export_dir: None, future_incompat_report: false, - timing_outputs: Vec::new(), + timing_outputs, sbom, compile_time_deps_only: false, }) diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index bd4b85263..3c6410ec6 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -844,6 +844,7 @@ unstable_cli_options!( avoid_dev_deps: bool = ("Avoid installing dev-dependencies if possible"), binary_dep_depinfo: bool = ("Track changes to dependency artifacts"), bindeps: bool = ("Allow Cargo packages to depend on bin, cdylib, and staticlib crates, and use the artifacts built by those crates"), + build_analysis: bool = ("Record and persist build metrics across runs, with commands to query past builds."), #[serde(deserialize_with = "deserialize_comma_separated_list")] build_std: Option> = ("Enable Cargo to compile the standard library itself as part of a crate graph compilation"), #[serde(deserialize_with = "deserialize_comma_separated_list")] @@ -1363,6 +1364,7 @@ impl CliUnstable { "avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?, "binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?, "bindeps" => self.bindeps = parse_empty(k, v)?, + "build-analysis" => self.build_analysis = parse_empty(k, v)?, "build-std" => self.build_std = Some(parse_list(v)), "build-std-features" => self.build_std_features = Some(parse_list(v)), "cargo-lints" => self.cargo_lints = parse_empty(k, v)?, diff --git a/src/cargo/util/context/mod.rs b/src/cargo/util/context/mod.rs index 409929e78..0f4e64d45 100644 --- a/src/cargo/util/context/mod.rs +++ b/src/cargo/util/context/mod.rs @@ -2768,6 +2768,15 @@ pub struct CargoBuildConfig { pub warnings: Option, /// Unstable feature `-Zsbom`. pub sbom: Option, + /// Unstable feature `-Zbuild-analysis`. + pub analysis: Option, +} + +/// Metrics collection for build analysis. +#[derive(Debug, Deserialize, Default)] +#[serde(rename_all = "kebab-case")] +pub struct CargoBuildAnalysis { + pub enabled: bool, } /// Whether warnings should warn, be allowed, or cause an error. diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index 6b14d563d..01800764d 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -113,6 +113,7 @@ Each new feature described below should explain how to use it. * [Build-plan](#build-plan) --- Emits JSON information on which commands will be run. * [unit-graph](#unit-graph) --- Emits JSON for Cargo's internal graph structure. * [`cargo rustc --print`](#rustc---print) --- Calls rustc with `--print` to display information from rustc. + * [Build analysis](#build-analysis) --- Record and persist detailed build metrics across runs, with new commands to query past builds. * Configuration * [config-include](#config-include) --- Adds the ability for config files to include other files. * [`cargo config`](#cargo-config) --- Adds a new subcommand for viewing config files. @@ -1922,6 +1923,22 @@ HTML/JSON output. cargo +nightly -Zsection-timings build --timings ``` +## Build analysis + +* Original Issue: [rust-lang/rust-project-goals#332](https://github.com/rust-lang/rust-project-goals/pull/332) +* Tracking Issue: [#15844](https://github.com/rust-lang/cargo/issues/15844) + +The `-Zbuild-analysis` feature records and persists detailed build metrics +(timings, rebuild reasons, etc.) across runs, with new commands to query past builds. + +```toml +# Example config.toml file. + +# Enable the build metric collection +[build.analysis] +enabled = true +``` + # Stabilized and removed features ## Compile progress diff --git a/tests/testsuite/build_analysis.rs b/tests/testsuite/build_analysis.rs new file mode 100644 index 000000000..a06be01a0 --- /dev/null +++ b/tests/testsuite/build_analysis.rs @@ -0,0 +1,45 @@ +//! Tests for `-Zbuild-analysis`. + +use crate::prelude::*; + +use cargo_test_support::basic_manifest; +use cargo_test_support::project; +use cargo_test_support::str; + +#[cargo_test] +fn gated() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.0.0")) + .file("src/lib.rs", "") + .build(); + + p.cargo("check") + .env("CARGO_BUILD_ANALYSIS_ENABLED", "true") + .masquerade_as_nightly_cargo(&["build-analysis"]) + .with_stderr_data(str![[r#" +[WARNING] ignoring 'build.analysis' config, pass `-Zbuild-analysis` to enable it +[CHECKING] foo v0.0.0 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} + +#[cargo_test] +fn simple() { + let p = project() + .file("Cargo.toml", &basic_manifest("foo", "0.0.0")) + .file("src/lib.rs", "") + .build(); + + p.cargo("check -Zbuild-analysis") + .env("CARGO_BUILD_ANALYSIS_ENABLED", "true") + .masquerade_as_nightly_cargo(&["build-analysis"]) + .with_stderr_data(str![[r#" +[CHECKING] foo v0.0.0 ([ROOT]/foo) + Timing report saved to [ROOT]/foo/target/cargo-timings/cargo-timing-[..].html +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s + +"#]]) + .run(); +} diff --git a/tests/testsuite/cargo/z_help/stdout.term.svg b/tests/testsuite/cargo/z_help/stdout.term.svg index 778215a09..623911104 100644 --- a/tests/testsuite/cargo/z_help/stdout.term.svg +++ b/tests/testsuite/cargo/z_help/stdout.term.svg @@ -1,4 +1,4 @@ - +