Implement interrupt handling for long-running commands and archive extraction
- Added an `interrupts` module to manage Ctrl-C signals and propagate them to child processes. - Refactored archive extraction functions to check for interrupts and handle them gracefully. - Introduced `copy_interruptibly` function to allow for interruptible copying of data. - Updated various source files to utilize the new interrupt handling functions, ensuring that commands and archive extractions can be interrupted by the user. - Added tests to verify the interrupt functionality during copying and tar extraction. - Introduced a new Meson option for generating HTML CLI documentation. - Enhanced the `PackageSpec` struct to support additional Rust LTO flags.
This commit is contained in:
+154
-8
@@ -29,9 +29,12 @@ pub fn build(
|
||||
// Environment variables
|
||||
let env_vars = crate::builder::standard_build_env(spec, cross, true, export_compiler_flags);
|
||||
|
||||
// Generate cross file if cross-compiling
|
||||
// Generate cross file if cross-compiling, or when the lib32 variant needs
|
||||
// Meson to treat the build as x86 instead of the native x86_64 host.
|
||||
let cross_file = if let Some(cc_cfg) = cross {
|
||||
Some(cc_cfg.generate_meson_cross_file(&build_dir)?)
|
||||
} else if flags.lib32_variant {
|
||||
Some(generate_lib32_meson_cross_file(flags, &build_dir)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -56,7 +59,8 @@ pub fn build(
|
||||
|
||||
crate::builder::prepare_tool_command(&mut meson_cmd, &env_vars);
|
||||
|
||||
let status = meson_cmd.status().context("Failed to run meson setup")?;
|
||||
let status = crate::interrupts::command_status(&mut meson_cmd)
|
||||
.context("Failed to run meson setup")?;
|
||||
if !status.success() {
|
||||
anyhow::bail!("meson setup failed");
|
||||
}
|
||||
@@ -76,8 +80,7 @@ pub fn build(
|
||||
|
||||
crate::builder::prepare_tool_command(&mut ninja_cmd, &env_vars);
|
||||
|
||||
let status = ninja_cmd
|
||||
.status()
|
||||
let status = crate::interrupts::command_status(&mut ninja_cmd)
|
||||
.with_context(|| format!("Failed to run ninja for {}", spec.package.name))?;
|
||||
if !status.success() {
|
||||
anyhow::bail!("ninja build failed");
|
||||
@@ -105,8 +108,7 @@ pub fn build(
|
||||
|
||||
crate::builder::prepare_tool_command(&mut test_cmd, &env_vars);
|
||||
|
||||
let status = test_cmd
|
||||
.status()
|
||||
let status = crate::interrupts::command_status(&mut test_cmd)
|
||||
.with_context(|| format!("Failed to run meson test for {}", spec.package.name))?;
|
||||
if !status.success() {
|
||||
anyhow::bail!("meson test failed");
|
||||
@@ -141,8 +143,7 @@ pub fn build(
|
||||
));
|
||||
crate::builder::prepare_tool_command(&mut install_cmd, &install_env);
|
||||
|
||||
let status = install_cmd
|
||||
.status()
|
||||
let status = crate::interrupts::command_status(&mut install_cmd)
|
||||
.with_context(|| format!("Failed to run meson install for {}", spec.package.name))?;
|
||||
if !status.success() {
|
||||
anyhow::bail!("meson install failed");
|
||||
@@ -258,6 +259,109 @@ fn meson_setup_args(
|
||||
args
|
||||
}
|
||||
|
||||
fn generate_lib32_meson_cross_file(
|
||||
flags: &crate::package::BuildFlags,
|
||||
build_dir: &Path,
|
||||
) -> Result<PathBuf> {
|
||||
let target = lib32_target_triple(flags);
|
||||
let arch = crate::cross::target_arch_from_triple(&target);
|
||||
let cpu_family = crate::cross::cpu_family_for_arch(arch);
|
||||
let c = meson_binary_value(
|
||||
&compiler_command_with_lib32_target(&flags.cc, &target),
|
||||
"C compiler",
|
||||
)?;
|
||||
let cpp = meson_binary_value(
|
||||
&compiler_command_with_lib32_target(&flags.cxx, &target),
|
||||
"C++ compiler",
|
||||
)?;
|
||||
let ar = meson_binary_value(&command_words(&flags.ar), "archiver")?;
|
||||
|
||||
let mut content = format!(
|
||||
"# Meson cross file for lib32 builds\n# Generated by depot for target: {target}\n\n[binaries]\nc = {c}\ncpp = {cpp}\nar = {ar}\n"
|
||||
);
|
||||
if !flags.ld.trim().is_empty() {
|
||||
let ld = meson_binary_value(&command_words(&flags.ld), "linker")?;
|
||||
content.push_str(&format!("ld = {ld}\n"));
|
||||
}
|
||||
content.push_str(&format!(
|
||||
"\n[host_machine]\nsystem = 'linux'\ncpu_family = '{cpu_family}'\ncpu = '{arch}'\nendian = 'little'\n"
|
||||
));
|
||||
|
||||
fs::create_dir_all(build_dir)?;
|
||||
let cross_path = build_dir.join("lib32-cross-file.ini");
|
||||
fs::write(&cross_path, content)
|
||||
.with_context(|| format!("Failed to write {}", cross_path.display()))?;
|
||||
Ok(cross_path)
|
||||
}
|
||||
|
||||
fn lib32_target_triple(flags: &crate::package::BuildFlags) -> String {
|
||||
let host = if !flags.chost.trim().is_empty() {
|
||||
flags.chost.trim().to_string()
|
||||
} else {
|
||||
match CrossConfig::build_triple() {
|
||||
Ok(triple) => triple,
|
||||
Err(err) => {
|
||||
crate::log_warn!(
|
||||
"Failed to detect native build triple for lib32 Meson target file: {}",
|
||||
err
|
||||
);
|
||||
"x86_64-unknown-linux-gnu".to_string()
|
||||
}
|
||||
}
|
||||
};
|
||||
crate::cross::lib32_target_triple(&host)
|
||||
}
|
||||
|
||||
fn compiler_command_with_lib32_target(command: &str, target: &str) -> Vec<String> {
|
||||
let mut parts = command_words(command);
|
||||
if compiler_command_supports_target(&parts) && !compiler_command_has_target(&parts) {
|
||||
parts.push(format!("--target={target}"));
|
||||
}
|
||||
parts
|
||||
}
|
||||
|
||||
fn command_words(command: &str) -> Vec<String> {
|
||||
command
|
||||
.split_whitespace()
|
||||
.filter(|part| !part.is_empty())
|
||||
.map(ToString::to_string)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn compiler_command_supports_target(parts: &[String]) -> bool {
|
||||
parts.first().is_some_and(|tool| {
|
||||
Path::new(tool)
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.is_some_and(|name| name.contains("clang"))
|
||||
})
|
||||
}
|
||||
|
||||
fn compiler_command_has_target(parts: &[String]) -> bool {
|
||||
parts.iter().any(|part| {
|
||||
part == "--target"
|
||||
|| part == "-target"
|
||||
|| part.starts_with("--target=")
|
||||
|| part.starts_with("-target=")
|
||||
})
|
||||
}
|
||||
|
||||
fn meson_binary_value(parts: &[String], label: &str) -> Result<String> {
|
||||
if parts.is_empty() {
|
||||
anyhow::bail!("Missing {} command for lib32 Meson cross file", label);
|
||||
}
|
||||
|
||||
let rendered = parts
|
||||
.iter()
|
||||
.map(|part| format!("'{}'", part.replace('\\', "\\\\").replace('\'', "\\'")))
|
||||
.collect::<Vec<_>>();
|
||||
if rendered.len() == 1 {
|
||||
Ok(rendered[0].clone())
|
||||
} else {
|
||||
Ok(format!("[{}]", rendered.join(", ")))
|
||||
}
|
||||
}
|
||||
|
||||
/// Expand environment variables in a string (e.g., $DEPOT_SYSROOT)
|
||||
fn expand_env_vars(input: &str) -> String {
|
||||
let mut result = input.to_string();
|
||||
@@ -439,6 +543,48 @@ mod tests {
|
||||
assert!(!args.iter().any(|a| a == "-Dcpp_ld=ld.lld"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compiler_command_with_lib32_target_adds_clang_target() {
|
||||
let parts = compiler_command_with_lib32_target("clang -m32", "i686-sfg-linux-gnu");
|
||||
assert_eq!(
|
||||
parts,
|
||||
vec![
|
||||
"clang".to_string(),
|
||||
"-m32".to_string(),
|
||||
"--target=i686-sfg-linux-gnu".to_string()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compiler_command_with_lib32_target_skips_non_clang_compilers() {
|
||||
let parts = compiler_command_with_lib32_target("gcc -m32", "i686-sfg-linux-gnu");
|
||||
assert_eq!(parts, vec!["gcc".to_string(), "-m32".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_lib32_meson_cross_file_sets_x86_host_machine() -> Result<()> {
|
||||
let tmp = tempdir()?;
|
||||
let flags = BuildFlags {
|
||||
lib32_variant: true,
|
||||
chost: "x86_64-sfg-linux-gnu".to_string(),
|
||||
cc: "clang -m32".to_string(),
|
||||
cxx: "clang++ -m32".to_string(),
|
||||
ar: "llvm-ar".to_string(),
|
||||
ld: "ld.lld".to_string(),
|
||||
..BuildFlags::default()
|
||||
};
|
||||
|
||||
let path = generate_lib32_meson_cross_file(&flags, tmp.path())?;
|
||||
let content = fs::read_to_string(path)?;
|
||||
assert!(content.contains("Generated by depot for target: i686-sfg-linux-gnu"));
|
||||
assert!(content.contains("c = ['clang', '-m32', '--target=i686-sfg-linux-gnu']"));
|
||||
assert!(content.contains("cpp = ['clang++', '-m32', '--target=i686-sfg-linux-gnu']"));
|
||||
assert!(content.contains("cpu_family = 'x86'"));
|
||||
assert!(content.contains("cpu = 'i686'"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_resolve_build_dir_uses_flag() {
|
||||
let flags = BuildFlags {
|
||||
|
||||
Reference in New Issue
Block a user