-
Notifications
You must be signed in to change notification settings - Fork 31
fix: support distributions CodSpeed publishes no valgrind package for (Arch, rolling releases) #525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
moha-bekh
merged 9 commits into
main
from
cod-3072-cli-fails-to-detect-os-version-on-arch-linux
Sep 17, 2026
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
9510b32
fix(system): don't fail when the distribution reports no OS version
moha-bekh c44a6db
fix(valgrind): accept a manual installation where we ship no package
moha-bekh ed3ed84
feat(valgrind): build valgrind-codspeed from source as a fallback
moha-bekh 803b667
refactor(valgrind): scope the libc debug symbol probe to the apt inst…
moha-bekh 372f15b
feat(valgrind): ask before building valgrind from source
moha-bekh efec650
refactor(system): model a missing OS version as an Option
moha-bekh 46de98d
refactor(valgrind): branch the setup on the executor support level
moha-bekh 43599d4
feat(setup): run the setup on hosts we publish no tooling for
moha-bekh 2097dec
chore(valgrind): clone valgrind-codspeed from its default branch
moha-bekh File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,220 @@ | ||
| //! Best-effort fallback that builds valgrind-codspeed from source, for the | ||
| //! systems we do not publish a package for (rolling releases, non-apt | ||
| //! distributions, ...). | ||
| //! | ||
| //! This is deliberately a "best effort": the toolchain needed to build valgrind | ||
| //! is not guaranteed to be present, so every failure is reported back to the | ||
| //! caller, which falls back to asking for a manual installation. | ||
| //! | ||
| //! The build is also opt-in rather than automatic, see [`is_wanted`]: it takes | ||
| //! minutes and installs system-wide, so an interactive user is asked first. | ||
|
|
||
| use crate::executor::helpers::command::CommandBuilder; | ||
| use crate::executor::helpers::run_command_with_log_pipe::run_command_with_log_pipe; | ||
| use crate::executor::helpers::run_with_sudo::wrap_with_sudo; | ||
| use crate::local_logger::rolling_buffer::{activate_rolling_buffer, deactivate_rolling_buffer}; | ||
| use crate::local_logger::{IS_TTY, suspend_progress_bar}; | ||
| use crate::prelude::*; | ||
| use console::Term; | ||
| use std::env; | ||
| use std::ffi::OsStr; | ||
| use std::path::Path; | ||
| use std::process::{Command, Stdio}; | ||
| use tempfile::TempDir; | ||
|
|
||
| const VALGRIND_CODSPEED_REPOSITORY: &str = "https://github.com/CodSpeedHQ/valgrind-codspeed.git"; | ||
|
|
||
| /// Environment variable that answers [`is_wanted`] without asking, for CI and any | ||
| /// other unattended run that wants the opposite of the default. | ||
| const BUILD_FROM_SOURCE_ENV: &str = "CODSPEED_VALGRIND_BUILD_FROM_SOURCE"; | ||
|
|
||
| /// Tools required to configure and build valgrind. Each entry lists the | ||
| /// interchangeable executables that satisfy the requirement. | ||
| const BUILD_DEPENDENCIES: &[&[&str]] = &[ | ||
| &["git"], | ||
| &["make"], | ||
| &["autoconf"], | ||
| &["automake"], | ||
| &["cc", "gcc", "clang"], | ||
| ]; | ||
|
|
||
| fn is_executable_available(executable: &str) -> bool { | ||
| Command::new("which") | ||
| .arg(executable) | ||
| .stdout(Stdio::null()) | ||
| .stderr(Stdio::null()) | ||
| .status() | ||
| .is_ok_and(|status| status.success()) | ||
| } | ||
|
|
||
| /// Names of the missing build dependencies, one per unsatisfied requirement. | ||
| fn missing_build_dependencies() -> Vec<&'static str> { | ||
| BUILD_DEPENDENCIES | ||
| .iter() | ||
| .filter(|alternatives| { | ||
| !alternatives | ||
| .iter() | ||
| .any(|executable| is_executable_available(executable)) | ||
| }) | ||
| .map(|alternatives| alternatives[0]) | ||
| .collect() | ||
| } | ||
|
|
||
| fn parallel_jobs() -> usize { | ||
| std::thread::available_parallelism() | ||
| .map(|jobs| jobs.get()) | ||
| .unwrap_or(1) | ||
| } | ||
|
|
||
| fn command_in<S: AsRef<OsStr>>(directory: &Path, program: S, args: &[&str]) -> CommandBuilder { | ||
| let mut builder = CommandBuilder::new(program); | ||
| builder.args(args); | ||
| builder.current_dir(directory); | ||
| builder | ||
| } | ||
|
|
||
| /// Run a build command, piping its output to the logs, and fail on a non-zero exit status. | ||
| async fn run_build_command(builder: CommandBuilder) -> Result<()> { | ||
| let command_line = builder.as_command_line(); | ||
| debug!("Running: {command_line}"); | ||
|
|
||
| let status = run_command_with_log_pipe(builder.build()) | ||
| .await | ||
| .with_context(|| format!("failed to run `{command_line}`"))?; | ||
|
|
||
| if !status.success() { | ||
| bail!("`{command_line}` failed with {status}"); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| /// Clone the sources into a temporary directory, wiped once the build is done. | ||
| async fn clone_sources() -> Result<TempDir> { | ||
| let source_dir = | ||
| TempDir::new().context("failed to create a temporary directory for the sources")?; | ||
|
|
||
| let source_dir_str = source_dir.path().to_string_lossy().into_owned(); | ||
| let mut builder = CommandBuilder::new("git"); | ||
| builder.args([ | ||
| "clone", | ||
| "--depth", | ||
| "1", | ||
| VALGRIND_CODSPEED_REPOSITORY, | ||
| &source_dir_str, | ||
| ]); | ||
| run_build_command(builder).await?; | ||
|
|
||
| Ok(source_dir) | ||
| } | ||
|
|
||
| /// Everything that runs unprivileged: fetching the sources and compiling them. | ||
| async fn fetch_and_compile() -> Result<TempDir> { | ||
| let source_dir = clone_sources().await?; | ||
| let path = source_dir.path(); | ||
|
|
||
| // The scripts are addressed by absolute path: how a relative program path is resolved against | ||
| // the working directory of the child is platform specific and unspecified. | ||
| run_build_command(command_in(path, path.join("autogen.sh"), &[])).await?; | ||
| run_build_command(command_in(path, path.join("configure"), &[])).await?; | ||
| run_build_command(command_in( | ||
| path, | ||
| "make", | ||
| &[&format!("-j{}", parallel_jobs())], | ||
| )) | ||
| .await?; | ||
|
|
||
| Ok(source_dir) | ||
| } | ||
|
|
||
| /// Install the freshly built valgrind system-wide. Kept out of the rolling | ||
| /// buffer so that a sudo password prompt stays visible to the user. | ||
| async fn install_build(source_dir: &Path) -> Result<()> { | ||
| let builder = wrap_with_sudo(command_in(source_dir, "make", &["install"]))?; | ||
| run_build_command(builder).await | ||
| } | ||
|
|
||
| /// Whether to build valgrind-codspeed from source, asking the user when we can. | ||
| /// | ||
| /// Decision, in order: | ||
| /// | ||
| /// - [`BUILD_FROM_SOURCE_ENV`] set to `true` or `false`: that answer, unconditionally; | ||
| /// - not a TTY (CI, unattended runs): build, since nobody is there to answer and | ||
| /// failing the run outright is the worse outcome; | ||
| /// - otherwise: ask, defaulting to building when the answer is empty. | ||
| /// | ||
| /// Declining is a legitimate choice, not a failure: the caller then points at a | ||
| /// manual installation, which is what happens on a failed build too. | ||
| pub(super) fn is_wanted() -> bool { | ||
| match env::var(BUILD_FROM_SOURCE_ENV).as_deref() { | ||
| Ok("true") => { | ||
| debug!("{BUILD_FROM_SOURCE_ENV} is true, building valgrind from source"); | ||
| return true; | ||
| } | ||
| Ok("false") => { | ||
| debug!("{BUILD_FROM_SOURCE_ENV} is false, not building valgrind from source"); | ||
| return false; | ||
| } | ||
| Ok(value) => warn!("Ignoring {BUILD_FROM_SOURCE_ENV}={value}, expected `true` or `false`"), | ||
| Err(_) => {} | ||
| } | ||
|
|
||
| if !*IS_TTY { | ||
| debug!("Not attached to a terminal, building valgrind from source without asking"); | ||
| return true; | ||
| } | ||
|
|
||
| suspend_progress_bar(prompt_for_source_build) | ||
| } | ||
|
|
||
| /// Ask whether to build valgrind from source, defaulting to yes on an empty answer. | ||
| /// | ||
| /// Mirrors the confirmation the walltime executor uses before installing bash: the | ||
| /// question goes to stderr so it stays visible whatever the caller does with stdout. | ||
| fn prompt_for_source_build() -> bool { | ||
| eprintln!( | ||
| "CodSpeed can build valgrind-codspeed from source for this system. It clones the sources \ | ||
| into a temporary directory, compiles them (a few minutes) and installs them system-wide \ | ||
| with sudo. Declining leaves the installation to you, see \ | ||
| https://github.com/CodSpeedHQ/valgrind-codspeed" | ||
| ); | ||
| eprint!("\nBuild valgrind-codspeed from source now? [Y/n] "); | ||
|
|
||
| let line = Term::stderr().read_line().unwrap_or_default(); | ||
| let answer = line.trim(); | ||
|
|
||
| // Default to yes on empty input (just pressing Enter), as the `[Y/n]` prompt announces. | ||
| let accepted = | ||
| answer.is_empty() || answer.eq_ignore_ascii_case("y") || answer.eq_ignore_ascii_case("yes"); | ||
| if !accepted { | ||
| info!( | ||
| "Skipping the source build. Set {BUILD_FROM_SOURCE_ENV}=true to build without being asked" | ||
| ); | ||
| } | ||
| accepted | ||
| } | ||
|
|
||
| /// Build and install valgrind-codspeed from source. | ||
| /// | ||
| /// Returns an error describing the first failing step, leaving the caller free | ||
| /// to fall back to instructions for a manual installation. | ||
| pub(super) async fn build_and_install() -> Result<()> { | ||
| let missing_dependencies = missing_build_dependencies(); | ||
| if !missing_dependencies.is_empty() { | ||
| bail!( | ||
| "the build toolchain is incomplete, install the missing tools: {}", | ||
| missing_dependencies.join(", ") | ||
| ); | ||
| } | ||
|
|
||
| info!("Building valgrind-codspeed from source, this can take a few minutes"); | ||
|
|
||
| activate_rolling_buffer("Building valgrind from source"); | ||
| let compilation_result = fetch_and_compile().await; | ||
| deactivate_rolling_buffer(); | ||
|
|
||
| let source_dir = compilation_result?; | ||
| install_build(source_dir.path()).await?; | ||
|
|
||
| Ok(()) | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| mod build_from_source; | ||
| pub mod executor; | ||
| pub mod helpers; | ||
| mod measure; | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.