Implement Perl build helper commands and integrate into CLI

- Added `run_helper_configure` and `run_helper_install` functions in `perl.rs` to handle Perl-specific build configurations and installations.
- Introduced new internal commands in `cli.rs` for Perl configuration and installation.
- Updated `internal.rs` to execute Perl helper commands using the new functions.
- Enhanced `shell_helpers.rs` to include scripts for Perl configure and install commands.
- Modified `hooks.rs` to apply build helper context and directories for post-extract and post-configure commands.
- Added tests to ensure the correct execution of Perl build helper commands and their integration with the build process.
This commit is contained in:
2026-04-04 17:31:39 -05:00
parent 34d52d11a9
commit d54e6d56e8
14 changed files with 1367 additions and 108 deletions
+219
View File
@@ -1,5 +1,6 @@
//! GNU Autotools build system (configure && make && make install)
use crate::builder::BuildHelperContext;
use crate::builder::state::{BuildStep, StateTracker};
use crate::cross::CrossConfig;
use crate::fakeroot;
@@ -501,6 +502,202 @@ pub(crate) fn ensure_host_build(
.with_context(|| format!("Failed to resolve host build dir: {}", build_dir.display()))
}
pub(crate) fn run_helper_configure(
context: &BuildHelperContext,
source_dir: Option<&Path>,
build_dir: Option<&Path>,
cross: Option<&CrossConfig>,
env_vars: &[(String, String)],
extra_args: &[String],
) -> Result<()> {
let flags = context.build_flags();
let source_dir = source_dir
.map(Path::to_path_buf)
.unwrap_or(std::env::current_dir().context("Failed to determine current directory")?);
let build_dir = build_dir.map(Path::to_path_buf).unwrap_or_else(|| {
flags
.build_dir
.as_ref()
.map(|dir| source_dir.join(dir))
.unwrap_or_else(|| source_dir.clone())
});
fs::create_dir_all(&build_dir)
.with_context(|| format!("Failed to create build directory: {}", build_dir.display()))?;
let mut helper_env = env_vars.to_vec();
let cc = if let Some(cc_cfg) = cross {
cc_cfg.cc.clone()
} else {
helper_env
.iter()
.find(|(key, _)| key == "CC")
.map(|(_, value)| value.clone())
.unwrap_or_else(|| flags.cc.clone())
};
if let Some(cflags_str) = helper_env
.iter()
.find(|(key, _)| key == "CFLAGS")
.map(|(_, value)| value.clone())
.filter(|value| !value.trim().is_empty())
{
let expanded = expand_shell_commands(&cflags_str, &cc)?;
crate::builder::set_env_var(&mut helper_env, "CFLAGS", expanded);
}
let configure_path = helper_configure_path(context, &source_dir);
let mut configure_cmd = Command::new(&configure_path);
configure_cmd.current_dir(&build_dir);
crate::builder::prepare_tool_command(&mut configure_cmd, &helper_env);
let help_text = configure_help_text(&configure_path, &build_dir, &helper_env);
configure_cmd.arg(format!("--prefix={}", flags.prefix));
for default_dir_arg in default_configure_install_dirs(&flags, help_text.as_deref()) {
configure_cmd.arg(default_dir_arg);
}
let supports_host =
configure_supports_option(help_text.as_deref(), "--host", &flags.configure_file);
let supports_build =
configure_supports_option(help_text.as_deref(), "--build", &flags.configure_file);
let requested_host = if let Some(cc_cfg) = cross {
Some(cc_cfg.host_triple().to_string())
} else if !flags.chost.is_empty() {
Some(flags.chost.clone())
} else {
None
}
.map(|host| {
if flags.lib32_variant {
lib32_host_triple(&host)
} else {
host
}
});
let requested_build = if cross.is_some() {
CrossConfig::build_triple().ok()
} else if !flags.cbuild.is_empty() {
Some(flags.cbuild.clone())
} else {
None
};
if let Some(host) = requested_host {
if supports_host {
configure_cmd.arg(format!("--host={host}"));
} else {
crate::log_info!(" configure does not support --host; skipping {}", host);
}
}
if let Some(build) = requested_build {
if supports_build {
configure_cmd.arg(format!("--build={build}"));
} else {
crate::log_info!(" configure does not support --build; skipping {}", build);
}
}
for arg in &flags.configure {
configure_cmd.arg(expand_with_envs(&context.expand_vars(arg), &helper_env));
}
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Autotools, &flags)?
{
add_auto_configure_arg_if_supported(&mut configure_cmd, help_text.as_deref(), &arg);
}
for arg in extra_args {
add_auto_configure_arg_if_supported(
&mut configure_cmd,
help_text.as_deref(),
&expand_with_envs(&context.expand_vars(arg), &helper_env),
);
}
let status = crate::interrupts::command_status(&mut configure_cmd)
.with_context(|| format!("Failed to run helper configure in {}", build_dir.display()))?;
if !status.success() {
anyhow::bail!("configure failed with status: {}", status);
}
Ok(())
}
pub(crate) fn run_helper_install(
context: &BuildHelperContext,
build_dir: Option<&Path>,
env_vars: &[(String, String)],
extra_args: &[String],
) -> Result<()> {
let flags = context.build_flags();
let source_dir = helper_source_dir();
let build_dir = build_dir.map(Path::to_path_buf).unwrap_or_else(|| {
flags
.build_dir
.as_ref()
.map(|dir| source_dir.join(dir))
.unwrap_or_else(|| source_dir.clone())
});
let destdir = std::env::var("DESTDIR").context("DESTDIR must be set for autotools_install")?;
let make_exec = resolve_make_exec(&flags.make_exec);
let install_dirs = resolve_make_dirs(
&build_dir,
&flags.make_install_dirs,
"build.flags.make_install_dirs",
)?;
let install_targets = phase_targets(
&flags.make_install_target,
&flags.make_install_targets,
Some("install"),
);
for install_dir in install_dirs {
let mut install_cmd = fakeroot::wrap_install_command(make_exec, Path::new(&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}"));
}
add_make_variable_overrides_if_supported(
&mut install_cmd,
make_exec,
&flags.make_install_vars,
"install",
)?;
for install_target in &install_targets {
install_cmd.arg(install_target);
}
for arg in extra_args {
install_cmd.arg(context.expand_vars(arg));
}
let mut install_env = env_vars.to_vec();
crate::builder::set_env_var(&mut install_env, "DESTDIR", destdir.clone());
crate::builder::prepare_tool_command(&mut install_cmd, &install_env);
let status = crate::interrupts::command_status(&mut install_cmd).with_context(|| {
format!(
"Failed to run helper {} {} in {}",
make_exec,
install_targets.join(" "),
install_dir.display()
)
})?;
if !status.success() {
anyhow::bail!(
"{} {} failed with status: {} (dir: {})",
make_exec,
install_targets.join(" "),
status,
install_dir.display()
);
}
}
Ok(())
}
fn num_cpus() -> usize {
std::thread::available_parallelism()
.map(|n| n.get())
@@ -544,6 +741,22 @@ fn resolve_configure_path(spec: &PackageSpec, actual_src: &Path) -> PathBuf {
}
}
fn helper_configure_path(context: &BuildHelperContext, actual_src: &Path) -> PathBuf {
let flags = context.build_flags();
let configured = context.expand_vars(&flags.configure_file);
let trimmed = configured.trim();
if trimmed.is_empty() {
return actual_src.join("configure");
}
let path = Path::new(trimmed);
if path.is_absolute() {
path.to_path_buf()
} else {
actual_src.join(path)
}
}
fn configure_help_text(
configure_path: &Path,
build_dir: &Path,
@@ -823,6 +1036,12 @@ fn nonempty_trimmed(value: &str) -> Option<&str> {
(!trimmed.is_empty()).then_some(trimmed)
}
fn helper_source_dir() -> PathBuf {
std::env::var(crate::builder::DEPOT_BUILD_HELPER_SOURCE_DIR_ENV)
.map(PathBuf::from)
.unwrap_or_else(|_| std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")))
}
pub(crate) fn resolve_make_exec(configured: &str) -> &str {
nonempty_trimmed(configured).unwrap_or("make")
}