106 lines
3.3 KiB
Rust
106 lines
3.3 KiB
Rust
mod cross;
|
|
mod local;
|
|
mod sbuild;
|
|
|
|
use crate::context;
|
|
use std::error::Error;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
#[derive(PartialEq)]
|
|
pub enum BuildMode {
|
|
Sbuild,
|
|
Local,
|
|
}
|
|
|
|
pub fn build_binary_package(
|
|
arch: Option<&str>,
|
|
series: Option<&str>,
|
|
cwd: Option<&Path>,
|
|
cross: bool,
|
|
mode: Option<BuildMode>,
|
|
) -> Result<(), Box<dyn Error>> {
|
|
let cwd = cwd.unwrap_or_else(|| Path::new("."));
|
|
|
|
// Parse changelog to get package name, version and series
|
|
let changelog_path = cwd.join("debian/changelog");
|
|
let (package, version, package_series) =
|
|
crate::changelog::parse_changelog_header(&changelog_path)?;
|
|
let series = if let Some(s) = series {
|
|
s
|
|
} else {
|
|
&package_series
|
|
};
|
|
let current_arch = crate::get_current_arch();
|
|
let arch = arch.unwrap_or(¤t_arch);
|
|
|
|
// Make sure we select a specific mode, either using user-requested
|
|
// or by using default for user-supplied parameters
|
|
let mode = if let Some(m) = mode {
|
|
m
|
|
} else {
|
|
// For cross-compilation, we use local with an ephemeral context
|
|
// created by the cross-compilation handler (see below)
|
|
if cross {
|
|
BuildMode::Local
|
|
} else {
|
|
// By default, we use sbuild
|
|
BuildMode::Sbuild
|
|
}
|
|
};
|
|
|
|
// Specific case: native cross-compilation, we don't allow that
|
|
// instead this wraps to an automatic unshare chroot
|
|
// using an ephemeral context
|
|
let _guard = if cross && mode == BuildMode::Local {
|
|
Some(cross::EphemeralContextGuard::new(series)?)
|
|
} else {
|
|
None
|
|
};
|
|
|
|
// Prepare build directory
|
|
let ctx = context::current();
|
|
let build_root = ctx.create_temp_dir()?;
|
|
|
|
// Ensure availability of all needed files for the build
|
|
let parent_dir = cwd.parent().ok_or("Cannot find parent directory")?;
|
|
ctx.ensure_available(parent_dir, &build_root)?;
|
|
let parent_dir_name = parent_dir
|
|
.file_name()
|
|
.ok_or("Cannot find parent directory name")?;
|
|
let build_root = format!("{}/{}", build_root, parent_dir_name.to_str().unwrap());
|
|
|
|
// Run the build using target build mode
|
|
match mode {
|
|
BuildMode::Local => local::build(&package, &version, arch, series, &build_root, cross)?,
|
|
BuildMode::Sbuild => sbuild::build(&package, &version, arch, series, &build_root, cross)?,
|
|
};
|
|
|
|
// Retrieve produced .deb files
|
|
let remote_files = ctx.list_files(Path::new(&build_root))?;
|
|
for remote_file in remote_files {
|
|
if remote_file.extension().is_some_and(|ext| ext == "deb") {
|
|
let file_name = remote_file.file_name().ok_or("Invalid remote filename")?;
|
|
let local_dest = parent_dir.join(file_name);
|
|
ctx.retrieve_path(&remote_file, &local_dest)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn find_dsc_file(
|
|
build_root: &str,
|
|
package: &str,
|
|
version: &str,
|
|
) -> Result<PathBuf, Box<dyn Error>> {
|
|
// Strip epoch from version (e.g., "1:2.3.4-5" -> "2.3.4-5")
|
|
let version_without_epoch = version.split_once(':').map(|(_, v)| v).unwrap_or(version);
|
|
let dsc_name = format!("{}_{}.dsc", package, version_without_epoch);
|
|
let dsc_path = PathBuf::from(build_root).join(&dsc_name);
|
|
|
|
if !dsc_path.exists() {
|
|
return Err(format!("Could not find .dsc file at {}", dsc_path.display()).into());
|
|
}
|
|
Ok(dsc_path)
|
|
}
|