diff --git a/Cargo.lock b/Cargo.lock index 51d2bba..ee87f46 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,7 +418,7 @@ checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2" [[package]] name = "depot" -version = "0.19.0" +version = "0.19.1" dependencies = [ "anyhow", "ar", diff --git a/Cargo.toml b/Cargo.toml index 8c876f8..193048b 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "depot" -version = "0.19.0" +version = "0.19.1" edition = "2024" [lints.rust] diff --git a/meson.build b/meson.build index 400c2c8..e9bba0c 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'depot', - version: '0.19.0', + version: '0.19.1', meson_version: '>=0.60.0', default_options: ['buildtype=release'], ) diff --git a/src/builder/autotools.rs b/src/builder/autotools.rs index 3cbb4c9..d331931 100755 --- a/src/builder/autotools.rs +++ b/src/builder/autotools.rs @@ -8,6 +8,7 @@ use anyhow::{Context, Result}; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; +use walkdir::WalkDir; pub fn build( spec: &PackageSpec, @@ -290,6 +291,24 @@ pub fn build( } ); + let install_destdir = install_destdir_path(&build_dir, destdir, flags.lib32_variant); + if flags.lib32_variant { + if install_destdir.exists() { + fs::remove_dir_all(&install_destdir).with_context(|| { + format!( + "Failed to clean temporary lib32 install dir: {}", + install_destdir.display() + ) + })?; + } + fs::create_dir_all(&install_destdir).with_context(|| { + format!( + "Failed to create temporary lib32 install dir: {}", + install_destdir.display() + ) + })?; + } + let install_dirs = resolve_make_dirs( &build_dir, &flags.make_install_dirs, @@ -301,12 +320,12 @@ pub fn build( Some("install"), ); for install_dir in install_dirs { - let mut install_cmd = fakeroot::wrap_install_command(make_exec, destdir); + let mut install_cmd = fakeroot::wrap_install_command(make_exec, &install_destdir); install_cmd.current_dir(&install_dir); if make_exec_supports_make_assignments(make_exec) && !has_make_variable_override(&flags.make_install_vars, "DESTDIR") { - install_cmd.arg(format!("DESTDIR={}", destdir.to_string_lossy())); + install_cmd.arg(format!("DESTDIR={}", install_destdir.to_string_lossy())); } add_make_variable_overrides_if_supported( &mut install_cmd, @@ -321,7 +340,7 @@ pub fn build( let mut install_env = env_vars.clone(); install_env.push(( "DESTDIR".to_string(), - destdir.to_string_lossy().into_owned(), + install_destdir.to_string_lossy().into_owned(), )); crate::builder::prepare_tool_command(&mut install_cmd, &install_env); @@ -346,8 +365,16 @@ pub fn build( } } - // Run post-install hooks (after make install) - hooks::run_post_install_commands(spec, &actual_src, destdir)?; + if flags.lib32_variant { + let staged_lib32 = install_destdir.join("usr/lib32"); + if !staged_lib32.exists() { + anyhow::bail!("lib32 install did not populate {}", staged_lib32.display()); + } + copy_tree_preserving_links(&staged_lib32, &destdir.join("usr/lib32"))?; + hooks::run_post_install_commands_in_dir(spec, &build_dir, destdir)?; + } else { + hooks::run_post_install_commands(spec, &actual_src, destdir)?; + } state.mark_done(BuildStep::PostInstallDone)?; } else { crate::log_info!("Skipping make install and post-install hooks (already done)"); @@ -468,6 +495,71 @@ fn default_configure_install_dirs( .collect() } +fn install_destdir_path(build_dir: &Path, destdir: &Path, lib32_variant: bool) -> PathBuf { + if lib32_variant { + build_dir.join("destdir") + } else { + destdir.to_path_buf() + } +} + +fn copy_tree_preserving_links(src: &Path, dst: &Path) -> Result<()> { + fs::create_dir_all(dst) + .with_context(|| format!("Failed to create destination dir: {}", dst.display()))?; + + for entry in WalkDir::new(src) { + let entry = entry?; + let rel = entry + .path() + .strip_prefix(src) + .with_context(|| format!("Failed to strip prefix: {}", src.display()))?; + let target = dst.join(rel); + + if entry.file_type().is_dir() { + fs::create_dir_all(&target) + .with_context(|| format!("Failed to create dir: {}", target.display()))?; + continue; + } + + if let Some(parent) = target.parent() { + fs::create_dir_all(parent) + .with_context(|| format!("Failed to create dir: {}", parent.display()))?; + } + + if entry.file_type().is_symlink() { + let link_target = fs::read_link(entry.path()) + .with_context(|| format!("Failed to read symlink: {}", entry.path().display()))?; + #[cfg(unix)] + { + use std::os::unix::fs as unix_fs; + unix_fs::symlink(&link_target, &target).with_context(|| { + format!( + "Failed to create symlink {} -> {}", + target.display(), + link_target.display() + ) + })?; + } + #[cfg(not(unix))] + { + anyhow::bail!( + "Symlink-preserving lib32 staging copy is only supported on unix hosts" + ); + } + } else { + fs::copy(entry.path(), &target).with_context(|| { + format!( + "Failed to copy {} to {}", + entry.path().display(), + target.display() + ) + })?; + } + } + + Ok(()) +} + fn lib32_host_triple(host: &str) -> String { host.replace("x86_64", "i686") } @@ -904,6 +996,20 @@ mod tests { assert!(args.is_empty()); } + #[test] + fn test_install_destdir_path_uses_build_dir_for_lib32() { + let build_dir = Path::new("/tmp/build"); + let destdir = Path::new("/tmp/pkg"); + assert_eq!( + install_destdir_path(build_dir, destdir, false), + destdir.to_path_buf() + ); + assert_eq!( + install_destdir_path(build_dir, destdir, true), + build_dir.join("destdir") + ); + } + #[test] fn test_makefile_content_has_target_detects_check_and_test() { let content = r#" diff --git a/src/commands.rs b/src/commands.rs index abdbb11..ce4a825 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -843,72 +843,13 @@ fn make_lib32_package_spec(base: &package::PackageSpec) -> package::PackageSpec let mut spec = base.clone(); let lib32_name = lib32_package_name(&base.package.name); spec.package.name = lib32_name.clone(); - // The lib32 pass currently emits a single companion package from /usr/lib32. + // The lib32 pass currently emits a single companion package. spec.packages.clear(); spec.alternatives = base.alternatives_for_output(&lib32_name); spec.dependencies = base.dependencies_for_output(&lib32_name); spec } -fn copy_tree_preserving_links(src: &Path, dst: &Path) -> Result<()> { - use walkdir::WalkDir; - - fs::create_dir_all(dst) - .with_context(|| format!("Failed to create destination dir: {}", dst.display()))?; - - for entry in WalkDir::new(src) { - let entry = entry?; - let rel = entry - .path() - .strip_prefix(src) - .with_context(|| format!("Failed to strip prefix: {}", src.display()))?; - let target = dst.join(rel); - - if entry.file_type().is_dir() { - fs::create_dir_all(&target) - .with_context(|| format!("Failed to create dir: {}", target.display()))?; - continue; - } - - if let Some(parent) = target.parent() { - fs::create_dir_all(parent) - .with_context(|| format!("Failed to create dir: {}", parent.display()))?; - } - - if entry.file_type().is_symlink() { - let link_target = fs::read_link(entry.path()) - .with_context(|| format!("Failed to read symlink: {}", entry.path().display()))?; - #[cfg(unix)] - { - use std::os::unix::fs as unix_fs; - unix_fs::symlink(&link_target, &target).with_context(|| { - format!( - "Failed to create symlink {} -> {}", - target.display(), - link_target.display() - ) - })?; - } - #[cfg(not(unix))] - { - anyhow::bail!( - "Symlink-preserving lib32 staging copy is only supported on unix hosts" - ); - } - } else { - fs::copy(entry.path(), &target).with_context(|| { - format!( - "Failed to copy {} to {}", - entry.path().display(), - target.display() - ) - })?; - } - } - - Ok(()) -} - fn build_lib32_companion_package( pkg_spec: &package::PackageSpec, src_dir: &Path, @@ -932,29 +873,6 @@ fn build_lib32_companion_package( let mut lib32_input = pkg_spec.clone(); lib32_input.build.flags.build_32 = true; let lib32_build_spec = make_lib32_build_spec(&lib32_input); - let lib32_install_destdir = config - .build_dir - .join("destdir") - .join(".lib32-build") - .join(&pkg_spec.package.name) - .join("lib32-dest"); - - builder::build( - &lib32_build_spec, - src_dir, - &lib32_install_destdir, - cross_config, - export_compiler_flags, - )?; - - let lib32_src = lib32_install_destdir.join("usr/lib32"); - if !lib32_src.exists() { - anyhow::bail!( - "lib32 build completed but did not install usr/lib32 into {}", - lib32_install_destdir.display() - ); - } - let lib32_pkg_spec = make_lib32_package_spec(pkg_spec); let lib32_destdir = config .build_dir @@ -972,7 +890,21 @@ fn build_lib32_companion_package( ) })?; - copy_tree_preserving_links(&lib32_src, &lib32_destdir.join("usr/lib32"))?; + builder::build( + &lib32_build_spec, + src_dir, + &lib32_destdir, + cross_config, + export_compiler_flags, + )?; + + let lib32_src = lib32_destdir.join("usr/lib32"); + if !lib32_src.exists() { + anyhow::bail!( + "lib32 build completed but did not install usr/lib32 into {}", + lib32_destdir.display() + ); + } staging::add_licenses(src_dir, &lib32_destdir, &lib32_pkg_spec.package.name)?; install::scripts::stage_scripts_from_spec_dir(&lib32_pkg_spec, &lib32_destdir)?; staging::process(&lib32_destdir, &lib32_pkg_spec)?; diff --git a/src/source/hooks.rs b/src/source/hooks.rs index 93aef43..a0f8028 100755 --- a/src/source/hooks.rs +++ b/src/source/hooks.rs @@ -201,8 +201,12 @@ pub fn run_post_compile_commands(spec: &PackageSpec, src_dir: &Path, destdir: &P Ok(()) } -/// Run post-install commands (after make install). -pub fn run_post_install_commands(spec: &PackageSpec, src_dir: &Path, destdir: &Path) -> Result<()> { +/// Run post-install commands (after make install) from the provided working directory. +pub fn run_post_install_commands_in_dir( + spec: &PackageSpec, + work_dir: &Path, + destdir: &Path, +) -> Result<()> { let commands = &spec.build.flags.post_install; if commands.is_empty() { return Ok(()); @@ -223,7 +227,7 @@ pub fn run_post_install_commands(spec: &PackageSpec, src_dir: &Path, destdir: &P let wrapped_cmd = crate::shell_helpers::wrap_shell_command(&cmd_str); let mut shell_cmd = Command::new("sh"); - shell_cmd.current_dir(src_dir); + shell_cmd.current_dir(work_dir); crate::builder::prepare_command(&mut shell_cmd, &env_vars); let status = shell_cmd .arg("-c") @@ -239,6 +243,11 @@ pub fn run_post_install_commands(spec: &PackageSpec, src_dir: &Path, destdir: &P Ok(()) } +/// Run post-install commands (after make install). +pub fn run_post_install_commands(spec: &PackageSpec, src_dir: &Path, destdir: &Path) -> Result<()> { + run_post_install_commands_in_dir(spec, src_dir, destdir) +} + fn resolve_patch_path(spec: &PackageSpec, patch: &str, patch_cache_dir: &Path) -> Result { if patch.starts_with("http://") || patch.starts_with("https://") { let filename = patch @@ -307,7 +316,10 @@ fn download(url: &str, dest: &Path) -> Result<()> { #[cfg(test)] mod tests { - use super::{resolve_patch_path, run_post_extract_commands, run_post_install_commands}; + use super::{ + resolve_patch_path, run_post_extract_commands, run_post_install_commands, + run_post_install_commands_in_dir, + }; use crate::package::{ Alternatives, Build, BuildFlags, BuildType, Dependencies, PackageInfo, PackageSpec, Source, }; @@ -403,6 +415,39 @@ mod tests { ); } + #[test] + fn post_install_commands_can_run_from_build_dir() { + let tmp = tempfile::tempdir().unwrap(); + let spec_dir = tmp.path().join("spec"); + let src_dir = tmp.path().join("src"); + let build_dir = tmp.path().join("build"); + let destdir = tmp.path().join("dest"); + std::fs::create_dir_all(&spec_dir).unwrap(); + std::fs::create_dir_all(&src_dir).unwrap(); + std::fs::create_dir_all(build_dir.join("destdir/usr/include/gnu")).unwrap(); + std::fs::create_dir_all(destdir.join("usr/include/gnu")).unwrap(); + std::fs::write( + build_dir.join("destdir/usr/include/gnu/lib-names-32.h"), + "lib32", + ) + .unwrap(); + + let mut spec = dummy_spec(&spec_dir); + spec.build.flags.post_install = vec![ + "install -m644 destdir/usr/include/gnu/lib-names-32.h \"$DESTDIR/usr/include/gnu/\"" + .into(), + ]; + + run_post_install_commands_in_dir(&spec, &build_dir, &destdir).unwrap(); + + assert!(destdir.join("usr/include/gnu/lib-names-32.h").exists()); + assert!( + !src_dir + .join("destdir/usr/include/gnu/lib-names-32.h") + .exists() + ); + } + #[test] fn post_extract_commands_can_call_python_build_helper() { let tmp = tempfile::tempdir().unwrap();