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, ) -> Result<(), Box> { 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> { // 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) }