Simplify Depot staging and preserve filesystem topology
Remove the old LBI bootstrap/system-state path and make Depot stage and install packages against standard FHS paths instead of rewriting payloads into the custom /system layout. This drops the bootstrap command surface, system-state storage, /system path normalization, and bootstrap chroot helper while moving rootfs state defaults back under /var and aliases under /usr/bin. Preserve hardlink topology throughout the package lifecycle. Add shared fs_copy helpers for symlink/hardlink-aware tree copies, use them for source preparation, binary packages, Rust installs, and lib32 staging, and keep hardlinks intact during stripping, atomic installation, and package archive creation. Replace the external fakeroot wrapper with an internal user-namespace based install command wrapper. Install phases now run as UID/GID 0 inside a private namespace while mapping ownership back to the invoking user on the host. Split the package spec parser into loading, model, config, and tests modules without changing the public PackageSpec surface. Keep strict unknown-key handling, manual source validation, config/appends support, lib32 overrides, and generated output helpers in the new layout. Tighten build and dependency behavior: - emit explicit shared-library selectors when static builds are disabled - apply configure_arch entries for the effective target/lib32 architecture - add CMake sysroot defaults for non-live DEPOT_ROOTFS builds - resolve Rust source_subdir before build hooks and binary installation - satisfy dependencies and local sibling plans through package real_name aliases - accept bare git:// source URLs as HEAD checkouts Update tests around FHS path expectations, namespace fakeroot installs, hardlink-preserving copies/archives/installs/stripping, real_name dependency resolution, configure_arch expansion, CMake sysroot defaults, Rust source subdirs, and git:// URL parsing. Bump suppaftp to 8.0.4 and lzma-rust2 to 0.16.4. Bump Depot to 0.50.0
This commit is contained in:
@@ -293,7 +293,7 @@ pub fn build(
|
||||
}
|
||||
|
||||
if !state.is_done(BuildStep::PostInstallDone) {
|
||||
// Run make install with fakeroot if not root
|
||||
// Run make install with internal fakeroot if not root
|
||||
crate::log_info!(
|
||||
"Running {} {}{}...",
|
||||
make_exec,
|
||||
|
||||
+1
-25
@@ -4,9 +4,7 @@ use crate::cross::CrossConfig;
|
||||
use crate::package::PackageSpec;
|
||||
use anyhow::{Context, Result};
|
||||
use std::fs;
|
||||
use std::os::unix::fs as unix_fs;
|
||||
use std::path::Path;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
/// For binary packages we simply copy the extracted files into DESTDIR (preserving
|
||||
/// directory structure). This is useful for .deb packages where extract step
|
||||
@@ -28,29 +26,7 @@ pub fn build(
|
||||
fs::create_dir_all(destdir)
|
||||
.with_context(|| format!("Failed to create destdir: {}", destdir.display()))?;
|
||||
|
||||
for entry in WalkDir::new(src_dir) {
|
||||
let entry = entry?;
|
||||
let rel = entry.path().strip_prefix(src_dir).unwrap();
|
||||
let target = destdir.join(rel);
|
||||
if entry.file_type().is_dir() {
|
||||
fs::create_dir_all(&target)?;
|
||||
} else if entry.file_type().is_symlink() {
|
||||
let link_target = fs::read_link(entry.path())?;
|
||||
if let Some(parent) = target.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
// overwrite existing links/files
|
||||
if target.exists() {
|
||||
let _ = fs::remove_file(&target);
|
||||
}
|
||||
unix_fs::symlink(link_target, &target)?;
|
||||
} else {
|
||||
if let Some(parent) = target.parent() {
|
||||
fs::create_dir_all(parent)?;
|
||||
}
|
||||
fs::copy(entry.path(), &target)?;
|
||||
}
|
||||
}
|
||||
crate::fs_copy::copy_tree_preserving_links(src_dir, destdir)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+87
-2
@@ -74,6 +74,9 @@ pub fn build(
|
||||
for arg in cmake_lib32_target_args(flags, cross) {
|
||||
cmake_cmd.arg(arg);
|
||||
}
|
||||
for arg in cmake_depot_sysroot_args(flags, depot_rootfs_from_env(&env_vars)) {
|
||||
cmake_cmd.arg(arg);
|
||||
}
|
||||
|
||||
// Add toolchain file for cross-compilation
|
||||
if let Some(ref tf) = toolchain_file {
|
||||
@@ -193,13 +196,13 @@ pub fn build(
|
||||
}
|
||||
|
||||
if !state.is_done(BuildStep::PostInstallDone) {
|
||||
// Run cmake install with fakeroot if not root
|
||||
// Run cmake install with internal fakeroot if not root
|
||||
crate::log_info!(
|
||||
"Running cmake install{}...",
|
||||
if fakeroot::is_root() {
|
||||
""
|
||||
} else {
|
||||
" (with fakeroot)"
|
||||
" (with internal fakeroot)"
|
||||
}
|
||||
);
|
||||
|
||||
@@ -402,6 +405,9 @@ pub(crate) fn run_helper_configure(
|
||||
for arg in cmake_lib32_target_args(&flags, cross) {
|
||||
cmake_cmd.arg(arg);
|
||||
}
|
||||
for arg in cmake_depot_sysroot_args(&flags, depot_rootfs_from_env(env_vars)) {
|
||||
cmake_cmd.arg(arg);
|
||||
}
|
||||
if let Some(toolchain_file) = &toolchain_file {
|
||||
cmake_cmd.arg(format!(
|
||||
"-DCMAKE_TOOLCHAIN_FILE={}",
|
||||
@@ -686,6 +692,34 @@ fn cmake_lib32_target_args(
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn cmake_depot_sysroot_args(flags: &crate::package::BuildFlags, depot_rootfs: &str) -> Vec<String> {
|
||||
let depot_rootfs = depot_rootfs.trim();
|
||||
if depot_rootfs.is_empty() || depot_rootfs == "/" {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let defaults = [
|
||||
("CMAKE_SYSROOT", depot_rootfs.to_string()),
|
||||
("CMAKE_FIND_ROOT_PATH_MODE_PROGRAM", "NEVER".to_string()),
|
||||
("CMAKE_FIND_ROOT_PATH_MODE_LIBRARY", "ONLY".to_string()),
|
||||
("CMAKE_FIND_ROOT_PATH_MODE_INCLUDE", "ONLY".to_string()),
|
||||
("CMAKE_FIND_ROOT_PATH_MODE_PACKAGE", "ONLY".to_string()),
|
||||
];
|
||||
|
||||
defaults
|
||||
.into_iter()
|
||||
.filter(|(variable, _)| cmake_cache_entry_value(&flags.configure, variable).is_none())
|
||||
.map(|(variable, value)| format!("-D{variable}={value}"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn depot_rootfs_from_env(env_vars: &[(String, String)]) -> &str {
|
||||
env_vars
|
||||
.iter()
|
||||
.find_map(|(key, value)| (key == "DEPOT_ROOTFS").then_some(value.as_str()))
|
||||
.unwrap_or("/")
|
||||
}
|
||||
|
||||
fn lib32_target_triple(
|
||||
flags: &crate::package::BuildFlags,
|
||||
cross: Option<&CrossConfig>,
|
||||
@@ -1030,6 +1064,57 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmake_depot_sysroot_args_skip_live_rootfs() {
|
||||
let args = cmake_depot_sysroot_args(&BuildFlags::default(), "/");
|
||||
assert!(args.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmake_depot_sysroot_args_include_non_live_rootfs_defaults() {
|
||||
let args = cmake_depot_sysroot_args(&BuildFlags::default(), "/tmp/depot-root");
|
||||
assert!(args.iter().any(|a| a == "-DCMAKE_SYSROOT=/tmp/depot-root"));
|
||||
assert!(
|
||||
args.iter()
|
||||
.any(|a| a == "-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER")
|
||||
);
|
||||
assert!(
|
||||
args.iter()
|
||||
.any(|a| a == "-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY")
|
||||
);
|
||||
assert!(
|
||||
args.iter()
|
||||
.any(|a| a == "-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY")
|
||||
);
|
||||
assert!(
|
||||
args.iter()
|
||||
.any(|a| a == "-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmake_depot_sysroot_args_respect_explicit_configure_overrides() {
|
||||
let flags = BuildFlags {
|
||||
configure: vec![
|
||||
"-DCMAKE_SYSROOT=/opt/custom-root".into(),
|
||||
"-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY:STRING=BOTH".into(),
|
||||
],
|
||||
..BuildFlags::default()
|
||||
};
|
||||
|
||||
let args = cmake_depot_sysroot_args(&flags, "/tmp/depot-root");
|
||||
assert!(!args.iter().any(|a| a.starts_with("-DCMAKE_SYSROOT=")));
|
||||
assert!(
|
||||
!args
|
||||
.iter()
|
||||
.any(|a| a.starts_with("-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY="))
|
||||
);
|
||||
assert!(
|
||||
args.iter()
|
||||
.any(|a| a == "-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmake_install_dir_args_respect_explicit_user_overrides() {
|
||||
let flags = BuildFlags {
|
||||
|
||||
+9
-44
@@ -134,7 +134,7 @@ pub fn build(
|
||||
let function_mode = custom_function_mode_enabled(&abs_build_script)?;
|
||||
if function_mode {
|
||||
crate::log_info!(
|
||||
"Running custom build script (function mode; fakeroot only during install)..."
|
||||
"Running custom build script (function mode; internal fakeroot only during install)..."
|
||||
);
|
||||
crate::log_info!(
|
||||
"Using custom build.sh function mode (per-output install functions enabled)"
|
||||
@@ -152,7 +152,7 @@ pub fn build(
|
||||
if fakeroot::is_root() {
|
||||
""
|
||||
} else {
|
||||
" (with fakeroot)"
|
||||
" (with internal fakeroot)"
|
||||
}
|
||||
);
|
||||
// Use POSIX `sh` (doing something wrong if your system doesn't have it...)
|
||||
@@ -580,15 +580,11 @@ depot_install_dev_pkg() {
|
||||
&build_sh,
|
||||
r#"#!/bin/sh
|
||||
depot_build() {
|
||||
if [ "${FAKEROOT_ACTIVE:-0}" = 1 ]; then
|
||||
echo yes > build-fakeroot.txt
|
||||
else
|
||||
echo no > build-fakeroot.txt
|
||||
fi
|
||||
id -u > build-uid.txt
|
||||
}
|
||||
depot_install() {
|
||||
mkdir -p "$DESTDIR/usr/share"
|
||||
echo installed > "$DESTDIR/usr/share/install-fakeroot.txt"
|
||||
id -u > "$DESTDIR/usr/share/install-uid.txt"
|
||||
}
|
||||
"#,
|
||||
)?;
|
||||
@@ -604,12 +600,12 @@ depot_install() {
|
||||
build(&spec, tmp_src.path(), tmp_dest.path(), None, true, None)?;
|
||||
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(tmp_src.path().join("build-fakeroot.txt"))?,
|
||||
"no\n"
|
||||
std::fs::read_to_string(tmp_src.path().join("build-uid.txt"))?,
|
||||
format!("{}\n", nix::unistd::geteuid().as_raw())
|
||||
);
|
||||
assert_eq!(
|
||||
std::fs::read_to_string(tmp_dest.path().join("usr/share/install-fakeroot.txt"))?,
|
||||
"installed\n"
|
||||
std::fs::read_to_string(tmp_dest.path().join("usr/share/install-uid.txt"))?,
|
||||
"0\n"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
@@ -626,12 +622,7 @@ depot_install() {
|
||||
let spec = mk_spec("custom-function-fakeroot-split", "1.0");
|
||||
let install_cmd =
|
||||
build_function_mode_install_command(&spec, install_destdir, build_script, true);
|
||||
let expected = if crate::fakeroot::is_root() {
|
||||
std::ffi::OsStr::new("sh")
|
||||
} else {
|
||||
std::ffi::OsStr::new("fakeroot")
|
||||
};
|
||||
assert_eq!(install_cmd.get_program(), expected);
|
||||
assert_eq!(install_cmd.get_program(), std::ffi::OsStr::new("sh"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -710,7 +701,6 @@ exit 0
|
||||
fn test_build_lib32_stages_only_usr_lib_payload() -> 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(
|
||||
@@ -729,31 +719,6 @@ printf 'manpage' > "$DESTDIR/usr/share/man/man1/foo.1"
|
||||
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;
|
||||
|
||||
|
||||
+29
-47
@@ -66,13 +66,13 @@ pub fn build(
|
||||
}
|
||||
|
||||
if !state.is_done(BuildStep::PostInstallDone) {
|
||||
// Run install commands with fakeroot
|
||||
// Run install commands with internal fakeroot
|
||||
crate::log_info!(
|
||||
"Running makefile install commands{}...",
|
||||
if crate::fakeroot::is_root() {
|
||||
""
|
||||
} else {
|
||||
" (with fakeroot)"
|
||||
" (with internal fakeroot)"
|
||||
}
|
||||
);
|
||||
|
||||
@@ -99,7 +99,7 @@ pub fn build(
|
||||
let cmd_str = spec.expand_vars(cmd_str);
|
||||
crate::log_info!(" Executing: {}", cmd_str);
|
||||
|
||||
// We need to run each command under fakeroot
|
||||
// We need to run each command under internal fakeroot
|
||||
let mut cmd = crate::fakeroot::wrap_install_command("sh", &install_destdir);
|
||||
cmd.arg("-c").arg(&cmd_str);
|
||||
cmd.current_dir(src_dir);
|
||||
@@ -136,21 +136,10 @@ pub fn build(
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::package::{Build, BuildFlags, BuildType, Dependencies, PackageInfo};
|
||||
use crate::test_support::TestEnv;
|
||||
use std::fs;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[cfg(unix)]
|
||||
fn write_executable(path: &std::path::Path, contents: &str) -> Result<()> {
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
|
||||
fs::write(path, contents)?;
|
||||
let mut perms = fs::metadata(path)?.permissions();
|
||||
perms.set_mode(0o755);
|
||||
fs::set_permissions(path, perms)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mk_spec(name: &str, version: &str) -> PackageSpec {
|
||||
PackageSpec {
|
||||
package: PackageInfo {
|
||||
@@ -189,24 +178,8 @@ mod tests {
|
||||
fn test_makefile_build_runs_commands() -> 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("test-make", "1.0");
|
||||
spec.build.flags.makefile_commands = vec![
|
||||
@@ -241,28 +214,37 @@ exec "$@"
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_makefile_install_preserves_staged_hardlinks() -> Result<()> {
|
||||
let tmp_src = tempdir()?;
|
||||
let tmp_dest = tempdir()?;
|
||||
let src_path = tmp_src.path();
|
||||
let dest_path = tmp_dest.path();
|
||||
|
||||
let mut spec = mk_spec("uutils-like", "1.0");
|
||||
spec.build.flags.makefile_install_commands = vec![
|
||||
"mkdir -p \"$DESTDIR/usr/bin\"".into(),
|
||||
"printf 'multicall' > \"$DESTDIR/usr/bin/uutils\"".into(),
|
||||
"ln \"$DESTDIR/usr/bin/uutils\" \"$DESTDIR/usr/bin/ls\"".into(),
|
||||
];
|
||||
|
||||
build(&spec, src_path, dest_path, None, true, None)?;
|
||||
|
||||
let uutils = dest_path.join("usr/bin/uutils").metadata()?;
|
||||
let ls = dest_path.join("usr/bin/ls").metadata()?;
|
||||
assert_eq!(uutils.ino(), ls.ino());
|
||||
assert_eq!(uutils.nlink(), 2);
|
||||
assert_eq!(ls.nlink(), 2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_makefile_lib32_install_relocates_usr_lib_without_copying_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;
|
||||
|
||||
@@ -141,13 +141,13 @@ pub fn build(
|
||||
}
|
||||
|
||||
if !state.is_done(BuildStep::PostInstallDone) {
|
||||
// Run meson install with fakeroot if not root
|
||||
// Run meson install with internal fakeroot if not root
|
||||
crate::log_info!(
|
||||
"Running meson install{}...",
|
||||
if fakeroot::is_root() {
|
||||
""
|
||||
} else {
|
||||
" (with fakeroot)"
|
||||
" (with internal fakeroot)"
|
||||
}
|
||||
);
|
||||
|
||||
@@ -750,11 +750,11 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_meson_setup_args_include_configure_flags() {
|
||||
let mut flags = BuildFlags {
|
||||
let flags = BuildFlags {
|
||||
prefix: "/usr".to_string(),
|
||||
configure: vec!["-Dmanpages=false".to_string()],
|
||||
..BuildFlags::default()
|
||||
};
|
||||
flags.configure = vec!["-Dmanpages=false".to_string()];
|
||||
|
||||
let args = meson_setup_args(&flags, None, &[]);
|
||||
assert!(args.iter().any(|a| a == "-Dmanpages=false"));
|
||||
@@ -764,8 +764,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_meson_setup_args_expand_host_build_dir() {
|
||||
let mut flags = BuildFlags::default();
|
||||
flags.configure = vec!["-Dtools_dir=$DEPOT_BUILD_HOST_DIR/bin".into()];
|
||||
let flags = BuildFlags {
|
||||
configure: vec!["-Dtools_dir=$DEPOT_BUILD_HOST_DIR/bin".into()],
|
||||
..BuildFlags::default()
|
||||
};
|
||||
|
||||
let args = meson_setup_args(
|
||||
&flags,
|
||||
|
||||
+186
-101
@@ -15,10 +15,8 @@ use crate::cross::CrossConfig;
|
||||
use crate::package::{BuildFlags, BuildType, PackageSpec};
|
||||
use anyhow::{Context, Result};
|
||||
use std::ffi::OsString;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub type EnvVars = Vec<(String, String)>;
|
||||
pub(crate) const DEPOT_BUILD_HOST_DIR_ENV: &str = "DEPOT_BUILD_HOST_DIR";
|
||||
@@ -292,6 +290,10 @@ fn normalized_arch(arch: &str) -> &str {
|
||||
}
|
||||
}
|
||||
|
||||
fn normalized_arch_key(arch: &str) -> String {
|
||||
normalized_arch(arch).to_ascii_lowercase().replace('-', "_")
|
||||
}
|
||||
|
||||
fn lib32_arch_for(arch: &str) -> String {
|
||||
match normalized_arch(arch) {
|
||||
"x86_64" => "i686".to_string(),
|
||||
@@ -374,9 +376,48 @@ pub(crate) fn host_build_spec(spec: &PackageSpec) -> PackageSpec {
|
||||
host_spec.build.flags.carch = host_arch().to_string();
|
||||
host_spec.build.flags.host_build_dir = None;
|
||||
host_spec.build.flags.build_dir = Some(default_host_build_dir_name(&spec.build.flags));
|
||||
append_configure_for_arch(&mut host_spec.build.flags, host_arch());
|
||||
host_spec
|
||||
}
|
||||
|
||||
fn append_configure_for_target_arch(
|
||||
flags: &mut crate::package::BuildFlags,
|
||||
cross: Option<&CrossConfig>,
|
||||
kind: TargetBuildKind,
|
||||
) {
|
||||
let arch = effective_target_arch(flags, cross, kind);
|
||||
append_configure_for_arch(flags, &arch);
|
||||
}
|
||||
|
||||
fn append_configure_for_arch(flags: &mut crate::package::BuildFlags, arch: &str) {
|
||||
if flags.configure_arch.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let target_arch = normalized_arch_key(arch);
|
||||
let matching_args: Vec<String> = flags
|
||||
.configure_arch
|
||||
.iter()
|
||||
.filter(|(key, _)| normalized_arch_key(key) == target_arch)
|
||||
.flat_map(|(_, values)| values.iter().cloned())
|
||||
.collect();
|
||||
flags.configure.extend(matching_args);
|
||||
}
|
||||
|
||||
fn spec_with_target_configure(
|
||||
spec: &PackageSpec,
|
||||
cross: Option<&CrossConfig>,
|
||||
kind: TargetBuildKind,
|
||||
) -> Option<PackageSpec> {
|
||||
if spec.build.flags.configure_arch.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut spec = spec.clone();
|
||||
append_configure_for_target_arch(&mut spec.build.flags, cross, kind);
|
||||
Some(spec)
|
||||
}
|
||||
|
||||
pub(crate) fn requested_static_build() -> Result<Option<bool>> {
|
||||
crate::build_options::requested_static_build()
|
||||
}
|
||||
@@ -395,11 +436,16 @@ fn static_build_args_for_request(
|
||||
}
|
||||
|
||||
match build_type {
|
||||
BuildType::Autotools => vec![if enabled {
|
||||
"--enable-static".to_string()
|
||||
} else {
|
||||
"--disable-static".to_string()
|
||||
}],
|
||||
BuildType::Autotools => {
|
||||
if enabled {
|
||||
vec!["--enable-static".to_string()]
|
||||
} else {
|
||||
vec![
|
||||
"--enable-shared".to_string(),
|
||||
"--disable-static".to_string(),
|
||||
]
|
||||
}
|
||||
}
|
||||
BuildType::CMake => vec![format!(
|
||||
"-DBUILD_SHARED_LIBS={}",
|
||||
if enabled { "OFF" } else { "ON" }
|
||||
@@ -586,64 +632,10 @@ pub(crate) fn install_destdir_path(
|
||||
|
||||
pub(crate) fn stage_lib32_install_tree(staging_destdir: &Path, destdir: &Path) -> Result<()> {
|
||||
let lib_rel = lib32_stage_source_rel(staging_destdir)?;
|
||||
copy_tree_preserving_links(&staging_destdir.join(&lib_rel), &destdir.join("usr/lib32"))
|
||||
}
|
||||
|
||||
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(())
|
||||
crate::fs_copy::copy_tree_preserving_links(
|
||||
&staging_destdir.join(&lib_rel),
|
||||
&destdir.join("usr/lib32"),
|
||||
)
|
||||
}
|
||||
fn lib32_stage_source_rel(staging_destdir: &Path) -> Result<PathBuf> {
|
||||
let staged_lib32 = PathBuf::from("usr/lib32");
|
||||
@@ -1028,6 +1020,14 @@ pub fn build(
|
||||
export_compiler_flags: bool,
|
||||
host_build_dir: Option<&Path>,
|
||||
) -> Result<()> {
|
||||
let target_kind = if spec.build.flags.lib32_variant {
|
||||
TargetBuildKind::Lib32
|
||||
} else {
|
||||
TargetBuildKind::Primary
|
||||
};
|
||||
let target_configured_spec = spec_with_target_configure(spec, cross, target_kind);
|
||||
let spec = target_configured_spec.as_ref().unwrap_or(spec);
|
||||
|
||||
if let Some(cc) = cross {
|
||||
crate::log_info!(
|
||||
"Cross-compiling for {} with {:?}...",
|
||||
@@ -1131,6 +1131,7 @@ mod tests {
|
||||
use crate::test_support::TestEnv;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn mk_spec(cflags: Vec<&str>, ldflags: Vec<&str>) -> PackageSpec {
|
||||
@@ -1284,7 +1285,22 @@ mod tests {
|
||||
fn test_static_build_args_keep_other_requested_modes() {
|
||||
assert_eq!(
|
||||
static_build_args_for_request(BuildType::Autotools, Some(false), false),
|
||||
vec!["--disable-static".to_string()]
|
||||
vec![
|
||||
"--enable-shared".to_string(),
|
||||
"--disable-static".to_string()
|
||||
]
|
||||
);
|
||||
assert_eq!(
|
||||
static_build_args_for_request(BuildType::CMake, Some(false), false),
|
||||
vec!["-DBUILD_SHARED_LIBS=ON".to_string()]
|
||||
);
|
||||
assert_eq!(
|
||||
static_build_args_for_request(BuildType::Meson, Some(false), false),
|
||||
vec!["-Ddefault_library=shared".to_string()]
|
||||
);
|
||||
assert_eq!(
|
||||
static_build_args_for_request(BuildType::Perl, Some(false), false),
|
||||
vec!["LINKTYPE=dynamic".to_string()]
|
||||
);
|
||||
assert_eq!(
|
||||
static_build_args_for_request(BuildType::Meson, Some(true), true),
|
||||
@@ -1463,6 +1479,60 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_spec_with_target_configure_appends_matching_arch_args() {
|
||||
let mut spec = mk_spec(Vec::new(), Vec::new());
|
||||
spec.build.flags.configure = vec!["--base".to_string()];
|
||||
spec.build
|
||||
.flags
|
||||
.configure_arch
|
||||
.insert("aarch64".to_string(), vec!["--for-aarch64".to_string()]);
|
||||
spec.build
|
||||
.flags
|
||||
.configure_arch
|
||||
.insert("x86_64".to_string(), vec!["--for-x86".to_string()]);
|
||||
let cross = CrossConfig {
|
||||
prefix: "aarch64-linux-gnu".into(),
|
||||
cc: "aarch64-linux-gnu-gcc".into(),
|
||||
cxx: "aarch64-linux-gnu-g++".into(),
|
||||
ar: "aarch64-linux-gnu-ar".into(),
|
||||
ranlib: "aarch64-linux-gnu-ranlib".into(),
|
||||
strip: "aarch64-linux-gnu-strip".into(),
|
||||
ld: "aarch64-linux-gnu-ld".into(),
|
||||
nm: "aarch64-linux-gnu-nm".into(),
|
||||
objcopy: "aarch64-linux-gnu-objcopy".into(),
|
||||
objdump: "aarch64-linux-gnu-objdump".into(),
|
||||
readelf: "aarch64-linux-gnu-readelf".into(),
|
||||
};
|
||||
|
||||
let adjusted = spec_with_target_configure(&spec, Some(&cross), TargetBuildKind::Primary)
|
||||
.expect("expected arch-specific configure args");
|
||||
|
||||
assert_eq!(
|
||||
adjusted.build.flags.configure,
|
||||
vec!["--base".to_string(), "--for-aarch64".to_string()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_spec_with_target_configure_uses_lib32_arch() {
|
||||
let mut spec = mk_spec(Vec::new(), Vec::new());
|
||||
spec.build.flags.lib32_variant = true;
|
||||
spec.build.flags.carch = "x86_64".to_string();
|
||||
spec.build
|
||||
.flags
|
||||
.configure_arch
|
||||
.insert("i686".to_string(), vec!["--for-lib32".to_string()]);
|
||||
|
||||
let adjusted = spec_with_target_configure(&spec, None, TargetBuildKind::Lib32)
|
||||
.expect("expected lib32 configure args");
|
||||
|
||||
assert_eq!(
|
||||
adjusted.build.flags.configure,
|
||||
vec!["--for-lib32".to_string()]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_standard_build_env_respects_export_compiler_flags_toggle() {
|
||||
let mut spec = mk_spec(vec!["-O2"], vec!["-Wl,--as-needed"]);
|
||||
@@ -1686,44 +1756,35 @@ mod tests {
|
||||
#[test]
|
||||
fn test_standard_build_env_exports_install_dir_vars() {
|
||||
let mut spec = mk_spec(Vec::new(), Vec::new());
|
||||
spec.build.flags.prefix = "/system".into();
|
||||
spec.build.flags.bindir = "/system/binaries".into();
|
||||
spec.build.flags.sbindir = "/system/systembinaries".into();
|
||||
spec.build.flags.libdir = "/system/libraries".into();
|
||||
spec.build.flags.libexecdir = "/system/libexec".into();
|
||||
spec.build.flags.sysconfdir = "/system/configuration".into();
|
||||
spec.build.flags.localstatedir = "/system/variable".into();
|
||||
spec.build.flags.sharedstatedir = "/system/variable/lib".into();
|
||||
spec.build.flags.includedir = "/system/headers".into();
|
||||
spec.build.flags.datarootdir = "/system/share".into();
|
||||
spec.build.flags.datadir = "/system/data".into();
|
||||
spec.build.flags.mandir = "/system/documentation/man-pages".into();
|
||||
spec.build.flags.infodir = "/system/documentation/info".into();
|
||||
spec.build.flags.prefix = "/opt/vertex".into();
|
||||
spec.build.flags.bindir = "/opt/vertex/bin".into();
|
||||
spec.build.flags.sbindir = "/opt/vertex/sbin".into();
|
||||
spec.build.flags.libdir = "/opt/vertex/lib".into();
|
||||
spec.build.flags.libexecdir = "/opt/vertex/libexec".into();
|
||||
spec.build.flags.sysconfdir = "/etc/vertex".into();
|
||||
spec.build.flags.localstatedir = "/var".into();
|
||||
spec.build.flags.sharedstatedir = "/var/lib".into();
|
||||
spec.build.flags.includedir = "/opt/vertex/include".into();
|
||||
spec.build.flags.datarootdir = "/opt/vertex/share".into();
|
||||
spec.build.flags.datadir = "/opt/vertex/share/data".into();
|
||||
spec.build.flags.mandir = "/opt/vertex/share/man".into();
|
||||
spec.build.flags.infodir = "/opt/vertex/share/info".into();
|
||||
|
||||
let env = standard_build_env(&spec, None, false, true);
|
||||
|
||||
assert_eq!(env_value(&env, "PREFIX"), Some("/system"));
|
||||
assert_eq!(env_value(&env, "BINDIR"), Some("/system/binaries"));
|
||||
assert_eq!(env_value(&env, "SBINDIR"), Some("/system/systembinaries"));
|
||||
assert_eq!(env_value(&env, "LIBDIR"), Some("/system/libraries"));
|
||||
assert_eq!(env_value(&env, "LIBEXECDIR"), Some("/system/libexec"));
|
||||
assert_eq!(env_value(&env, "SYSCONFDIR"), Some("/system/configuration"));
|
||||
assert_eq!(env_value(&env, "LOCALSTATEDIR"), Some("/system/variable"));
|
||||
assert_eq!(
|
||||
env_value(&env, "SHAREDSTATEDIR"),
|
||||
Some("/system/variable/lib")
|
||||
);
|
||||
assert_eq!(env_value(&env, "INCLUDEDIR"), Some("/system/headers"));
|
||||
assert_eq!(env_value(&env, "DATAROOTDIR"), Some("/system/share"));
|
||||
assert_eq!(env_value(&env, "DATADIR"), Some("/system/data"));
|
||||
assert_eq!(
|
||||
env_value(&env, "MANDIR"),
|
||||
Some("/system/documentation/man-pages")
|
||||
);
|
||||
assert_eq!(
|
||||
env_value(&env, "INFODIR"),
|
||||
Some("/system/documentation/info")
|
||||
);
|
||||
assert_eq!(env_value(&env, "PREFIX"), Some("/opt/vertex"));
|
||||
assert_eq!(env_value(&env, "BINDIR"), Some("/opt/vertex/bin"));
|
||||
assert_eq!(env_value(&env, "SBINDIR"), Some("/opt/vertex/sbin"));
|
||||
assert_eq!(env_value(&env, "LIBDIR"), Some("/opt/vertex/lib"));
|
||||
assert_eq!(env_value(&env, "LIBEXECDIR"), Some("/opt/vertex/libexec"));
|
||||
assert_eq!(env_value(&env, "SYSCONFDIR"), Some("/etc/vertex"));
|
||||
assert_eq!(env_value(&env, "LOCALSTATEDIR"), Some("/var"));
|
||||
assert_eq!(env_value(&env, "SHAREDSTATEDIR"), Some("/var/lib"));
|
||||
assert_eq!(env_value(&env, "INCLUDEDIR"), Some("/opt/vertex/include"));
|
||||
assert_eq!(env_value(&env, "DATAROOTDIR"), Some("/opt/vertex/share"));
|
||||
assert_eq!(env_value(&env, "DATADIR"), Some("/opt/vertex/share/data"));
|
||||
assert_eq!(env_value(&env, "MANDIR"), Some("/opt/vertex/share/man"));
|
||||
assert_eq!(env_value(&env, "INFODIR"), Some("/opt/vertex/share/info"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1952,4 +2013,28 @@ mod tests {
|
||||
assert!(!dest.join("usr/lib").exists());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stage_lib32_install_tree_preserves_hardlinks() -> Result<()> {
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
|
||||
let temp = tempfile::tempdir()?;
|
||||
let staging = temp.path().join("staging");
|
||||
let dest = temp.path().join("dest");
|
||||
fs::create_dir_all(staging.join("usr/lib32"))?;
|
||||
fs::write(staging.join("usr/lib32/libfoo.so.1"), "lib32")?;
|
||||
fs::hard_link(
|
||||
staging.join("usr/lib32/libfoo.so.1"),
|
||||
staging.join("usr/lib32/libfoo-current.so"),
|
||||
)?;
|
||||
|
||||
stage_lib32_install_tree(&staging, &dest)?;
|
||||
|
||||
let first = dest.join("usr/lib32/libfoo.so.1").metadata()?;
|
||||
let second = dest.join("usr/lib32/libfoo-current.so").metadata()?;
|
||||
assert_eq!(first.ino(), second.ino());
|
||||
assert_eq!(first.nlink(), 2);
|
||||
assert_eq!(second.nlink(), 2);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -598,16 +598,6 @@ case "$target" in
|
||||
esac
|
||||
"#,
|
||||
)?;
|
||||
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));
|
||||
|
||||
+111
-12
@@ -3,9 +3,9 @@
|
||||
use crate::cross::CrossConfig;
|
||||
use crate::package::PackageSpec;
|
||||
use crate::source::hooks;
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{Context, Result, bail};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
pub fn build(
|
||||
@@ -17,13 +17,14 @@ pub fn build(
|
||||
_host_build_dir: Option<&Path>,
|
||||
) -> Result<()> {
|
||||
let flags = &spec.build.flags;
|
||||
let actual_src = resolve_actual_src(spec, src_dir)?;
|
||||
|
||||
// Create destdir
|
||||
fs::create_dir_all(destdir)?;
|
||||
|
||||
// Isolate from parent workspace by adding empty [workspace] if not present
|
||||
// This prevents "believes it's in a workspace when it's not" errors
|
||||
let cargo_toml = src_dir.join("Cargo.toml");
|
||||
let cargo_toml = actual_src.join("Cargo.toml");
|
||||
if cargo_toml.exists() {
|
||||
let contents = fs::read_to_string(&cargo_toml)
|
||||
.with_context(|| format!("Failed to read {}", cargo_toml.display()))?;
|
||||
@@ -75,7 +76,7 @@ pub fn build(
|
||||
crate::builder::set_env_var(&mut env_vars, "RUSTUP_TOOLCHAIN", "stable");
|
||||
}
|
||||
|
||||
hooks::run_post_configure_commands(spec, src_dir, destdir)?;
|
||||
hooks::run_post_configure_commands(spec, &actual_src, destdir)?;
|
||||
|
||||
// Run cargo build
|
||||
crate::log_info!(
|
||||
@@ -83,7 +84,7 @@ pub fn build(
|
||||
if is_release { "release" } else { "debug" }
|
||||
);
|
||||
let mut cargo_cmd = Command::new("cargo");
|
||||
cargo_cmd.current_dir(src_dir);
|
||||
cargo_cmd.current_dir(&actual_src);
|
||||
cargo_cmd.arg("build");
|
||||
|
||||
if is_release {
|
||||
@@ -110,16 +111,16 @@ pub fn build(
|
||||
}
|
||||
|
||||
// Run post-compile hooks
|
||||
hooks::run_post_compile_commands(spec, src_dir, destdir)?;
|
||||
hooks::run_post_compile_commands(spec, &actual_src, destdir)?;
|
||||
|
||||
// Install binaries to destdir
|
||||
crate::log_info!("Installing binaries to DESTDIR...");
|
||||
|
||||
// Determine target directory
|
||||
let target_dir = if let Some(ref t) = target {
|
||||
src_dir.join("target").join(t).join(profile_dir)
|
||||
actual_src.join("target").join(t).join(profile_dir)
|
||||
} else {
|
||||
src_dir.join("target").join(profile_dir)
|
||||
actual_src.join("target").join(profile_dir)
|
||||
};
|
||||
|
||||
// Use bindir from flags (default: /usr/bin)
|
||||
@@ -127,6 +128,7 @@ pub fn build(
|
||||
fs::create_dir_all(&bin_dir)?;
|
||||
|
||||
// Find and copy executable files
|
||||
let mut hardlink_tracker = crate::fs_copy::HardlinkCopyTracker::new();
|
||||
if target_dir.exists() {
|
||||
for entry in fs::read_dir(&target_dir)
|
||||
.with_context(|| format!("Failed to read target directory: {}", target_dir.display()))?
|
||||
@@ -164,9 +166,7 @@ pub fn build(
|
||||
{
|
||||
let dest = bin_dir.join(&*file_name);
|
||||
crate::log_info!(" Installing: {}", file_name);
|
||||
fs::copy(&path, &dest).with_context(|| {
|
||||
format!("Failed to copy {} to {}", path.display(), dest.display())
|
||||
})?;
|
||||
hardlink_tracker.copy_file(&path, &dest)?;
|
||||
|
||||
// Preserve executable permission
|
||||
let mut perms = fs::metadata(&dest)?.permissions();
|
||||
@@ -178,7 +178,106 @@ pub fn build(
|
||||
}
|
||||
|
||||
// Run post-install hooks
|
||||
hooks::run_post_install_commands(spec, src_dir, destdir)?;
|
||||
hooks::run_post_install_commands(spec, &actual_src, destdir)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_actual_src(spec: &PackageSpec, src_dir: &Path) -> Result<PathBuf> {
|
||||
let source_subdir = spec.expand_vars(&spec.build.flags.source_subdir);
|
||||
if source_subdir.is_empty() {
|
||||
return Ok(src_dir.to_path_buf());
|
||||
}
|
||||
|
||||
let candidate = Path::new(&source_subdir);
|
||||
if candidate.is_absolute() {
|
||||
if candidate.exists() {
|
||||
return Ok(candidate.to_path_buf());
|
||||
}
|
||||
bail!(
|
||||
"Source directory not found: {} (source_subdir: {} -> {})",
|
||||
candidate.display(),
|
||||
spec.build.flags.source_subdir,
|
||||
source_subdir
|
||||
);
|
||||
}
|
||||
|
||||
let under_src = src_dir.join(&source_subdir);
|
||||
if under_src.exists() {
|
||||
return Ok(under_src);
|
||||
}
|
||||
|
||||
let under_spec = spec.spec_dir.join(&source_subdir);
|
||||
if under_spec.exists() {
|
||||
return Ok(under_spec);
|
||||
}
|
||||
|
||||
if candidate.exists() {
|
||||
return Ok(candidate.to_path_buf());
|
||||
}
|
||||
|
||||
bail!(
|
||||
"Source directory not found: {} (expanded from '{}'; tried src_dir, spec_dir, and absolute path)",
|
||||
source_subdir,
|
||||
spec.build.flags.source_subdir
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::package::{Build, BuildFlags, BuildType, PackageInfo};
|
||||
use tempfile::tempdir;
|
||||
|
||||
fn test_spec(spec_dir: PathBuf, source_subdir: &str) -> PackageSpec {
|
||||
PackageSpec {
|
||||
package: PackageInfo {
|
||||
name: "red".into(),
|
||||
real_name: None,
|
||||
version: "1.0.2".into(),
|
||||
revision: 1,
|
||||
description: String::new(),
|
||||
homepage: String::new(),
|
||||
abi_breaking: false,
|
||||
license: vec!["MIT".into()],
|
||||
},
|
||||
packages: Vec::new(),
|
||||
alternatives: Default::default(),
|
||||
manual_sources: Vec::new(),
|
||||
source: Vec::new(),
|
||||
build: Build {
|
||||
build_type: BuildType::Rust,
|
||||
flags: BuildFlags {
|
||||
source_subdir: source_subdir.into(),
|
||||
..BuildFlags::default()
|
||||
},
|
||||
},
|
||||
dependencies: Default::default(),
|
||||
package_alternatives: Default::default(),
|
||||
package_dependencies: Default::default(),
|
||||
spec_dir,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_actual_src_supports_source_subdir() {
|
||||
let tmp = tempdir().unwrap();
|
||||
let src_dir = tmp.path().join("source");
|
||||
let nested = src_dir.join("red-1.0.2");
|
||||
fs::create_dir_all(&nested).unwrap();
|
||||
|
||||
let spec = test_spec(tmp.path().join("spec"), "$name-$version");
|
||||
assert_eq!(resolve_actual_src(&spec, &src_dir).unwrap(), nested);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_actual_src_rejects_missing_source_subdir() {
|
||||
let tmp = tempdir().unwrap();
|
||||
let src_dir = tmp.path().join("source");
|
||||
fs::create_dir_all(&src_dir).unwrap();
|
||||
|
||||
let spec = test_spec(tmp.path().join("spec"), "missing");
|
||||
let error = resolve_actual_src(&spec, &src_dir).unwrap_err();
|
||||
assert!(error.to_string().contains("Source directory not found"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user