Skip to content

Commit 20e4190

Browse files
feat(cargo-codspeed): handle rustflags from .cargo/config.toml
1 parent a32301d commit 20e4190

4 files changed

Lines changed: 79 additions & 10 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ itertools = "0.14.0"
2020
serde = { version = "1.0.217", features = ["derive"] }
2121
serde_json = "1.0.138"
2222
tempfile = "3.7.0"
23+
semver = "1.0"

crates/cargo-codspeed/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ itertools = { workspace = true }
2626
anstyle = "1.0.8"
2727
serde = { workspace = true }
2828
serde_json = { workspace = true }
29+
semver = { workspace = true }
2930
codspeed = { path = "../codspeed", version = "=3.0.2" }
3031

3132
[dev-dependencies]

crates/cargo-codspeed/src/build.rs

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::{
55
prelude::*,
66
};
77
use cargo_metadata::{camino::Utf8PathBuf, Message, Metadata, TargetKind};
8+
use semver::Version;
89
use std::process::{exit, Command, Stdio};
910

1011
struct BuildOptions<'a> {
@@ -105,15 +106,23 @@ impl BuildOptions<'_> {
105106
Ok(built_benches)
106107
}
107108

108-
/// Generates a subcommand to build the benchmarks by invoking cargo and forwarding the filters
109-
/// This command explicitly ignores the `self.benches`: all benches are built
110-
fn build_command(&self, measurement_mode: MeasurementMode) -> Command {
111-
let mut cargo = Command::new("cargo");
112-
cargo.args(["build", "--benches"]);
109+
/// Adds debug flags and codspeed compilation
110+
///
111+
/// If the user has set `RUSTFLAGS`, it will append the flags to it.
112+
/// Else, and if the cargo version allows it, it will set the cargo config through
113+
/// `--config 'build.rustflags=[ ... ]'`
114+
///
115+
/// # Why we do this
116+
/// As tracked in [https://github.com/rust-lang/cargo/issues/5376], setting `RUSTFLAGS`
117+
/// completely overrides rustflags from rust config
118+
/// We use the cargo built-in config mechanism to set the flags if the user has not set
119+
/// `RUSTFLAGS`.
120+
fn add_rust_flags(&self, cargo: &mut Command, measurement_mode: MeasurementMode) {
121+
let mut flags = Vec::new();
113122

114-
let mut rust_flags = std::env::var("RUSTFLAGS").unwrap_or_else(|_| "".into());
115123
// Add debug info (equivalent to -g)
116-
rust_flags.push_str(" -C debuginfo=2");
124+
flags.push("-C".to_string());
125+
flags.push("debuginfo=2".to_string());
117126

118127
// Prevent debug info stripping
119128
// https://doc.rust-lang.org/cargo/reference/profiles.html#release
@@ -122,13 +131,70 @@ impl BuildOptions<'_> {
122131
// In practice, if we set debug info through RUSTFLAGS, cargo still strips them, most
123132
// likely because debug = false in the release profile.
124133
// We also need to disable stripping through rust flags.
125-
rust_flags.push_str(" -C strip=none");
134+
flags.push("-C".to_string());
135+
flags.push("strip=none".to_string());
126136

127137
// Add the codspeed cfg flag if instrumentation mode is enabled
128138
if measurement_mode == MeasurementMode::Instrumentation {
129-
rust_flags.push_str(" --cfg codspeed");
139+
flags.push("--cfg".to_string());
140+
flags.push("codspeed".to_string());
141+
}
142+
143+
match std::env::var("RUSTFLAGS") {
144+
std::result::Result::Ok(existing_rustflags) => {
145+
// If RUSTFLAGS is set, append our flags to it
146+
let mut combined_flags = existing_rustflags;
147+
for flag in flags {
148+
combined_flags.push(' ');
149+
combined_flags.push_str(&flag);
150+
}
151+
cargo.env("RUSTFLAGS", combined_flags);
152+
}
153+
std::result::Result::Err(_) => {
154+
// Check if cargo version supports --config (Cargo 1.63+, 2022-08-11)
155+
if Self::cargo_supports_config_from_cli() {
156+
// Use --config to set rustflags
157+
let config_value = format!(
158+
"build.rustflags=[{}]",
159+
flags.into_iter().map(|f| format!("\"{f}\"")).join(",")
160+
);
161+
cargo.arg("--config").arg(config_value);
162+
} else {
163+
// Fallback to RUSTFLAGS for older cargo versions
164+
let rustflags = flags.join(" ");
165+
cargo.env("RUSTFLAGS", rustflags);
166+
}
167+
}
168+
}
169+
}
170+
171+
/// Check if cargo version supports --config flag (Cargo 1.63+)
172+
/// [https://doc.rust-lang.org/nightly/cargo/CHANGELOG.html#cargo-163-2022-08-11]
173+
fn cargo_supports_config_from_cli() -> bool {
174+
let output = Command::new("cargo").arg("--version").output();
175+
176+
if let std::result::Result::Ok(output) = output {
177+
if let std::result::Result::Ok(version_str) = String::from_utf8(output.stdout) {
178+
// Parse version string like "cargo 1.70.0 (7fe40dc9c 2023-04-27)"
179+
if let Some(version_part) = version_str.split_whitespace().nth(1) {
180+
if let std::result::Result::Ok(version) = Version::parse(version_part) {
181+
// Cargo 1.63.0 introduced --config support
182+
return version >= Version::new(1, 63, 0);
183+
}
184+
}
185+
}
130186
}
131-
cargo.env("RUSTFLAGS", rust_flags);
187+
188+
false
189+
}
190+
191+
/// Generates a subcommand to build the benchmarks by invoking cargo and forwarding the filters
192+
/// This command explicitly ignores the `self.benches`: all benches are built
193+
fn build_command(&self, measurement_mode: MeasurementMode) -> Command {
194+
let mut cargo = Command::new("cargo");
195+
cargo.args(["build", "--benches"]);
196+
197+
self.add_rust_flags(&mut cargo, measurement_mode);
132198

133199
if let Some(features) = self.features {
134200
cargo.arg("--features").arg(features.join(","));

0 commit comments

Comments
 (0)