From 99ceacdb64d08b3844d3716d593ba01c1a76270f Mon Sep 17 00:00:00 2001 From: SFG545 Date: Sun, 15 Mar 2026 22:55:17 -0500 Subject: [PATCH] feat: update depot version to 0.25.1 and enhance lib32 installation handling --- Cargo.lock | 2 +- Cargo.toml | 2 +- meson.build | 2 +- src/builder/autotools.rs | 64 +---------------- src/builder/custom.rs | 114 +++++++++++++++++++++++++++---- src/builder/makefile.rs | 76 ++++++++++++++++++++- src/builder/mod.rs | 144 +++++++++++++++++++++++++++++++++------ 7 files changed, 302 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab5eb07..5853fb1 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -418,7 +418,7 @@ checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2" [[package]] name = "depot" -version = "0.25.0" +version = "0.25.1" dependencies = [ "anyhow", "ar", diff --git a/Cargo.toml b/Cargo.toml index e3be11a..62ee7ad 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "depot" -version = "0.25.0" +version = "0.25.1" edition = "2024" [lints.rust] diff --git a/meson.build b/meson.build index 8b128b4..9138387 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project( 'depot', - version: '0.25.0', + version: '0.25.1', meson_version: '>=0.60.0', default_options: ['buildtype=release'], ) diff --git a/src/builder/autotools.rs b/src/builder/autotools.rs index 4c1f780..81fa4a4 100755 --- a/src/builder/autotools.rs +++ b/src/builder/autotools.rs @@ -8,7 +8,6 @@ use anyhow::{Context, Result}; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; -use walkdir::WalkDir; pub fn build( spec: &PackageSpec, @@ -373,11 +372,7 @@ pub fn build( } 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"))?; + crate::builder::stage_lib32_install_tree(&install_destdir, destdir)?; hooks::run_post_install_commands_in_dir(spec, &build_dir, destdir)?; } else { hooks::run_post_install_commands(spec, &actual_src, destdir)?; @@ -510,63 +505,6 @@ fn install_destdir_path(build_dir: &Path, destdir: &Path, lib32_variant: bool) - } } -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") } diff --git a/src/builder/custom.rs b/src/builder/custom.rs index 1e6d0c7..10faafe 100755 --- a/src/builder/custom.rs +++ b/src/builder/custom.rs @@ -17,12 +17,37 @@ pub fn build( export_compiler_flags: bool, ) -> Result<()> { let flags = &spec.build.flags; + let build_dir = if let Some(dir) = &flags.build_dir { + let bdir = src_dir.join(dir); + fs::create_dir_all(&bdir)?; + bdir + } else { + src_dir.to_path_buf() + }; + let install_destdir = + crate::builder::install_destdir_path(&build_dir, destdir, flags.lib32_variant); // Create destdir fs::create_dir_all(destdir)?; + 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 mut env_vars = crate::builder::standard_build_env(spec, cross, true, export_compiler_flags); - let shell_helpers = crate::shell_helpers::ShellHelpers::new(destdir)?; + let shell_helpers = crate::shell_helpers::ShellHelpers::new(&install_destdir)?; shell_helpers.apply_to_env_vars(&mut env_vars); // For custom builds, look for a build.sh script in the source directory @@ -81,25 +106,17 @@ pub fn build( } ); - let build_dir = if let Some(dir) = &flags.build_dir { - let bdir = src_dir.join(dir); - fs::create_dir_all(&bdir)?; - bdir - } else { - src_dir.to_path_buf() - }; - crate::builder::set_env_var( &mut env_vars, "DESTDIR", - destdir.to_string_lossy().into_owned(), + install_destdir.to_string_lossy().into_owned(), ); crate::builder::set_env_var( &mut env_vars, "DEPOT_PRIMARY_DESTDIR", - destdir.to_string_lossy().into_owned(), + install_destdir.to_string_lossy().into_owned(), ); - add_output_destdir_envs(spec, destdir, &mut env_vars); + add_output_destdir_envs(spec, &install_destdir, &mut env_vars); // Ensure build script path is absolute for when we are in a sub-build-dir let abs_build_script = if build_script.is_absolute() { @@ -113,9 +130,9 @@ pub fn build( crate::log_info!( "Using custom build.sh function mode (per-output install functions enabled)" ); - build_function_mode_command(spec, destdir, &abs_build_script)? + build_function_mode_command(spec, &install_destdir, &abs_build_script)? } else { - let mut cmd = fakeroot::wrap_install_command("sh", destdir); + let mut cmd = fakeroot::wrap_install_command("sh", &install_destdir); let wrapper = crate::shell_helpers::wrap_shell_command(". \"$1\""); // Run custom scripts through `sh -c` so helper commands like `haul` // work even when the helper scripts live on a `noexec` mount. @@ -142,6 +159,9 @@ pub fn build( if !status.success() { anyhow::bail!("Custom build script failed with status: {}", status); } + if flags.lib32_variant { + crate::builder::stage_lib32_install_tree(&install_destdir, destdir)?; + } state.mark_done(BuildStep::PostInstallDone)?; } else { crate::log_info!("Skipping custom build script (already done)"); @@ -467,4 +487,70 @@ exit 0 assert!(err.to_string().contains("Custom build script failed")); Ok(()) } + + #[test] + fn test_build_lib32_stages_usr_lib_and_keeps_non_library_paths() -> Result<()> { + let tmp_src = tempdir()?; + let tmp_dest = tempdir()?; + let tmp_tools = tempdir()?; + + let build_sh = tmp_src.path().join("build.sh"); + std::fs::write( + &build_sh, + r#"#!/bin/sh +mkdir -p "$DESTDIR/usr/lib" "$DESTDIR/usr/share/man/man1" +printf 'lib32' > "$DESTDIR/usr/lib/libfoo.so.1" +printf 'manpage' > "$DESTDIR/usr/share/man/man1/foo.1" +"#, + )?; + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let mut perms = std::fs::metadata(&build_sh)?.permissions(); + perms.set_mode(0o755); + std::fs::set_permissions(&build_sh, perms)?; + } + + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; + let fakeroot = tmp_tools.path().join("fakeroot"); + std::fs::write( + &fakeroot, + r#"#!/bin/sh +if [ "$1" = "--" ]; then + shift +fi +exec "$@" +"#, + )?; + let mut perms = std::fs::metadata(&fakeroot)?.permissions(); + perms.set_mode(0o755); + std::fs::set_permissions(&fakeroot, perms)?; + } + + let mut env = crate::test_support::TestEnv::new(); + let old_path = std::env::var("PATH").unwrap_or_default(); + env.set_var( + "PATH", + format!("{}:{}", tmp_tools.path().display(), old_path), + ); + + let mut spec = mk_spec("custom-lib32", "1.0"); + spec.build.flags.lib32_variant = true; + + build(&spec, tmp_src.path(), tmp_dest.path(), None, true)?; + + assert_eq!( + std::fs::read_to_string(tmp_dest.path().join("usr/lib32/libfoo.so.1"))?, + "lib32" + ); + assert_eq!( + std::fs::read_to_string(tmp_dest.path().join("usr/share/man/man1/foo.1"))?, + "manpage" + ); + assert!(!tmp_dest.path().join("usr/lib").exists()); + + Ok(()) + } } diff --git a/src/builder/makefile.rs b/src/builder/makefile.rs index 6e54e84..fca0735 100644 --- a/src/builder/makefile.rs +++ b/src/builder/makefile.rs @@ -2,6 +2,7 @@ use crate::builder::state::{BuildStep, StateTracker}; use crate::cross::CrossConfig; use crate::package::PackageSpec; use anyhow::{Context, Result}; +use std::fs; use std::path::Path; use std::process::Command; @@ -17,6 +18,8 @@ pub fn build( spec.build.flags.lib32_variant.then_some("lib32"), )?; let flags = &spec.build.flags; + fs::create_dir_all(destdir) + .with_context(|| format!("Failed to create DESTDIR: {}", destdir.display()))?; let mut env_vars = crate::builder::standard_build_env(spec, cross, true, export_compiler_flags); @@ -72,19 +75,38 @@ pub fn build( } ); + let install_destdir = + crate::builder::install_destdir_path(src_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() + ) + })?; + } + for cmd_str in &spec.build.flags.makefile_install_commands { let cmd_str = spec.expand_vars(cmd_str); crate::log_info!(" Executing: {}", cmd_str); // We need to run each command under fakeroot - let mut cmd = crate::fakeroot::wrap_install_command("sh", destdir); + let mut cmd = crate::fakeroot::wrap_install_command("sh", &install_destdir); cmd.arg("-c").arg(&cmd_str); cmd.current_dir(src_dir); 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 cmd, &install_env); @@ -95,7 +117,12 @@ pub fn build( } } - crate::source::hooks::run_post_install_commands(spec, src_dir, destdir)?; + if flags.lib32_variant { + crate::builder::stage_lib32_install_tree(&install_destdir, destdir)?; + crate::source::hooks::run_post_install_commands_in_dir(spec, src_dir, destdir)?; + } else { + crate::source::hooks::run_post_install_commands(spec, src_dir, destdir)?; + } state.mark_done(BuildStep::PostInstallDone)?; } else { crate::log_info!("Skipping makefile install commands (already done)"); @@ -212,4 +239,47 @@ exec "$@" Ok(()) } + + #[test] + fn test_makefile_lib32_install_relocates_usr_lib_and_keeps_other_paths() -> Result<()> { + let tmp_src = tempdir()?; + let tmp_dest = tempdir()?; + let tmp_tools = tempdir()?; + let src_path = tmp_src.path(); + let dest_path = tmp_dest.path(); + let tools_path = tmp_tools.path(); + + write_executable( + &tools_path.join("fakeroot"), + r#"#!/bin/sh +if [ "$1" = "--" ]; then + shift +fi +exec "$@" +"#, + )?; + + let mut env = TestEnv::new(); + let old_path = std::env::var("PATH").unwrap_or_default(); + env.set_var("PATH", format!("{}:{}", tools_path.display(), old_path)); + + let mut spec = mk_spec("lib32-test-make", "1.0"); + spec.build.flags.lib32_variant = true; + spec.build.flags.makefile_install_commands = vec![ + "mkdir -p \"$DESTDIR/usr/lib\" \"$DESTDIR/usr/bin\"".into(), + "printf 'lib32' > \"$DESTDIR/usr/lib/libfoo.so.1\"".into(), + "printf 'bin' > \"$DESTDIR/usr/bin/foo\"".into(), + ]; + + build(&spec, src_path, dest_path, None, true)?; + + assert_eq!( + fs::read_to_string(dest_path.join("usr/lib32/libfoo.so.1"))?, + "lib32" + ); + assert_eq!(fs::read_to_string(dest_path.join("usr/bin/foo"))?, "bin"); + assert!(!dest_path.join("usr/lib").exists()); + + Ok(()) + } } diff --git a/src/builder/mod.rs b/src/builder/mod.rs index 502bb5b..e1a3a04 100755 --- a/src/builder/mod.rs +++ b/src/builder/mod.rs @@ -180,25 +180,9 @@ pub(crate) fn install_destdir_path( } pub(crate) fn stage_lib32_install_tree(staging_destdir: &Path, destdir: &Path) -> Result<()> { - let staged_lib32 = staging_destdir.join("usr/lib32"); - if staged_lib32.exists() { - return copy_tree_preserving_links(&staged_lib32, &destdir.join("usr/lib32")); - } - - let staged_lib = staging_destdir.join("usr/lib"); - if staged_lib.exists() { - crate::log_warn!( - "lib32 install populated {} instead of usr/lib32; relocating staged libraries", - staged_lib.display() - ); - return copy_tree_preserving_links(&staged_lib, &destdir.join("usr/lib32")); - } - - anyhow::bail!( - "lib32 install did not populate {} or {}", - staged_lib32.display(), - staged_lib.display() - ); + let lib_rel = lib32_stage_source_rel(staging_destdir)?; + copy_install_tree_preserving_links(staging_destdir, destdir, Some(&lib_rel))?; + copy_tree_preserving_links(&staging_destdir.join(&lib_rel), &destdir.join("usr/lib32")) } fn copy_tree_preserving_links(src: &Path, dst: &Path) -> Result<()> { @@ -258,6 +242,105 @@ fn copy_tree_preserving_links(src: &Path, dst: &Path) -> Result<()> { Ok(()) } +fn copy_install_tree_preserving_links( + src: &Path, + dst: &Path, + relocated_lib_rel: Option<&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()))?; + if should_skip_staged_install_entry(rel, relocated_lib_rel) { + continue; + } + + 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 should_skip_staged_install_entry(rel: &Path, relocated_lib_rel: Option<&Path>) -> bool { + if rel.as_os_str().is_empty() { + return false; + } + + if rel.starts_with(Path::new(crate::shell_helpers::INTERNAL_DEPOT_DIR)) { + return true; + } + + relocated_lib_rel.is_some_and(|lib_rel| rel.starts_with(lib_rel)) +} + +fn lib32_stage_source_rel(staging_destdir: &Path) -> Result { + let staged_lib32 = PathBuf::from("usr/lib32"); + if staging_destdir.join(&staged_lib32).exists() { + return Ok(staged_lib32); + } + + let staged_lib = PathBuf::from("usr/lib"); + if staging_destdir.join(&staged_lib).exists() { + crate::log_warn!( + "lib32 install populated {} instead of usr/lib32; relocating staged libraries", + staging_destdir.join(&staged_lib).display() + ); + return Ok(staged_lib); + } + + anyhow::bail!( + "lib32 install did not populate {} or {}", + staging_destdir.join("usr/lib32").display(), + staging_destdir.join("usr/lib").display() + ); +} + fn compiler_flag_sets( flags: &crate::package::BuildFlags, ) -> (Vec, Vec, Vec, Vec) { @@ -978,7 +1061,16 @@ mod tests { let staging = temp.path().join("staging"); let dest = temp.path().join("dest"); fs::create_dir_all(staging.join("usr/lib32"))?; + fs::create_dir_all(staging.join("usr/bin"))?; fs::write(staging.join("usr/lib32/libfoo.so.1"), "lib32")?; + fs::write(staging.join("usr/bin/foo"), "bin")?; + fs::create_dir_all(staging.join(crate::shell_helpers::INTERNAL_DEPOT_DIR))?; + fs::write( + staging + .join(crate::shell_helpers::INTERNAL_DEPOT_DIR) + .join("internal.txt"), + "skip", + )?; stage_lib32_install_tree(&staging, &dest)?; @@ -986,6 +1078,13 @@ mod tests { fs::read_to_string(dest.join("usr/lib32/libfoo.so.1"))?, "lib32" ); + assert_eq!(fs::read_to_string(dest.join("usr/bin/foo"))?, "bin"); + assert!( + !dest + .join(crate::shell_helpers::INTERNAL_DEPOT_DIR) + .join("internal.txt") + .exists() + ); Ok(()) } @@ -998,7 +1097,9 @@ mod tests { let staging = temp.path().join("staging"); let dest = temp.path().join("dest"); fs::create_dir_all(staging.join("usr/lib"))?; + fs::create_dir_all(staging.join("usr/share/man/man1"))?; fs::write(staging.join("usr/lib/libfoo.so.1"), "relocated")?; + fs::write(staging.join("usr/share/man/man1/foo.1"), "manpage")?; unix_fs::symlink("libfoo.so.1", staging.join("usr/lib/libfoo.so"))?; stage_lib32_install_tree(&staging, &dest)?; @@ -1011,6 +1112,11 @@ mod tests { fs::read_link(dest.join("usr/lib32/libfoo.so"))?, PathBuf::from("libfoo.so.1") ); + assert_eq!( + fs::read_to_string(dest.join("usr/share/man/man1/foo.1"))?, + "manpage" + ); + assert!(!dest.join("usr/lib").exists()); Ok(()) } }