742 lines
25 KiB
Rust
Executable File
742 lines
25 KiB
Rust
Executable File
//! Custom build scripts
|
|
|
|
use crate::cross::CrossConfig;
|
|
use crate::fakeroot;
|
|
use crate::package::PackageSpec;
|
|
use crate::source::hooks;
|
|
use anyhow::{Context, Result};
|
|
use std::fs;
|
|
use std::path::Path;
|
|
use std::process::Command;
|
|
use tempfile::NamedTempFile;
|
|
|
|
pub fn build(
|
|
spec: &PackageSpec,
|
|
src_dir: &Path,
|
|
destdir: &Path,
|
|
cross: Option<&CrossConfig>,
|
|
export_compiler_flags: bool,
|
|
_host_build_dir: Option<&Path>,
|
|
) -> Result<()> {
|
|
let flags = &spec.build.flags;
|
|
let build_dir = if let Some(dir) = &flags.build_dir {
|
|
let bdir = src_dir.join(dir);
|
|
fs::create_dir_all(&bdir)?;
|
|
bdir
|
|
} else {
|
|
src_dir.to_path_buf()
|
|
};
|
|
let install_destdir =
|
|
crate::builder::install_destdir_path(&build_dir, destdir, flags.lib32_variant);
|
|
|
|
// Create destdir
|
|
fs::create_dir_all(destdir)?;
|
|
if flags.lib32_variant {
|
|
if install_destdir.exists() {
|
|
fs::remove_dir_all(&install_destdir).with_context(|| {
|
|
format!(
|
|
"Failed to clean temporary lib32 install dir: {}",
|
|
install_destdir.display()
|
|
)
|
|
})?;
|
|
}
|
|
fs::create_dir_all(&install_destdir).with_context(|| {
|
|
format!(
|
|
"Failed to create temporary lib32 install dir: {}",
|
|
install_destdir.display()
|
|
)
|
|
})?;
|
|
}
|
|
|
|
let mut env_vars = crate::builder::standard_build_env(spec, cross, true, export_compiler_flags);
|
|
let shell_helpers = crate::shell_helpers::ShellHelpers::new(&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, the spec's build.sh is authoritative when present.
|
|
// Upstream source archives sometimes ship unrelated helper scripts with the
|
|
// same name, and the package spec needs to override them deterministically.
|
|
let build_script = src_dir.join("build.sh");
|
|
let spec_build = spec.spec_dir.join("build.sh");
|
|
if spec_build.exists() {
|
|
fs::create_dir_all(src_dir)?;
|
|
fs::copy(&spec_build, &build_script).with_context(|| {
|
|
format!(
|
|
"Failed to copy build.sh from spec dir: {}",
|
|
spec_build.display()
|
|
)
|
|
})?;
|
|
// Ensure executable bit
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
let mut perms = fs::metadata(&build_script)?.permissions();
|
|
perms.set_mode(0o755);
|
|
fs::set_permissions(&build_script, perms)?;
|
|
}
|
|
crate::log_info!("Using build.sh from spec dir: {}", spec_build.display());
|
|
}
|
|
|
|
if !build_script.exists() {
|
|
anyhow::bail!(
|
|
"Custom build type requires build.sh in source directory: {}",
|
|
src_dir.display()
|
|
);
|
|
}
|
|
|
|
use crate::builder::state::{BuildStep, StateTracker};
|
|
let mut state = StateTracker::new_with_namespace(
|
|
src_dir,
|
|
spec.build.flags.lib32_variant.then_some("lib32"),
|
|
)?;
|
|
|
|
if !state.is_done(BuildStep::Configured) {
|
|
hooks::run_post_configure_commands(spec, src_dir, destdir)?;
|
|
state.mark_done(BuildStep::Configured)?;
|
|
} else {
|
|
crate::log_info!("Skipping post-configure hooks (already done)");
|
|
}
|
|
|
|
if !state.is_done(BuildStep::PostInstallDone) {
|
|
crate::builder::set_env_var(
|
|
&mut env_vars,
|
|
"DESTDIR",
|
|
install_destdir.to_string_lossy().into_owned(),
|
|
);
|
|
crate::builder::set_env_var(
|
|
&mut env_vars,
|
|
"DEPOT_PRIMARY_DESTDIR",
|
|
install_destdir.to_string_lossy().into_owned(),
|
|
);
|
|
let starbuild_workdir = if spec.sources().is_empty() {
|
|
src_dir.to_path_buf()
|
|
} else {
|
|
src_dir
|
|
.parent()
|
|
.map(Path::to_path_buf)
|
|
.unwrap_or_else(|| src_dir.to_path_buf())
|
|
};
|
|
crate::builder::set_env_var(
|
|
&mut env_vars,
|
|
"DEPOT_STARBUILD_WORKDIR",
|
|
starbuild_workdir.to_string_lossy().into_owned(),
|
|
);
|
|
add_output_destdir_envs(spec, &install_destdir, &mut env_vars);
|
|
|
|
// Ensure build script path is absolute for when we are in a sub-build-dir
|
|
let abs_build_script = if build_script.is_absolute() {
|
|
build_script.clone()
|
|
} else {
|
|
std::env::current_dir()?.join(&build_script)
|
|
};
|
|
|
|
let function_mode = custom_function_mode_enabled(&abs_build_script)?;
|
|
if function_mode {
|
|
crate::log_info!(
|
|
"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)"
|
|
);
|
|
run_function_mode_build_script(
|
|
spec,
|
|
&build_dir,
|
|
&install_destdir,
|
|
&abs_build_script,
|
|
&env_vars,
|
|
)?;
|
|
} else {
|
|
crate::log_info!(
|
|
"Running custom build script{}...",
|
|
if fakeroot::is_root() {
|
|
""
|
|
} else {
|
|
" (with internal fakeroot)"
|
|
}
|
|
);
|
|
// Use POSIX `sh` (doing something wrong if your system doesn't have it...)
|
|
let mut cmd = fakeroot::wrap_install_command("sh", &install_destdir)?;
|
|
let wrapper = crate::shell_helpers::wrap_shell_command(". \"$1\"");
|
|
// Run custom scripts through `sh -c` so helper commands like `haul`
|
|
// work even when the helper scripts live on a `noexec` mount.
|
|
cmd.arg("-eu")
|
|
.arg("-c")
|
|
.arg(wrapper)
|
|
.arg("sh")
|
|
.arg(&abs_build_script);
|
|
cmd.current_dir(&build_dir);
|
|
|
|
crate::builder::prepare_tool_command(&mut cmd, &env_vars);
|
|
|
|
// Run the command and include the OS error on spawn failures for clearer diagnostics
|
|
let status = crate::interrupts::command_status(&mut cmd).map_err(|e| {
|
|
anyhow::anyhow!(
|
|
"Failed to run build script {}: {}",
|
|
build_script.display(),
|
|
e
|
|
)
|
|
})?;
|
|
|
|
if !status.success() {
|
|
anyhow::bail!("Custom build script failed with status: {}", status);
|
|
}
|
|
}
|
|
if flags.lib32_variant {
|
|
crate::builder::stage_lib32_install_tree(&install_destdir, destdir)?;
|
|
}
|
|
state.mark_done(BuildStep::PostInstallDone)?;
|
|
} else {
|
|
crate::log_info!("Skipping custom build script (already done)");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn add_output_destdir_envs(
|
|
spec: &PackageSpec,
|
|
destdir: &Path,
|
|
env_vars: &mut crate::builder::EnvVars,
|
|
) {
|
|
for out in spec.outputs() {
|
|
let out_destdir = if out.name == spec.package.name {
|
|
destdir.to_path_buf()
|
|
} else {
|
|
crate::staging::output_staging_dir(destdir, &out.name)
|
|
};
|
|
let suffix = crate::shell_helpers::shell_ident_suffix(&out.name);
|
|
crate::builder::set_env_var(
|
|
env_vars,
|
|
&format!("DEPOT_SUBDESTDIR_{suffix}"),
|
|
out_destdir.to_string_lossy().into_owned(),
|
|
);
|
|
}
|
|
}
|
|
|
|
fn custom_function_mode_enabled(build_script: &Path) -> Result<bool> {
|
|
let contents = fs::read_to_string(build_script)
|
|
.with_context(|| format!("Failed to read build script: {}", build_script.display()))?;
|
|
Ok(contents.contains("depot_build()")
|
|
|| contents.contains("depot_install()")
|
|
|| contents.contains("depot_install_")
|
|
|| contents.contains("install_"))
|
|
}
|
|
|
|
fn function_mode_shell_prelude() -> String {
|
|
let mut wrapper = crate::shell_helpers::wrap_shell_command("");
|
|
wrapper.push_str("\nset -eu\n");
|
|
wrapper.push_str("depot_has_function() {\n");
|
|
wrapper.push_str(" case \"$(type \"$1\" 2>/dev/null || :)\" in\n");
|
|
wrapper.push_str(" *function*) return 0 ;;\n");
|
|
wrapper.push_str(" *) return 1 ;;\n");
|
|
wrapper.push_str(" esac\n");
|
|
wrapper.push_str("}\n");
|
|
wrapper
|
|
}
|
|
|
|
fn build_function_mode_build_command(build_script: &Path, state_file: &Path) -> Command {
|
|
let mut wrapper = function_mode_shell_prelude();
|
|
wrapper.push_str("depot_build_ran=0\n");
|
|
wrapper.push_str(". \"$1\"\n");
|
|
wrapper.push_str("if depot_has_function depot_build; then depot_build; depot_build_ran=1;\n");
|
|
wrapper.push_str("elif depot_has_function build; then build; depot_build_ran=1;\n");
|
|
wrapper.push_str("fi\n");
|
|
wrapper.push_str("printf '%s\\n' \"$depot_build_ran\" > \"$2\"\n");
|
|
|
|
let mut cmd = Command::new("sh");
|
|
cmd.arg("-c")
|
|
.arg(wrapper)
|
|
.arg("sh")
|
|
.arg(build_script)
|
|
.arg(state_file);
|
|
cmd
|
|
}
|
|
|
|
fn build_function_mode_install_command(
|
|
spec: &PackageSpec,
|
|
destdir: &Path,
|
|
build_script: &Path,
|
|
build_ran: bool,
|
|
) -> Result<Command> {
|
|
let mut wrapper = function_mode_shell_prelude();
|
|
wrapper.push_str(&format!(
|
|
"depot_build_ran={}\n",
|
|
if build_ran { 1 } else { 0 }
|
|
));
|
|
wrapper.push_str(". \"$1\"\n");
|
|
|
|
let primary = &spec.package.name;
|
|
for out in spec.outputs() {
|
|
let out_destdir = if out.name == *primary {
|
|
destdir.to_path_buf()
|
|
} else {
|
|
crate::staging::output_staging_dir(destdir, &out.name)
|
|
};
|
|
let fn_suffix = shell_fn_suffix(&out.name);
|
|
let q_name = sh_single_quote(&out.name);
|
|
let q_dest = sh_single_quote(&out_destdir.to_string_lossy());
|
|
|
|
wrapper.push_str(&format!(
|
|
"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");
|
|
if out.name == *primary {
|
|
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");
|
|
wrapper.push_str(
|
|
" echo \"depot: missing install handler for output '$DEPOT_OUTPUT_NAME' in custom function mode\" >&2\n",
|
|
);
|
|
wrapper.push_str(" exit 1\n");
|
|
wrapper.push_str("fi\n");
|
|
}
|
|
|
|
let mut cmd = fakeroot::wrap_install_command("sh", destdir)?;
|
|
cmd.arg("-c").arg(wrapper).arg("sh").arg(build_script);
|
|
Ok(cmd)
|
|
}
|
|
|
|
fn run_function_mode_build_script(
|
|
spec: &PackageSpec,
|
|
build_dir: &Path,
|
|
install_destdir: &Path,
|
|
build_script: &Path,
|
|
env_vars: &crate::builder::EnvVars,
|
|
) -> Result<()> {
|
|
let state_file = NamedTempFile::new_in(build_dir).with_context(|| {
|
|
format!(
|
|
"Failed to create function mode state file in {}",
|
|
build_dir.display()
|
|
)
|
|
})?;
|
|
let state_path = state_file.path().to_path_buf();
|
|
|
|
let mut build_cmd = build_function_mode_build_command(build_script, &state_path);
|
|
build_cmd.current_dir(build_dir);
|
|
let mut build_env = env_vars.clone();
|
|
crate::builder::set_env_var(
|
|
&mut build_env,
|
|
"DESTDIR",
|
|
install_destdir.to_string_lossy().into_owned(),
|
|
);
|
|
crate::builder::prepare_tool_command(&mut build_cmd, &build_env);
|
|
let build_status = crate::interrupts::command_status(&mut build_cmd).map_err(|e| {
|
|
anyhow::anyhow!(
|
|
"Failed to run build script {}: {}",
|
|
build_script.display(),
|
|
e
|
|
)
|
|
})?;
|
|
if !build_status.success() {
|
|
anyhow::bail!("Custom build script failed with status: {}", build_status);
|
|
}
|
|
|
|
let build_ran = fs::read_to_string(&state_path)
|
|
.ok()
|
|
.map(|value| value.trim() == "1")
|
|
.unwrap_or(false);
|
|
|
|
let mut install_cmd =
|
|
build_function_mode_install_command(spec, install_destdir, build_script, build_ran)?;
|
|
install_cmd.current_dir(build_dir);
|
|
crate::builder::prepare_tool_command(&mut install_cmd, env_vars);
|
|
let install_status = crate::interrupts::command_status(&mut install_cmd).map_err(|e| {
|
|
anyhow::anyhow!(
|
|
"Failed to run build script {}: {}",
|
|
build_script.display(),
|
|
e
|
|
)
|
|
})?;
|
|
if !install_status.success() {
|
|
anyhow::bail!("Custom build script failed with status: {}", install_status);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn shell_fn_suffix(pkg_name: &str) -> String {
|
|
let mut out = String::with_capacity(pkg_name.len().max(1));
|
|
for ch in pkg_name.chars() {
|
|
if ch.is_ascii_alphanumeric() || ch == '_' {
|
|
out.push(ch);
|
|
} else {
|
|
out.push('_');
|
|
}
|
|
}
|
|
if out.is_empty() {
|
|
out.push('_');
|
|
}
|
|
if out.as_bytes().first().is_some_and(|b| b.is_ascii_digit()) {
|
|
out.insert(0, '_');
|
|
}
|
|
out
|
|
}
|
|
|
|
fn sh_single_quote(s: &str) -> String {
|
|
s.replace('\'', "'\"'\"'")
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::package::{Build, BuildFlags, BuildType, Dependencies, PackageInfo};
|
|
use tempfile::tempdir;
|
|
|
|
fn mk_spec(name: &str, version: &str) -> PackageSpec {
|
|
PackageSpec {
|
|
package: PackageInfo {
|
|
name: name.into(),
|
|
real_name: None,
|
|
version: version.into(),
|
|
revision: 1,
|
|
description: "d".into(),
|
|
homepage: "h".into(),
|
|
abi_breaking: false,
|
|
built_against: Vec::new(),
|
|
license: vec!["MIT".into()],
|
|
},
|
|
packages: Vec::new(),
|
|
alternatives: Default::default(),
|
|
manual_sources: Vec::new(),
|
|
source: vec![crate::package::Source {
|
|
url: "h".into(),
|
|
sha256: "s".into(),
|
|
extract_dir: "e".into(),
|
|
patches: Vec::new(),
|
|
post_extract: Vec::new(),
|
|
cherry_pick: Vec::new(),
|
|
}],
|
|
build: Build {
|
|
build_type: BuildType::Custom,
|
|
flags: BuildFlags::default(),
|
|
},
|
|
dependencies: Dependencies::default(),
|
|
package_alternatives: Default::default(),
|
|
package_dependencies: Default::default(),
|
|
spec_dir: std::path::PathBuf::from("."),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_errors_without_build_sh() -> Result<()> {
|
|
let tmp_src = tempdir()?;
|
|
let tmp_dest = tempdir()?;
|
|
|
|
let spec = mk_spec("custom-no-build", "1.0");
|
|
|
|
let res = build(&spec, tmp_src.path(), tmp_dest.path(), None, true, None);
|
|
assert!(res.is_err());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_uses_build_sh_from_spec_dir() -> Result<()> {
|
|
let tmp_src = tempdir()?;
|
|
let tmp_dest = tempdir()?;
|
|
let spec_dir = tempdir()?;
|
|
|
|
// write a no-op build.sh into spec_dir
|
|
let build_sh = spec_dir.path().join("build.sh");
|
|
std::fs::write(&build_sh, "#!/bin/sh\nexit 0\n")?;
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
let mut perms = std::fs::metadata(&build_sh)?.permissions();
|
|
perms.set_mode(0o755);
|
|
std::fs::set_permissions(&build_sh, perms)?;
|
|
}
|
|
|
|
let mut spec = mk_spec("custom-from-spec", "1.0");
|
|
spec.spec_dir = spec_dir.path().to_path_buf();
|
|
|
|
// src_dir is empty; build() should copy build.sh from spec_dir and run it (no-op)
|
|
build(&spec, tmp_src.path(), tmp_dest.path(), None, true, None)?;
|
|
// If we reached here, build() succeeded and build.sh was copied into src
|
|
assert!(tmp_src.path().join("build.sh").exists());
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_prefers_spec_build_sh_over_source_build_sh() -> Result<()> {
|
|
let tmp_src = tempdir()?;
|
|
let tmp_dest = tempdir()?;
|
|
let spec_dir = tempdir()?;
|
|
|
|
let source_build_sh = tmp_src.path().join("build.sh");
|
|
std::fs::write(&source_build_sh, "#!/bin/sh\nexit 77\n")?;
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
let mut perms = std::fs::metadata(&source_build_sh)?.permissions();
|
|
perms.set_mode(0o755);
|
|
std::fs::set_permissions(&source_build_sh, perms)?;
|
|
}
|
|
|
|
let spec_build_sh = spec_dir.path().join("build.sh");
|
|
std::fs::write(
|
|
&spec_build_sh,
|
|
"#!/bin/sh\nprintf 'from spec\\n' > \"$DESTDIR/spec-won\"\n",
|
|
)?;
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
let mut perms = std::fs::metadata(&spec_build_sh)?.permissions();
|
|
perms.set_mode(0o755);
|
|
std::fs::set_permissions(&spec_build_sh, perms)?;
|
|
}
|
|
|
|
let mut spec = mk_spec("custom-prefer-spec", "1.0");
|
|
spec.spec_dir = spec_dir.path().to_path_buf();
|
|
|
|
build(&spec, tmp_src.path(), tmp_dest.path(), None, true, None)?;
|
|
|
|
assert_eq!(
|
|
std::fs::read_to_string(tmp_dest.path().join("spec-won"))?,
|
|
"from spec\n"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_function_mode_uses_per_output_destdirs() -> Result<()> {
|
|
let tmp_src = tempdir()?;
|
|
let tmp_dest = tempdir()?;
|
|
|
|
let build_sh = tmp_src.path().join("build.sh");
|
|
std::fs::write(
|
|
&build_sh,
|
|
r#"#!/bin/sh
|
|
depot_build() {
|
|
:
|
|
}
|
|
depot_install() {
|
|
mkdir -p "$DESTDIR/usr/share"
|
|
echo primary > "$DESTDIR/usr/share/primary.txt"
|
|
}
|
|
depot_install_dev_pkg() {
|
|
mkdir -p "$DESTDIR/usr/include"
|
|
echo header > "$DESTDIR/usr/include/dev.h"
|
|
}
|
|
"#,
|
|
)?;
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
let mut perms = std::fs::metadata(&build_sh)?.permissions();
|
|
perms.set_mode(0o755);
|
|
std::fs::set_permissions(&build_sh, perms)?;
|
|
}
|
|
|
|
let mut spec = mk_spec("demo", "1.0");
|
|
spec.packages.push(crate::package::PackageInfo {
|
|
name: "dev-pkg".into(),
|
|
real_name: None,
|
|
version: "1.0".into(),
|
|
revision: 1,
|
|
description: "d".into(),
|
|
homepage: "h".into(),
|
|
abi_breaking: false,
|
|
built_against: Vec::new(),
|
|
license: vec!["MIT".into()],
|
|
});
|
|
|
|
build(&spec, tmp_src.path(), tmp_dest.path(), None, true, None)?;
|
|
|
|
assert!(tmp_dest.path().join("usr/share/primary.txt").exists());
|
|
assert!(
|
|
tmp_dest
|
|
.path()
|
|
.join(".depot/outputs/dev-pkg/usr/include/dev.h")
|
|
.exists()
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_function_mode_only_uses_fakeroot_for_install() -> Result<()> {
|
|
let tmp_src = tempdir()?;
|
|
let tmp_dest = tempdir()?;
|
|
|
|
let build_sh = tmp_src.path().join("build.sh");
|
|
std::fs::write(
|
|
&build_sh,
|
|
r#"#!/bin/sh
|
|
depot_build() {
|
|
id -u > build-uid.txt
|
|
}
|
|
depot_install() {
|
|
mkdir -p "$DESTDIR/usr/share"
|
|
id -u > "$DESTDIR/usr/share/install-uid.txt"
|
|
}
|
|
"#,
|
|
)?;
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
let mut perms = std::fs::metadata(&build_sh)?.permissions();
|
|
perms.set_mode(0o755);
|
|
std::fs::set_permissions(&build_sh, perms)?;
|
|
}
|
|
|
|
let spec = mk_spec("custom-function-fakeroot-split", "1.0");
|
|
build(&spec, tmp_src.path(), tmp_dest.path(), None, true, None)?;
|
|
|
|
assert_eq!(
|
|
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-uid.txt"))?,
|
|
"0\n"
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_function_mode_commands_split_fakeroot_boundary() -> Result<()> {
|
|
let tmp_dest = tempdir()?;
|
|
let build_script = Path::new("/tmp/build.sh");
|
|
let state_file = Path::new("/tmp/build-state");
|
|
let install_destdir = tmp_dest.path();
|
|
|
|
let build_cmd = build_function_mode_build_command(build_script, state_file);
|
|
assert_eq!(build_cmd.get_program(), std::ffi::OsStr::new("sh"));
|
|
|
|
let spec = mk_spec("custom-function-fakeroot-split", "1.0");
|
|
let install_cmd =
|
|
build_function_mode_install_command(&spec, install_destdir, build_script, true)?;
|
|
assert_eq!(install_cmd.get_program(), std::ffi::OsStr::new("sh"));
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_function_mode_errors_when_output_handler_missing() -> Result<()> {
|
|
let tmp_src = tempdir()?;
|
|
let tmp_dest = tempdir()?;
|
|
|
|
let build_sh = tmp_src.path().join("build.sh");
|
|
std::fs::write(
|
|
&build_sh,
|
|
r#"#!/bin/sh
|
|
depot_build() {
|
|
:
|
|
}
|
|
depot_install() {
|
|
mkdir -p "$DESTDIR/usr/share"
|
|
echo primary > "$DESTDIR/usr/share/primary.txt"
|
|
}
|
|
"#,
|
|
)?;
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
let mut perms = std::fs::metadata(&build_sh)?.permissions();
|
|
perms.set_mode(0o755);
|
|
std::fs::set_permissions(&build_sh, perms)?;
|
|
}
|
|
|
|
let mut spec = mk_spec("demo", "1.0");
|
|
spec.packages.push(crate::package::PackageInfo {
|
|
name: "dev-pkg".into(),
|
|
real_name: None,
|
|
version: "1.0".into(),
|
|
revision: 1,
|
|
description: "d".into(),
|
|
homepage: "h".into(),
|
|
abi_breaking: false,
|
|
built_against: Vec::new(),
|
|
license: vec!["MIT".into()],
|
|
});
|
|
|
|
let err = build(&spec, tmp_src.path(), tmp_dest.path(), None, true, None)
|
|
.expect_err("missing per-output install handler should fail");
|
|
assert!(err.to_string().contains("Custom build script failed"));
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_non_function_mode_stops_on_first_command_failure() -> Result<()> {
|
|
let tmp_src = tempdir()?;
|
|
let tmp_dest = tempdir()?;
|
|
|
|
let build_sh = tmp_src.path().join("build.sh");
|
|
std::fs::write(
|
|
&build_sh,
|
|
r#"#!/bin/sh
|
|
false
|
|
exit 0
|
|
"#,
|
|
)?;
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
let mut perms = std::fs::metadata(&build_sh)?.permissions();
|
|
perms.set_mode(0o755);
|
|
std::fs::set_permissions(&build_sh, perms)?;
|
|
}
|
|
|
|
let spec = mk_spec("custom-fail-fast", "1.0");
|
|
let err = build(&spec, tmp_src.path(), tmp_dest.path(), None, true, None)
|
|
.expect_err("non-function custom scripts should fail when a command fails");
|
|
assert!(err.to_string().contains("Custom build script failed"));
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn test_build_lib32_stages_only_usr_lib_payload() -> Result<()> {
|
|
let tmp_src = tempdir()?;
|
|
let tmp_dest = tempdir()?;
|
|
|
|
let build_sh = tmp_src.path().join("build.sh");
|
|
std::fs::write(
|
|
&build_sh,
|
|
r#"#!/bin/sh
|
|
mkdir -p "$DESTDIR/usr/lib" "$DESTDIR/usr/share/man/man1"
|
|
printf 'lib32' > "$DESTDIR/usr/lib/libfoo.so.1"
|
|
printf 'manpage' > "$DESTDIR/usr/share/man/man1/foo.1"
|
|
"#,
|
|
)?;
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::PermissionsExt;
|
|
let mut perms = std::fs::metadata(&build_sh)?.permissions();
|
|
perms.set_mode(0o755);
|
|
std::fs::set_permissions(&build_sh, perms)?;
|
|
}
|
|
|
|
let mut spec = mk_spec("custom-lib32", "1.0");
|
|
spec.build.flags.lib32_variant = true;
|
|
|
|
build(&spec, tmp_src.path(), tmp_dest.path(), None, true, None)?;
|
|
|
|
assert_eq!(
|
|
std::fs::read_to_string(tmp_dest.path().join("usr/lib32/libfoo.so.1"))?,
|
|
"lib32"
|
|
);
|
|
assert!(!tmp_dest.path().join("usr/share/man/man1/foo.1").exists());
|
|
assert!(!tmp_dest.path().join("usr/lib").exists());
|
|
|
|
Ok(())
|
|
}
|
|
}
|