-
Notifications
You must be signed in to change notification settings - Fork 29
fix: support distributions that report no OS version (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
Open
moha-bekh
wants to merge
3
commits into
main
Choose a base branch
from
cod-3072-cli-fails-to-detect-os-version-on-arch-linux
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| //! 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. | ||
|
|
||
| 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::prelude::*; | ||
| use crate::system::{SupportedOs, SystemInfo}; | ||
| use std::ffi::OsStr; | ||
| use std::path::{Path, PathBuf}; | ||
| use std::process::{Command, Stdio}; | ||
| use std::{env, fs}; | ||
|
|
||
| const VALGRIND_CODSPEED_REPOSITORY: &str = "https://github.com/CodSpeedHQ/valgrind-codspeed.git"; | ||
|
|
||
| /// Branch of the valgrind-codspeed repository to build from. | ||
| // TODO: switch back to `main` once the self-contained build script has landed there. | ||
| const VALGRIND_CODSPEED_BRANCH: &str = "cod-3465-create-a-self-contained-valgrind-build-script"; | ||
|
|
||
| /// Directory name, under the system temporary directory, the sources are cloned into. | ||
| const SOURCE_DIR_NAME: &str = "valgrind-codspeed-src"; | ||
|
|
||
| /// 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() | ||
| } | ||
|
|
||
| /// Command that installs the build toolchain, for the distributions we can name it for. | ||
| /// | ||
| /// Valgrind itself needs no development library: it links `-nodefaultlibs` and vendors its only | ||
| /// third-party decoder, so an autotools and C toolchain is the whole requirement. | ||
| fn build_toolchain_install_hint(system_info: &SystemInfo) -> Option<&'static str> { | ||
| let SupportedOs::Linux(distro) = &system_info.os else { | ||
| return None; | ||
| }; | ||
|
|
||
| // `id` is the `ID` field of /etc/os-release, so derivatives report their own id. | ||
| let hint = match distro.id() { | ||
| "arch" | "archarm" | "manjaro" | "endeavouros" | "cachyos" => { | ||
| "sudo pacman -S --needed base-devel git" | ||
| } | ||
| "fedora" | "rhel" | "centos" | "rocky" | "almalinux" => { | ||
| "sudo dnf install -y @development-tools git" | ||
| } | ||
| "opensuse-tumbleweed" | "opensuse-leap" | "sles" => { | ||
| "sudo zypper install -y -t pattern devel_basis git" | ||
| } | ||
| "alpine" => "sudo apk add build-base autoconf automake git", | ||
| "ubuntu" | "debian" | "linuxmint" | "pop" | "raspbian" => { | ||
| "sudo apt-get install -y build-essential autoconf automake git" | ||
| } | ||
| _ => return None, | ||
| }; | ||
| Some(hint) | ||
| } | ||
|
|
||
| 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 from scratch, so that a partial or outdated checkout left | ||
| /// over by a previous attempt never leaks into the build. | ||
| async fn clone_sources() -> Result<PathBuf> { | ||
| let source_dir = env::temp_dir().join(SOURCE_DIR_NAME); | ||
| if source_dir.exists() { | ||
| debug!("Removing the previous checkout at {}", source_dir.display()); | ||
| fs::remove_dir_all(&source_dir).with_context(|| { | ||
| format!( | ||
| "failed to remove the previous checkout at {}", | ||
| source_dir.display() | ||
| ) | ||
| })?; | ||
| } | ||
|
|
||
| let source_dir_str = source_dir.to_string_lossy().into_owned(); | ||
| let mut builder = CommandBuilder::new("git"); | ||
| builder.args([ | ||
| "clone", | ||
| "--depth", | ||
| "1", | ||
| "--branch", | ||
| VALGRIND_CODSPEED_BRANCH, | ||
| 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<PathBuf> { | ||
| let source_dir = clone_sources().await?; | ||
|
|
||
| // 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(&source_dir, source_dir.join("autogen.sh"), &[])).await?; | ||
| run_build_command(command_in(&source_dir, source_dir.join("configure"), &[])).await?; | ||
| run_build_command(command_in( | ||
| &source_dir, | ||
| "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 | ||
| } | ||
|
|
||
| /// 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(system_info: &SystemInfo) -> Result<()> { | ||
| let missing_dependencies = missing_build_dependencies(); | ||
| if !missing_dependencies.is_empty() { | ||
| let missing = missing_dependencies.join(", "); | ||
| match build_toolchain_install_hint(system_info) { | ||
| Some(hint) => bail!( | ||
| "the build toolchain is incomplete ({missing} missing), install it with `{hint}`" | ||
| ), | ||
| None => bail!("the build toolchain is incomplete ({missing} missing)"), | ||
| } | ||
| } | ||
|
|
||
| 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).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
| 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; | ||
|
|
||
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
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On unsupported Ubuntu or Debian versions,
is_valgrind_installedalso requires resolvable libc debug symbols, but this error only instructs users to installvalgrind-codspeed. A user who follows that instruction can receive the same error again without learning that the debug-symbol package is the remaining requirement.Knowledge Base Used: Valgrind measurement
Prompt To Fix With AI
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!