feat: update depot version to 0.17.1 and enhance command execution with improved error handling and environment variable management

This commit is contained in:
2026-03-12 00:48:30 -05:00
parent f72b6c46de
commit d434e79a76
6 changed files with 393 additions and 73 deletions
Generated
+1 -1
View File
@@ -418,7 +418,7 @@ checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2"
[[package]] [[package]]
name = "depot" name = "depot"
version = "0.17.0" version = "0.17.1"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"ar", "ar",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "depot" name = "depot"
version = "0.17.0" version = "0.17.1"
edition = "2024" edition = "2024"
[lints.rust] [lints.rust]
+1 -1
View File
@@ -1,6 +1,6 @@
project( project(
'depot', 'depot',
version: '0.17.0', version: '0.17.1',
meson_version: '>=0.60.0', meson_version: '>=0.60.0',
default_options: ['buildtype=release'], default_options: ['buildtype=release'],
) )
+298 -30
View File
@@ -165,7 +165,10 @@ fn command_status_with_sh_fallback(
) -> std::io::Result<std::process::ExitStatus> { ) -> std::io::Result<std::process::ExitStatus> {
match cmd.status() { match cmd.status() {
Ok(status) => Ok(status), Ok(status) => Ok(status),
Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => { Err(err)
if err.kind() == std::io::ErrorKind::PermissionDenied
|| err.raw_os_error() == Some(26) =>
{
let program = cmd.get_program(); let program = cmd.get_program();
let contents = fs::read(program); let contents = fs::read(program);
let is_script = contents.ok().is_some_and(|bytes| bytes.starts_with(b"#!")); let is_script = contents.ok().is_some_and(|bytes| bytes.starts_with(b"#!"));
@@ -261,7 +264,43 @@ fn parse_keep_list(metadata: &toml::Value) -> Vec<String> {
} }
fn current_process_env_vars() -> Vec<(String, String)> { fn current_process_env_vars() -> Vec<(String, String)> {
std::env::vars().collect() const ALLOWED_ENV_VARS: &[&str] = &[
"AR",
"CARCH",
"CBUILD",
"CC",
"CHOST",
"CPP",
"CROSS_COMPILE",
"CROSS_PREFIX",
"CFLAGS",
"CXX",
"CXXFLAGS",
"DEPOT_ROOTFS",
"DEPOT_SPECDIR",
"DESTDIR",
"LD",
"LDFLAGS",
"LTOFLAGS",
"MAKEFLAGS",
"NM",
"PREFIX",
"PYTHONDONTWRITEBYTECODE",
"PYTHONNOUSERSITE",
"RANLIB",
"RUSTFLAGS",
"SETUPTOOLS_USE_DISTUTILS",
"STRIP",
];
ALLOWED_ENV_VARS
.iter()
.filter_map(|key| {
std::env::var(key)
.ok()
.map(|value| ((*key).to_string(), value))
})
.collect()
} }
fn run_internal_command(command: InternalCommands) -> Result<()> { fn run_internal_command(command: InternalCommands) -> Result<()> {
@@ -594,6 +633,28 @@ fn maybe_disable_tests_for_missing_deps(
Ok(()) Ok(())
} }
fn maybe_prompt_to_skip_tests_for_missing_requested_deps(
pkg_spec: &mut package::PackageSpec,
missing_test: &[String],
reason: &str,
) -> Result<bool> {
if pkg_spec.build.flags.skip_tests
|| !build_type_runs_automatic_tests(pkg_spec)
|| missing_test.is_empty()
{
return Ok(false);
}
ui::warn(format!("{reason}: {}", missing_test.join(", ")));
if ui::prompt_yes_no("Continue without tests?", false)? {
pkg_spec.build.flags.skip_tests = true;
ui::warn("Tests will be skipped for this build.");
return Ok(true);
}
Ok(false)
}
fn should_install_test_deps(pkg_spec: &package::PackageSpec, install_test_deps: bool) -> bool { fn should_install_test_deps(pkg_spec: &package::PackageSpec, install_test_deps: bool) -> bool {
install_test_deps && !pkg_spec.build.flags.skip_tests && !pkg_spec.dependencies.test.is_empty() install_test_deps && !pkg_spec.build.flags.skip_tests && !pkg_spec.dependencies.test.is_empty()
} }
@@ -3380,27 +3441,33 @@ fn run_direct_install_request(
})?; })?;
let db_path = config.installed_db_path(options.rootfs); let db_path = config.installed_db_path(options.rootfs);
if staging_dir.is_none() if staging_dir.is_none() {
&& (options.no_deps || !should_install_test_deps(&pkg_spec, options.install_test_deps)) if options.no_deps && should_install_test_deps(&pkg_spec, options.install_test_deps) {
{ let missing_test = deps::check_test_deps(&pkg_spec, &db_path)?;
maybe_disable_tests_for_missing_deps(&mut pkg_spec, &db_path)?; if !missing_test.is_empty()
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
&mut pkg_spec,
&missing_test,
"Requested test dependencies are missing",
)?
{
anyhow::bail!("Missing test dependencies: {}", missing_test.join(", "));
}
} else if options.no_deps || !should_install_test_deps(&pkg_spec, options.install_test_deps)
{
maybe_disable_tests_for_missing_deps(&mut pkg_spec, &db_path)?;
}
} }
// Check dependencies and prompt for auto-install if needed // Check dependencies and prompt for auto-install if needed
if !options.no_deps { if !options.no_deps {
deps::print_dep_status(&pkg_spec, &db_path)?; deps::print_dep_status(&pkg_spec, &db_path)?;
// Collect all missing dependencies (build + runtime) let missing_required = merge_missing_dependencies(
let missing = merge_missing_dependencies(
deps::check_build_deps(&pkg_spec, &db_path)?, deps::check_build_deps(&pkg_spec, &db_path)?,
deps::check_runtime_deps(&pkg_spec, &db_path)?, deps::check_runtime_deps(&pkg_spec, &db_path)?,
); );
let missing = if should_install_test_deps(&pkg_spec, options.install_test_deps) { if !missing_required.is_empty() {
merge_missing_dependencies(missing, deps::check_test_deps(&pkg_spec, &db_path)?)
} else {
missing
};
if !missing.is_empty() {
// Check for dependency cycles via DEPOT_DEPCHAIN env var // Check for dependency cycles via DEPOT_DEPCHAIN env var
let dep_chain = std::env::var("DEPOT_DEPCHAIN").unwrap_or_default(); let dep_chain = std::env::var("DEPOT_DEPCHAIN").unwrap_or_default();
let chain_set: std::collections::HashSet<&str> = let chain_set: std::collections::HashSet<&str> =
@@ -3414,8 +3481,11 @@ fn run_direct_install_request(
); );
} }
ui::warn(format!("Missing dependencies: {}", missing.join(", "))); ui::warn(format!(
if ui::prompt_package_action("dependency installation", &missing, true)? { "Missing dependencies: {}",
missing_required.join(", ")
));
if ui::prompt_package_action("dependency installation", &missing_required, true)? {
// Build package index for fast lookups // Build package index for fast lookups
let pkg_index = let pkg_index =
index::PackageIndex::build_with_repo_dir(Some(config.repo_clone_dir.clone())); index::PackageIndex::build_with_repo_dir(Some(config.repo_clone_dir.clone()));
@@ -3428,7 +3498,7 @@ fn run_direct_install_request(
}; };
let mut dep_spec_paths = Vec::new(); let mut dep_spec_paths = Vec::new();
for dep in missing { for dep in missing_required {
// Use package index for O(1) lookup // Use package index for O(1) lookup
let candidate = pkg_index.find(&dep); let candidate = pkg_index.find(&dep);
@@ -3465,7 +3535,79 @@ fn run_direct_install_request(
deps::require_build_deps(&pkg_spec, &db_path)?; deps::require_build_deps(&pkg_spec, &db_path)?;
deps::require_runtime_deps(&pkg_spec, &db_path)?; deps::require_runtime_deps(&pkg_spec, &db_path)?;
if should_install_test_deps(&pkg_spec, options.install_test_deps) { if should_install_test_deps(&pkg_spec, options.install_test_deps) {
deps::require_test_deps(&pkg_spec, &db_path)?; let missing_test = deps::check_test_deps(&pkg_spec, &db_path)?;
if !missing_test.is_empty() {
let pkg_index =
index::PackageIndex::build_with_repo_dir(Some(config.repo_clone_dir.clone()));
let mut dep_spec_paths = Vec::new();
let mut unavailable_test = Vec::new();
for dep in &missing_test {
if let Some(dep_spec_path) = pkg_index.find(dep) {
dep_spec_paths.push(dep_spec_path);
} else {
unavailable_test.push(dep.clone());
}
}
if !unavailable_test.is_empty()
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
&mut pkg_spec,
&unavailable_test,
"Requested test dependencies could not be resolved",
)?
{
anyhow::bail!("Missing test dependencies: {}", unavailable_test.join(", "));
}
if !pkg_spec.build.flags.skip_tests && !dep_spec_paths.is_empty() {
ui::warn(format!(
"Missing test dependencies: {}",
missing_test.join(", ")
));
if ui::prompt_package_action("dependency installation", &missing_test, true)? {
ui::info(format!(
"Installing test dependencies: {}",
install_request_display(&dep_spec_paths)
));
let exe =
std::env::current_exe().context("Failed to locate depot executable")?;
run_install_command_with_program(
&exe,
&dep_spec_paths,
options.rootfs,
ChildInstallCommandOptions {
no_deps: options.no_deps,
assume_yes: false,
no_flags: options.no_flags,
cross_prefix: options.cross_prefix,
clean: options.clean,
install_test_deps: options.install_test_deps,
install_context: None,
dep_chain: None,
},
)?;
} else if !maybe_prompt_to_skip_tests_for_missing_requested_deps(
&mut pkg_spec,
&missing_test,
"Requested test dependencies were not installed",
)? {
anyhow::bail!("Aborted");
}
}
}
}
if should_install_test_deps(&pkg_spec, options.install_test_deps) {
let missing_test = deps::check_test_deps(&pkg_spec, &db_path)?;
if !missing_test.is_empty()
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
&mut pkg_spec,
&missing_test,
"Requested test dependencies are still missing",
)?
{
deps::require_test_deps(&pkg_spec, &db_path)?;
}
} }
} }
@@ -3717,24 +3859,33 @@ pub fn run(cli: Cli) -> Result<()> {
})?; })?;
let db_path = config.installed_db_path(&cli.rootfs); let db_path = config.installed_db_path(&cli.rootfs);
if cli.no_deps || !should_install_test_deps(&pkg_spec, install_test_deps) { if cli.no_deps && should_install_test_deps(&pkg_spec, install_test_deps) {
let missing_test = deps::check_test_deps(&pkg_spec, &db_path)?;
if !missing_test.is_empty()
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
&mut pkg_spec,
&missing_test,
"Requested test dependencies are missing",
)?
{
anyhow::bail!("Missing test dependencies: {}", missing_test.join(", "));
}
} else if cli.no_deps || !should_install_test_deps(&pkg_spec, install_test_deps) {
maybe_disable_tests_for_missing_deps(&mut pkg_spec, &db_path)?; maybe_disable_tests_for_missing_deps(&mut pkg_spec, &db_path)?;
} }
// Check build dependencies // Check build dependencies
if !cli.no_deps { if !cli.no_deps {
deps::print_dep_status(&pkg_spec, &db_path)?; deps::print_dep_status(&pkg_spec, &db_path)?;
let missing = merge_missing_dependencies( let missing_required = merge_missing_dependencies(
deps::check_build_deps(&pkg_spec, &db_path)?, deps::check_build_deps(&pkg_spec, &db_path)?,
deps::check_runtime_deps(&pkg_spec, &db_path)?, deps::check_runtime_deps(&pkg_spec, &db_path)?,
); );
let missing = if should_install_test_deps(&pkg_spec, install_test_deps) { if !missing_required.is_empty() {
merge_missing_dependencies(missing, deps::check_test_deps(&pkg_spec, &db_path)?) ui::warn(format!(
} else { "Missing dependencies: {}",
missing missing_required.join(", ")
}; ));
if !missing.is_empty() {
ui::warn(format!("Missing dependencies: {}", missing.join(", ")));
let local_sibling_root = spec_path let local_sibling_root = spec_path
.parent() .parent()
.and_then(|p| p.parent()) .and_then(|p| p.parent())
@@ -3742,7 +3893,7 @@ pub fn run(cli: Cli) -> Result<()> {
let dep_plan = planner::build_dependency_install_plan( let dep_plan = planner::build_dependency_install_plan(
&config, &config,
&cli.rootfs, &cli.rootfs,
&missing, &missing_required,
planner::PlannerOptions { planner::PlannerOptions {
assume_yes: cli.yes, assume_yes: cli.yes,
prefer_binary: config.repo_settings.prefer_binary, prefer_binary: config.repo_settings.prefer_binary,
@@ -3751,7 +3902,11 @@ pub fn run(cli: Cli) -> Result<()> {
}, },
)?; )?;
print_plan_summary(&dep_plan); print_plan_summary(&dep_plan);
if !ui::prompt_package_action("dependency installation", &missing, true)? { if !ui::prompt_package_action(
"dependency installation",
&missing_required,
true,
)? {
anyhow::bail!("Aborted"); anyhow::bail!("Aborted");
} }
if cli.dry_run { if cli.dry_run {
@@ -3775,7 +3930,84 @@ pub fn run(cli: Cli) -> Result<()> {
deps::require_build_deps(&pkg_spec, &db_path)?; deps::require_build_deps(&pkg_spec, &db_path)?;
deps::require_runtime_deps(&pkg_spec, &db_path)?; deps::require_runtime_deps(&pkg_spec, &db_path)?;
if should_install_test_deps(&pkg_spec, install_test_deps) { if should_install_test_deps(&pkg_spec, install_test_deps) {
deps::require_test_deps(&pkg_spec, &db_path)?; let missing_test = deps::check_test_deps(&pkg_spec, &db_path)?;
if !missing_test.is_empty() {
let local_sibling_root = spec_path
.parent()
.and_then(|p| p.parent())
.map(Path::to_path_buf);
let dep_plan = match planner::build_dependency_install_plan(
&config,
&cli.rootfs,
&missing_test,
planner::PlannerOptions {
assume_yes: cli.yes,
prefer_binary: config.repo_settings.prefer_binary,
local_sibling_root,
include_test_deps: install_test_deps,
},
) {
Ok(plan) => plan,
Err(err)
if maybe_prompt_to_skip_tests_for_missing_requested_deps(
&mut pkg_spec,
&missing_test,
"Requested test dependencies could not be resolved",
)? =>
{
planner::ExecutionPlan { steps: Vec::new() }
}
Err(err) => return Err(err),
};
if !pkg_spec.build.flags.skip_tests && !dep_plan.steps.is_empty() {
ui::warn(format!(
"Missing test dependencies: {}",
missing_test.join(", ")
));
print_plan_summary(&dep_plan);
if !ui::prompt_package_action(
"dependency installation",
&missing_test,
true,
)? {
if !maybe_prompt_to_skip_tests_for_missing_requested_deps(
&mut pkg_spec,
&missing_test,
"Requested test dependencies were not installed",
)? {
anyhow::bail!("Aborted");
}
} else if !cli.dry_run {
execute_install_plan_with_child_commands(
&dep_plan,
&cli.rootfs,
&config,
InstallPlanExecutionOptions {
no_flags: cli.no_flags,
cross_prefix: cli.cross_prefix.as_deref(),
clean: cli.clean,
dry_run: cli.dry_run,
confirm_installation: false,
install_test_deps,
},
)?;
}
}
if should_install_test_deps(&pkg_spec, install_test_deps) {
let missing_test = deps::check_test_deps(&pkg_spec, &db_path)?;
if !missing_test.is_empty()
&& !maybe_prompt_to_skip_tests_for_missing_requested_deps(
&mut pkg_spec,
&missing_test,
"Requested test dependencies are still missing",
)?
{
deps::require_test_deps(&pkg_spec, &db_path)?;
}
}
}
} }
} else if cli.dry_run { } else if cli.dry_run {
ui::info("Dry run enabled, stopping before build."); ui::info("Dry run enabled, stopping before build.");
@@ -5594,6 +5826,42 @@ optional = []
))); )));
} }
#[test]
fn requested_test_deps_prompt_can_disable_tests() -> Result<()> {
let mut spec = test_package_spec(package::BuildType::Meson, None, &[]);
spec.dependencies.test = vec!["pytest".into()];
ui::set_assume_yes(true);
let prompted = maybe_prompt_to_skip_tests_for_missing_requested_deps(
&mut spec,
&["pytest".into()],
"Requested test dependencies are missing",
)?;
ui::set_assume_yes(false);
assert!(prompted);
assert!(spec.build.flags.skip_tests);
Ok(())
}
#[test]
fn requested_test_deps_prompt_is_ignored_for_non_automatic_test_builders() -> Result<()> {
let mut spec = test_package_spec(package::BuildType::Custom, None, &[]);
spec.dependencies.test = vec!["pytest".into()];
ui::set_assume_yes(true);
let prompted = maybe_prompt_to_skip_tests_for_missing_requested_deps(
&mut spec,
&["pytest".into()],
"Requested test dependencies are missing",
)?;
ui::set_assume_yes(false);
assert!(!prompted);
assert!(!spec.build.flags.skip_tests);
Ok(())
}
#[test] #[test]
fn rootfs_is_system_root_detects_live_rootfs() { fn rootfs_is_system_root_detects_live_rootfs() {
assert!(rootfs_is_system_root(Path::new("/"))); assert!(rootfs_is_system_root(Path::new("/")));
-12
View File
@@ -172,18 +172,6 @@ impl ShellHelpers {
self.depot_executable.to_string_lossy().into_owned(), self.depot_executable.to_string_lossy().into_owned(),
); );
} }
/// Apply helper-related variables directly to a `std::process::Command`.
pub fn apply_to_command(&self, cmd: &mut std::process::Command) {
cmd.env("PATH", &self.path_value)
.env("DEPOT_OUTPUTS_DIR", &self.outputs_dir)
.env("DEPOT_INTERNAL_DIR", INTERNAL_DEPOT_DIR)
.env(DEPOT_HAUL_HELPER_ENV, &self.haul_path)
.env(DEPOT_SUBDESTDIR_HELPER_ENV, &self.subdestdir_path)
.env(DEPOT_PYTHON_BUILD_HELPER_ENV, &self.python_build_path)
.env(DEPOT_PYTHON_INSTALL_HELPER_ENV, &self.python_install_path)
.env(DEPOT_EXECUTABLE_ENV, &self.depot_executable);
}
} }
/// Wrap a shell command with helper functions that invoke the helper scripts /// Wrap a shell command with helper functions that invoke the helper scripts
+92 -28
View File
@@ -10,6 +10,15 @@ use std::process::Command;
use crate::builder::state::{BuildStep, StateTracker}; use crate::builder::state::{BuildStep, StateTracker};
fn hook_env_vars(
spec: &PackageSpec,
shell_helpers: &crate::shell_helpers::ShellHelpers,
) -> crate::builder::EnvVars {
let mut env_vars = crate::builder::standard_build_env(spec, None, true, true);
shell_helpers.apply_to_env_vars(&mut env_vars);
env_vars
}
/// Apply patches and run `post_extract` commands in the extracted source tree. /// Apply patches and run `post_extract` commands in the extracted source tree.
pub fn post_extract( pub fn post_extract(
spec: &PackageSpec, spec: &PackageSpec,
@@ -85,23 +94,27 @@ fn run_post_extract_commands(spec: &PackageSpec, source: &Source, src_dir: &Path
"Running {} post-extract command(s)...", "Running {} post-extract command(s)...",
source.post_extract.len() source.post_extract.len()
); );
let helper_root = tempfile::tempdir().context("Failed to create post-extract helper root")?;
let shell_helpers = crate::shell_helpers::ShellHelpers::new(helper_root.path())?;
let env_vars = hook_env_vars(spec, &shell_helpers);
for cmd in &source.post_extract { for cmd in &source.post_extract {
let cmd = spec.expand_vars(cmd); let cmd_str = spec.expand_vars(cmd);
crate::log_info!(" post_extract: {}", cmd); crate::log_info!(" post_extract: {}", cmd_str);
let wrapped_cmd = crate::shell_helpers::wrap_shell_command(&cmd_str);
// Use a shell for convenience; this is a package manager, so specs are trusted input. // Use a shell for convenience; this is a package manager, so specs are trusted input.
let status = Command::new("sh") let mut shell_cmd = Command::new("sh");
.current_dir(src_dir) shell_cmd.current_dir(src_dir);
.env("DEPOT_SPECDIR", &spec.spec_dir) crate::builder::prepare_command(&mut shell_cmd, &env_vars);
.env("PATH", crate::runtime_env::safe_script_path()) let status = shell_cmd
.arg("-c") .arg("-c")
.arg(&cmd) .arg(&wrapped_cmd)
.status() .status()
.with_context(|| format!("Failed to run post_extract command: {}", cmd))?; .with_context(|| format!("Failed to run post_extract command: {}", cmd_str))?;
if !status.success() { if !status.success() {
bail!("post_extract command failed: {}", cmd); bail!("post_extract command failed: {}", cmd_str);
} }
} }
@@ -121,6 +134,12 @@ pub fn run_post_configure_commands(
crate::log_info!("Running {} post-configure command(s)...", commands.len()); crate::log_info!("Running {} post-configure command(s)...", commands.len());
let shell_helpers = crate::shell_helpers::ShellHelpers::new(destdir)?; let shell_helpers = crate::shell_helpers::ShellHelpers::new(destdir)?;
let mut env_vars = hook_env_vars(spec, &shell_helpers);
crate::builder::set_env_var(
&mut env_vars,
"DESTDIR",
destdir.to_string_lossy().into_owned(),
);
for cmd in commands { for cmd in commands {
let cmd_str = spec.expand_vars(cmd); let cmd_str = spec.expand_vars(cmd);
@@ -129,13 +148,8 @@ pub fn run_post_configure_commands(
let mut shell_cmd = Command::new("sh"); let mut shell_cmd = Command::new("sh");
shell_cmd.current_dir(src_dir); shell_cmd.current_dir(src_dir);
shell_helpers.apply_to_command(&mut shell_cmd); crate::builder::prepare_command(&mut shell_cmd, &env_vars);
let status = shell_cmd let status = shell_cmd
.env("DEPOT_SPECDIR", &spec.spec_dir)
.env("DESTDIR", destdir)
.env("DEPOT_ROOTFS", &spec.build.flags.rootfs)
.env("CC", &spec.build.flags.cc)
.env("AR", &spec.build.flags.ar)
.arg("-c") .arg("-c")
.arg(&wrapped_cmd) .arg(&wrapped_cmd)
.status() .status()
@@ -158,6 +172,12 @@ pub fn run_post_compile_commands(spec: &PackageSpec, src_dir: &Path, destdir: &P
crate::log_info!("Running {} post-compile command(s)...", commands.len()); crate::log_info!("Running {} post-compile command(s)...", commands.len());
let shell_helpers = crate::shell_helpers::ShellHelpers::new(destdir)?; let shell_helpers = crate::shell_helpers::ShellHelpers::new(destdir)?;
let mut env_vars = hook_env_vars(spec, &shell_helpers);
crate::builder::set_env_var(
&mut env_vars,
"DESTDIR",
destdir.to_string_lossy().into_owned(),
);
for cmd in commands { for cmd in commands {
let cmd_str = spec.expand_vars(cmd); let cmd_str = spec.expand_vars(cmd);
@@ -166,13 +186,8 @@ pub fn run_post_compile_commands(spec: &PackageSpec, src_dir: &Path, destdir: &P
let mut shell_cmd = Command::new("sh"); let mut shell_cmd = Command::new("sh");
shell_cmd.current_dir(src_dir); shell_cmd.current_dir(src_dir);
shell_helpers.apply_to_command(&mut shell_cmd); crate::builder::prepare_command(&mut shell_cmd, &env_vars);
let status = shell_cmd let status = shell_cmd
.env("DEPOT_SPECDIR", &spec.spec_dir)
.env("DESTDIR", destdir)
.env("DEPOT_ROOTFS", &spec.build.flags.rootfs)
.env("CC", &spec.build.flags.cc)
.env("AR", &spec.build.flags.ar)
.arg("-c") .arg("-c")
.arg(&wrapped_cmd) .arg(&wrapped_cmd)
.status() .status()
@@ -195,6 +210,12 @@ pub fn run_post_install_commands(spec: &PackageSpec, src_dir: &Path, destdir: &P
crate::log_info!("Running {} post-install command(s)...", commands.len()); crate::log_info!("Running {} post-install command(s)...", commands.len());
let shell_helpers = crate::shell_helpers::ShellHelpers::new(destdir)?; let shell_helpers = crate::shell_helpers::ShellHelpers::new(destdir)?;
let mut env_vars = hook_env_vars(spec, &shell_helpers);
crate::builder::set_env_var(
&mut env_vars,
"DESTDIR",
destdir.to_string_lossy().into_owned(),
);
for cmd in commands { for cmd in commands {
let cmd_str = spec.expand_vars(cmd); let cmd_str = spec.expand_vars(cmd);
@@ -203,13 +224,8 @@ pub fn run_post_install_commands(spec: &PackageSpec, src_dir: &Path, destdir: &P
let mut shell_cmd = Command::new("sh"); let mut shell_cmd = Command::new("sh");
shell_cmd.current_dir(src_dir); shell_cmd.current_dir(src_dir);
shell_helpers.apply_to_command(&mut shell_cmd); crate::builder::prepare_command(&mut shell_cmd, &env_vars);
let status = shell_cmd let status = shell_cmd
.env("DEPOT_SPECDIR", &spec.spec_dir)
.env("DESTDIR", destdir)
.env("DEPOT_ROOTFS", &spec.build.flags.rootfs)
.env("CC", &spec.build.flags.cc)
.env("AR", &spec.build.flags.ar)
.arg("-c") .arg("-c")
.arg(&wrapped_cmd) .arg(&wrapped_cmd)
.status() .status()
@@ -291,7 +307,7 @@ fn download(url: &str, dest: &Path) -> Result<()> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{resolve_patch_path, run_post_install_commands}; use super::{resolve_patch_path, run_post_extract_commands, run_post_install_commands};
use crate::package::{ use crate::package::{
Alternatives, Build, BuildFlags, BuildType, Dependencies, PackageInfo, PackageSpec, Source, Alternatives, Build, BuildFlags, BuildType, Dependencies, PackageInfo, PackageSpec, Source,
}; };
@@ -386,4 +402,52 @@ mod tests {
.exists() .exists()
); );
} }
#[test]
fn post_extract_commands_can_call_python_build_helper() {
let tmp = tempfile::tempdir().unwrap();
let spec_dir = tmp.path().join("spec");
let src_dir = tmp.path().join("src");
std::fs::create_dir_all(&spec_dir).unwrap();
std::fs::create_dir_all(&src_dir).unwrap();
let fake_depot = tmp.path().join("fake-depot");
let log_path = tmp.path().join("python-build.log");
std::fs::write(
&fake_depot,
format!(
"#!/bin/sh\nset -eu\nprintf '%s\\n' \"$@\" > '{}'\n",
log_path.display()
),
)
.unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(&fake_depot).unwrap().permissions();
perms.set_mode(0o755);
std::fs::set_permissions(&fake_depot, perms).unwrap();
}
let spec = dummy_spec(&spec_dir);
let source = Source {
url: "https://example.com/foo.tar.gz".into(),
sha256: "skip".into(),
extract_dir: "foo".into(),
patches: Vec::new(),
post_extract: vec![format!(
"DEPOT_EXECUTABLE='{}' python_build --src-dir . --dist-dir dist",
fake_depot.display()
)],
cherry_pick: Vec::new(),
};
run_post_extract_commands(&spec, &source, &src_dir).unwrap();
let logged = std::fs::read_to_string(&log_path).unwrap();
assert!(logged.contains("internal"));
assert!(logged.contains("python-build"));
assert!(logged.contains("--src-dir"));
assert!(logged.contains("--dist-dir"));
}
} }