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")
}
+140
View File
@@ -1,5 +1,6 @@
//! CMake build system
use crate::builder::BuildHelperContext;
use crate::builder::state::{BuildStep, StateTracker};
use crate::cross::CrossConfig;
use crate::fakeroot;
@@ -359,6 +360,139 @@ 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.join("build"))
});
let prefix = effective_cmake_install_prefix(&flags);
fs::create_dir_all(&build_dir)
.with_context(|| format!("Failed to create build directory: {}", build_dir.display()))?;
let toolchain_file = if let Some(cc_cfg) = cross {
Some(cc_cfg.generate_cmake_toolchain(&build_dir)?)
} else {
None
};
let mut cmake_cmd = Command::new("cmake");
cmake_cmd.current_dir(&build_dir);
cmake_cmd.arg("-S").arg(&source_dir);
cmake_cmd.arg("-B").arg(&build_dir);
cmake_cmd.arg(format!("-DCMAKE_INSTALL_PREFIX={prefix}"));
cmake_cmd.arg("-DCMAKE_BUILD_TYPE=Release");
for arg in cmake_install_dir_args(&flags, prefix) {
cmake_cmd.arg(arg);
}
for arg in cmake_lib32_target_args(&flags, cross) {
cmake_cmd.arg(arg);
}
if let Some(toolchain_file) = &toolchain_file {
cmake_cmd.arg(format!(
"-DCMAKE_TOOLCHAIN_FILE={}",
toolchain_file.display()
));
}
let make_exec_override = flags.make_exec.trim();
if !make_exec_override.is_empty() {
if !cmake_configure_flags_specify_generator(&flags.configure)
&& let Some(generator) = cmake_generator_for_make_exec(make_exec_override)
{
cmake_cmd.arg("-G").arg(generator);
}
if !cmake_configure_flags_set_make_program(&flags.configure) {
cmake_cmd.arg(format!("-DCMAKE_MAKE_PROGRAM={make_exec_override}"));
}
}
for flag in &flags.configure {
cmake_cmd.arg(expand_with_envs(&context.expand_vars(flag), env_vars));
}
for arg in crate::builder::static_build_args_for(crate::package::BuildType::CMake, &flags)? {
cmake_cmd.arg(arg);
}
for arg in extra_args {
cmake_cmd.arg(expand_with_envs(&context.expand_vars(arg), env_vars));
}
crate::builder::prepare_tool_command(&mut cmake_cmd, &env_vars.to_vec());
let status = crate::interrupts::command_status(&mut cmake_cmd)
.context("Failed to run helper cmake configure")?;
if !status.success() {
anyhow::bail!("cmake configure failed");
}
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.join("build"))
});
let destdir = std::env::var("DESTDIR").context("DESTDIR must be set for cmake_install")?;
let install_targets = phase_targets(&flags.make_install_target, &flags.make_install_targets);
let mut install_cmd = fakeroot::wrap_install_command("cmake", Path::new(&destdir));
if install_targets.is_empty() {
install_cmd.arg("--install").arg(&build_dir);
} else {
install_cmd.arg("--build").arg(&build_dir);
install_cmd.arg("--target");
for target in &install_targets {
install_cmd.arg(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);
crate::builder::prepare_tool_command(&mut install_cmd, &install_env);
let status = crate::interrupts::command_status(&mut install_cmd)
.context("Failed to run helper cmake install")?;
if !status.success() {
if install_targets.is_empty() {
anyhow::bail!("cmake install failed");
}
anyhow::bail!(
"cmake install target(s) '{}' failed",
install_targets.join(" ")
);
}
Ok(())
}
/// Expand environment variables in a string (e.g., $DEPOT_SYSROOT)
fn expand_env_vars(input: &str) -> String {
let mut result = input.to_string();
@@ -580,6 +714,12 @@ fn num_cpus() -> usize {
.unwrap_or(1)
}
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(".")))
}
/// Resolve `source_subdir` with multiple fallbacks:
/// - empty -> use `src_dir`
/// - absolute path -> use if exists
+18 -8
View File
@@ -51,6 +51,8 @@ pub fn build(
let mut env_vars = crate::builder::standard_build_env(spec, cross, true, export_compiler_flags);
let shell_helpers = crate::shell_helpers::ShellHelpers::new(&install_destdir)?;
shell_helpers.apply_to_env_vars(&mut env_vars);
crate::builder::apply_build_helper_context_env(&mut env_vars, spec)?;
crate::builder::apply_build_helper_dirs_env(&mut env_vars, Some(src_dir), Some(&build_dir));
// For custom builds, look for a build.sh script in the source directory
let build_script = src_dir.join("build.sh");
@@ -280,19 +282,27 @@ fn build_function_mode_install_command(
"DEPOT_OUTPUT_NAME='{q_name}'; DEPOT_OUTPUT_DESTDIR='{q_dest}'; DESTDIR=\"$DEPOT_OUTPUT_DESTDIR\"; export DEPOT_OUTPUT_NAME DEPOT_OUTPUT_DESTDIR DESTDIR\n"
));
wrapper.push_str("depot_output_installed=0\n");
wrapper.push_str(&format!(
"if depot_has_function depot_install_{fn_suffix}; then depot_install_{fn_suffix}; depot_output_installed=1;\n"
));
wrapper.push_str(&format!(
"elif depot_has_function install_{fn_suffix}; then install_{fn_suffix}; depot_output_installed=1;\n"
));
if out.name == *primary {
wrapper
.push_str("elif depot_has_function depot_install; then depot_install; depot_output_installed=1;\n");
wrapper.push_str(
"if depot_has_function depot_install; then depot_install; depot_output_installed=1;\n",
);
wrapper.push_str(
"elif depot_has_function install; then install; depot_output_installed=1;\n",
);
wrapper.push_str(&format!(
"elif depot_has_function depot_install_{fn_suffix}; then depot_install_{fn_suffix}; depot_output_installed=1;\n"
));
wrapper.push_str(&format!(
"elif depot_has_function install_{fn_suffix}; then install_{fn_suffix}; depot_output_installed=1;\n"
));
wrapper.push_str("elif [ \"$depot_build_ran\" = 1 ]; then depot_output_installed=1;\n");
} else {
wrapper.push_str(&format!(
"if depot_has_function depot_install_{fn_suffix}; then depot_install_{fn_suffix}; depot_output_installed=1;\n"
));
wrapper.push_str(&format!(
"elif depot_has_function install_{fn_suffix}; then install_{fn_suffix}; depot_output_installed=1;\n"
));
}
wrapper.push_str("fi\n");
wrapper.push_str("if [ \"$depot_output_installed\" != 1 ]; then\n");
+95
View File
@@ -1,5 +1,6 @@
//! Meson build system
use crate::builder::BuildHelperContext;
use crate::builder::state::{BuildStep, StateTracker};
use crate::cross::CrossConfig;
use crate::fakeroot;
@@ -267,12 +268,106 @@ 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(|| resolve_build_dir(&source_dir, &flags));
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();
configure_pkg_config_env(&mut helper_env, &flags, cross);
let cross_file = if let Some(cc_cfg) = cross {
Some(cc_cfg.generate_meson_cross_file(&build_dir)?)
} else if flags.lib32_variant {
Some(generate_lib32_meson_cross_file(&flags, &build_dir)?)
} else {
None
};
let mut meson_cmd = Command::new("meson");
meson_cmd.current_dir(&source_dir);
meson_cmd.arg("setup");
meson_cmd.arg(&build_dir);
for arg in meson_setup_args(&flags, cross_file.as_deref(), &helper_env) {
meson_cmd.arg(arg);
}
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Meson, &flags)? {
meson_cmd.arg(arg);
}
for arg in extra_args {
meson_cmd.arg(context.expand_vars(arg));
}
crate::builder::prepare_tool_command(&mut meson_cmd, &helper_env);
let status = crate::interrupts::command_status(&mut meson_cmd)
.context("Failed to run helper meson setup")?;
if !status.success() {
bail!("meson setup failed");
}
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 build_dir = build_dir
.map(Path::to_path_buf)
.unwrap_or_else(|| resolve_build_dir(&helper_source_dir(), &flags));
let destdir = std::env::var("DESTDIR").context("DESTDIR must be set for meson_install")?;
let mut install_cmd = fakeroot::wrap_install_command("meson", Path::new(&destdir));
install_cmd.arg("install");
install_cmd.arg("-C").arg(&build_dir);
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);
crate::builder::prepare_tool_command(&mut install_cmd, &install_env);
let status = crate::interrupts::command_status(&mut install_cmd)
.context("Failed to run helper meson install")?;
if !status.success() {
bail!("meson install failed");
}
Ok(())
}
fn num_cpus() -> usize {
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1)
}
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(".")))
}
fn resolve_build_dir(actual_src: &Path, flags: &crate::package::BuildFlags) -> PathBuf {
if let Some(dir) = flags
.build_dir
+162
View File
@@ -22,6 +22,9 @@ use walkdir::WalkDir;
pub type EnvVars = Vec<(String, String)>;
pub(crate) const DEPOT_BUILD_HOST_DIR_ENV: &str = "DEPOT_BUILD_HOST_DIR";
pub(crate) const DEPOT_BUILD_HELPER_CONTEXT_ENV: &str = "DEPOT_BUILD_HELPER_CONTEXT";
pub(crate) const DEPOT_BUILD_HELPER_SOURCE_DIR_ENV: &str = "DEPOT_BUILD_HELPER_SOURCE_DIR";
pub(crate) const DEPOT_BUILD_HELPER_BUILD_DIR_ENV: &str = "DEPOT_BUILD_HELPER_BUILD_DIR";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetBuildKind {
@@ -45,6 +48,154 @@ pub(crate) struct InstallDirs {
pub infodir: String,
}
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
pub(crate) struct BuildHelperContext {
pub package_name: String,
pub package_version: String,
pub spec_dir: PathBuf,
pub flags: BuildFlags,
pub lib32_variant: bool,
pub host_build_dir: Option<String>,
}
impl BuildHelperContext {
pub(crate) fn from_spec(spec: &PackageSpec) -> Self {
Self {
package_name: spec.package.name.clone(),
package_version: spec.package.version.clone(),
spec_dir: spec.spec_dir.clone(),
flags: spec.build.flags.clone(),
lib32_variant: spec.build.flags.lib32_variant,
host_build_dir: spec.build.flags.host_build_dir.clone(),
}
}
pub(crate) fn expand_vars(&self, input: &str) -> String {
let specdir = self.spec_dir.to_string_lossy();
input
.replace("$name", &self.package_name)
.replace("$version", &self.package_version)
.replace("$specdir", &specdir)
.replace("$DEPOT_SPECDIR", &specdir)
}
pub(crate) fn build_flags(&self) -> BuildFlags {
let mut flags = self.flags.clone();
flags.lib32_variant = self.lib32_variant;
flags.host_build_dir = self.host_build_dir.clone();
flags
}
}
pub(crate) fn apply_build_helper_context_env(
env_vars: &mut EnvVars,
spec: &PackageSpec,
) -> Result<()> {
let encoded = toml::to_string(&BuildHelperContext::from_spec(spec))
.context("Failed to serialize build helper context")?;
set_env_var(env_vars, DEPOT_BUILD_HELPER_CONTEXT_ENV, encoded);
Ok(())
}
pub(crate) fn apply_build_helper_dirs_env(
env_vars: &mut EnvVars,
source_dir: Option<&Path>,
build_dir: Option<&Path>,
) {
if let Some(source_dir) = source_dir {
set_env_var(
env_vars,
DEPOT_BUILD_HELPER_SOURCE_DIR_ENV,
source_dir.to_string_lossy().into_owned(),
);
}
if let Some(build_dir) = build_dir {
set_env_var(
env_vars,
DEPOT_BUILD_HELPER_BUILD_DIR_ENV,
build_dir.to_string_lossy().into_owned(),
);
}
}
pub(crate) fn run_autotools_helper_configure(
context: &BuildHelperContext,
source_dir: Option<&Path>,
build_dir: Option<&Path>,
cross: Option<&CrossConfig>,
env_vars: &EnvVars,
extra_args: &[String],
) -> Result<()> {
autotools::run_helper_configure(context, source_dir, build_dir, cross, env_vars, extra_args)
}
pub(crate) fn run_autotools_helper_install(
context: &BuildHelperContext,
build_dir: Option<&Path>,
env_vars: &EnvVars,
extra_args: &[String],
) -> Result<()> {
autotools::run_helper_install(context, build_dir, env_vars, extra_args)
}
pub(crate) fn run_cmake_helper_configure(
context: &BuildHelperContext,
source_dir: Option<&Path>,
build_dir: Option<&Path>,
cross: Option<&CrossConfig>,
env_vars: &EnvVars,
extra_args: &[String],
) -> Result<()> {
cmake::run_helper_configure(context, source_dir, build_dir, cross, env_vars, extra_args)
}
pub(crate) fn run_cmake_helper_install(
context: &BuildHelperContext,
build_dir: Option<&Path>,
env_vars: &EnvVars,
extra_args: &[String],
) -> Result<()> {
cmake::run_helper_install(context, build_dir, env_vars, extra_args)
}
pub(crate) fn run_meson_helper_configure(
context: &BuildHelperContext,
source_dir: Option<&Path>,
build_dir: Option<&Path>,
cross: Option<&CrossConfig>,
env_vars: &EnvVars,
extra_args: &[String],
) -> Result<()> {
meson::run_helper_configure(context, source_dir, build_dir, cross, env_vars, extra_args)
}
pub(crate) fn run_meson_helper_install(
context: &BuildHelperContext,
build_dir: Option<&Path>,
env_vars: &EnvVars,
extra_args: &[String],
) -> Result<()> {
meson::run_helper_install(context, build_dir, env_vars, extra_args)
}
pub(crate) fn run_perl_helper_configure(
context: &BuildHelperContext,
source_dir: Option<&Path>,
env_vars: &EnvVars,
extra_args: &[String],
) -> Result<()> {
perl::run_helper_configure(context, source_dir, env_vars, extra_args)
}
pub(crate) fn run_perl_helper_install(
context: &BuildHelperContext,
build_dir: Option<&Path>,
env_vars: &EnvVars,
extra_args: &[String],
) -> Result<()> {
perl::run_helper_install(context, build_dir, env_vars, extra_args)
}
pub fn set_env_var(env_vars: &mut EnvVars, key: &str, value: impl Into<String>) {
let value = value.into();
if let Some((_, existing)) = env_vars.iter_mut().find(|(k, _)| k == key) {
@@ -1353,6 +1504,17 @@ mod tests {
assert_eq!(lib32_dirs.libexecdir, "/usr/lib32");
}
#[test]
fn test_build_helper_context_restores_runtime_build_flags() {
let mut spec = mk_spec(Vec::new(), Vec::new());
spec.build.flags.lib32_variant = true;
spec.build.flags.host_build_dir = Some("/tmp/build-host".into());
let restored = BuildHelperContext::from_spec(&spec).build_flags();
assert!(restored.lib32_variant);
assert_eq!(restored.host_build_dir.as_deref(), Some("/tmp/build-host"));
}
#[test]
fn test_install_dirs_respect_explicit_overrides_and_derived_defaults() {
let dirs = install_dirs(&BuildFlags {
+141
View File
@@ -1,5 +1,6 @@
//! Perl MakeMaker build system (`perl Makefile.PL && make && make test && make install`)
use crate::builder::BuildHelperContext;
use crate::builder::autotools;
use crate::builder::state::{BuildStep, StateTracker};
use crate::cross::CrossConfig;
@@ -261,6 +262,124 @@ pub fn build(
Ok(())
}
pub(crate) fn run_helper_configure(
context: &BuildHelperContext,
source_dir: Option<&Path>,
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 configure_script = helper_configure_script(context, &source_dir);
let mut helper_env = env_vars.to_vec();
crate::builder::set_env_var(&mut helper_env, "PERL_MM_USE_DEFAULT", "1");
if !flags.rootfs.is_empty() && flags.rootfs != "/" {
crate::builder::set_env_var(
&mut helper_env,
"PKG_CONFIG_SYSROOT_DIR",
flags.rootfs.clone(),
);
}
let mut configure_cmd =
Command::new(resolved_command_path("perl").unwrap_or_else(|| PathBuf::from("perl")));
configure_cmd.current_dir(&source_dir);
configure_cmd.arg(&configure_script);
if !has_assignment_prefix(&flags.configure, "INSTALLDIRS") {
configure_cmd.arg("INSTALLDIRS=vendor");
}
for arg in &flags.configure {
configure_cmd.arg(context.expand_vars(arg));
}
for arg in crate::builder::static_build_args_for(crate::package::BuildType::Perl, &flags)? {
configure_cmd.arg(arg);
}
for arg in extra_args {
configure_cmd.arg(context.expand_vars(arg));
}
crate::builder::prepare_tool_command(&mut configure_cmd, &helper_env);
let status = command_status_with_sh_fallback(&mut configure_cmd)
.context("Failed to run helper perl configure")?;
if !status.success() {
anyhow::bail!("perl Makefile.PL 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 build_dir = build_dir
.map(Path::to_path_buf)
.unwrap_or_else(helper_source_dir);
let destdir = std::env::var("DESTDIR").context("DESTDIR must be set for perl_install")?;
let make_exec = autotools::resolve_make_exec(&flags.make_exec);
let install_dirs = autotools::resolve_make_dirs(
&build_dir,
&flags.make_install_dirs,
"build.flags.make_install_dirs",
)?;
let install_targets = autotools::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 autotools::make_exec_supports_make_assignments(make_exec)
&& !autotools::has_make_variable_override(&flags.make_install_vars, "DESTDIR")
{
install_cmd.arg(format!("DESTDIR={destdir}"));
}
autotools::add_make_variable_overrides_if_supported(
&mut install_cmd,
make_exec,
&flags.make_install_vars,
"install",
)?;
for target in &install_targets {
install_cmd.arg(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 = command_status_with_sh_fallback(&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 resolve_perl_configure_script(spec: &PackageSpec, actual_src: &Path) -> PathBuf {
let configured = spec.expand_vars(&spec.build.flags.configure_file);
let trimmed = configured.trim();
@@ -276,6 +395,22 @@ fn resolve_perl_configure_script(spec: &PackageSpec, actual_src: &Path) -> PathB
}
}
fn helper_configure_script(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("Makefile.PL");
}
let path = Path::new(trimmed);
if path.is_absolute() {
path.to_path_buf()
} else {
actual_src.join(path)
}
}
fn command_status_with_sh_fallback(cmd: &mut Command) -> std::io::Result<std::process::ExitStatus> {
match crate::interrupts::command_status(cmd) {
Ok(status) => Ok(status),
@@ -346,6 +481,12 @@ fn has_assignment_prefix(args: &[String], name: &str) -> bool {
})
}
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(".")))
}
#[cfg(test)]
mod tests {
use super::*;