diff --git a/src/context/unshare.rs b/src/context/unshare.rs index e6efd0c..110e8a5 100644 --- a/src/context/unshare.rs +++ b/src/context/unshare.rs @@ -78,12 +78,20 @@ impl ContextDriver for UnshareDriver { )) } - fn list_files(&self, _path: &Path) -> io::Result> { - // TODO: Implement chroot file listing logic - Err(io::Error::new( - io::ErrorKind::Unsupported, - "list_files not yet implemented for chroot", - )) + fn list_files(&self, path: &Path) -> io::Result> { + let host_path = Path::new(&self.path).join(path.to_string_lossy().trim_start_matches('/')); + let host_entries = self.parent().list_files(&host_path)?; + + let mut entries = Vec::new(); + let prefix = Path::new(&self.path); + for entry in host_entries { + if let Ok(rel_path) = entry.strip_prefix(prefix) { + entries.push(Path::new("/").join(rel_path)); + } else { + entries.push(entry); + } + } + Ok(entries) } fn run( @@ -164,7 +172,10 @@ impl UnshareDriver { let mut cmd = self.parent().command("sudo"); cmd.args(env.iter().map(|(k, v)| format!("{k}={v}"))); - cmd.arg("unshare").arg("-R").arg(&self.path); + cmd.arg("unshare") + .arg("--mount-proc") + .arg("-R") + .arg(&self.path); if let Some(dir) = cwd { cmd.arg("-w").arg(dir); diff --git a/src/deb/cross.rs b/src/deb/cross.rs index 1a39302..1766ecd 100644 --- a/src/deb/cross.rs +++ b/src/deb/cross.rs @@ -5,34 +5,28 @@ use std::error::Error; /// Setup a specific chroot context for native cross builds pub fn setup_native_context(series: &str) -> Result<(), Box> { - // Use the system cache directory to store the chroot - let proj_dirs = directories::ProjectDirs::from("com", "pkh", "pkh") - .expect("Could not determine cache directory"); - let cache_dir = proj_dirs.cache_dir(); - let chroot_path = cache_dir.join(format!("pkh-cross-{series}")); + // Create a temporary directory for the chroot + let chroot_path_str = context::current().create_temp_dir()?; + let chroot_path = std::path::PathBuf::from(chroot_path_str); - // Check if the chroot already exists - if !chroot_path.exists() { - log::debug!( - "Creating new chroot for {} at {}...", - series, - chroot_path.display() - ); - std::fs::create_dir_all(&chroot_path)?; + log::debug!( + "Creating new chroot for {} at {}...", + series, + chroot_path.display() + ); - let status = context::current() - .command("sudo") - .arg("mmdebstrap") - .arg("--variant=buildd") - .arg(series) - .arg(chroot_path.to_string_lossy().to_string()) - .status()?; + let status = context::current() + .command("sudo") + .arg("mmdebstrap") + .arg("--variant=buildd") + .arg(series) + .arg(chroot_path.to_string_lossy().to_string()) + .status()?; - if !status.success() { - // Clean up on failure - let _ = std::fs::remove_dir_all(&chroot_path); - return Err(format!("mmdebstrap failed for series {}", series).into()); - } + if !status.success() { + // Clean up on failure + let _ = std::fs::remove_dir_all(&chroot_path); + return Err(format!("mmdebstrap failed for series {}", series).into()); } // Switch to an ephemeral context to build the package in the chroot @@ -44,6 +38,26 @@ pub fn setup_native_context(series: &str) -> Result<(), Box> { Ok(()) } +pub fn clean_native_context() -> Result<(), Box> { + let ctx = context::current(); + if let ContextConfig::Unshare { path, .. } = &ctx.config { + let chroot_path = path.clone(); + + // Reset to normal context + context::manager().set_current(&context::manager().current_name())?; + + // Remove chroot directory + context::current() + .command("sudo") + .arg("rm") + .arg("-rf") + .arg(chroot_path) + .status()?; + } + + Ok(()) +} + /// Set environment variables for cross-compilation pub fn setup_environment( env: &mut HashMap, @@ -109,6 +123,23 @@ pub fn ensure_repositories(arch: &str, series: &str) -> Result<(), Box Result<(), Box> {}", line, sources_path)) + .status()?; + } + } + // Add ports repository to sources.list if not already present let has_ports = ctx .command("grep") @@ -155,8 +215,7 @@ pub fn ensure_repositories(arch: &str, series: &str) -> Result<(), Box::new(); env.insert("LANG".to_string(), "C".to_string()); + env.insert("DEBIAN_FRONTEND".to_string(), "noninteractive".to_string()); let ctx = context::current(); @@ -47,6 +48,7 @@ pub fn build( .arg("-y") .arg("install") .arg("build-essential") + .arg("dose-builddebcheck") .arg("fakeroot"); if cross { cmd.arg(format!("crossbuild-essential-{arch}")); @@ -66,7 +68,10 @@ pub fn build( cmd.arg(format!("--host-architecture={arch}")); } let status = cmd.arg("./").status()?; + + // If build-dep fails, we try to explain the failure using dose-debcheck if !status.success() { + dose3_explain_dependencies(package, version, arch, build_root, cross)?; return Err("Could not install build-dependencies for the build".into()); } @@ -97,3 +102,60 @@ pub fn build( Ok(()) } + +fn dose3_explain_dependencies( + package: &str, + version: &str, + arch: &str, + build_root: &str, + cross: bool, +) -> Result<(), Box> { + let ctx = context::current(); + + // Construct the list of Packages files + let mut bg_args = Vec::new(); + let mut cmd = ctx.command("apt-get"); + cmd.arg("indextargets") + .arg("--format") + .arg("$(FILENAME)") + .arg("Created-By: Packages"); + + let output = cmd.output()?; + if output.status.success() { + let filenames = String::from_utf8_lossy(&output.stdout); + for file in filenames.lines() { + let file = file.trim(); + if !file.is_empty() { + bg_args.push(file.to_string()); + } + } + } + + // Transform the dsc file into a 'Source' stanza (replacing 'Source' with 'Package') + let dsc_path = find_dsc_file(build_root, package, version)?; + let mut dsc_content = ctx.read_file(&dsc_path)?; + dsc_content = dsc_content.replace("Source", "Package"); + ctx.write_file( + Path::new(&format!("{build_root}/dsc-processed")), + &dsc_content, + )?; + + // Call dose-builddebcheck + let local_arch = crate::get_current_arch(); + let mut cmd = ctx.command("dose-builddebcheck"); + cmd.arg("--verbose") + .arg("--failures") + .arg("--explain") + .arg("--summary") + .arg(format!("--deb-native-arch={}", local_arch)); + + if cross { + cmd.arg(format!("--deb-host-arch={}", arch)) + .arg("--deb-profiles=cross") + .arg(format!("--deb-foreign-archs={}", arch)); + } + + cmd.args(bg_args).arg(format!("{build_root}/dsc-processed")); + cmd.status()?; + Ok(()) +} diff --git a/src/deb/mod.rs b/src/deb/mod.rs index 548736d..fc32dd6 100644 --- a/src/deb/mod.rs +++ b/src/deb/mod.rs @@ -4,7 +4,7 @@ mod sbuild; use crate::context; use std::error::Error; -use std::path::Path; +use std::path::{Path, PathBuf}; pub fn build_binary_package( arch: Option<&str>, @@ -47,9 +47,9 @@ pub fn build_binary_package( // Run sbuild if cross { - local::build(cwd, &package, &version, arch, series, &build_root, cross)?; + local::build(&package, &version, arch, series, &build_root, cross)?; } else { - sbuild::build(cwd, &package, &version, arch, series, &build_root, cross)?; + sbuild::build(&package, &version, arch, series, &build_root, cross)?; } // Retrieve artifacts @@ -66,5 +66,25 @@ pub fn build_binary_package( } } + if cross { + cross::clean_native_context()?; + } + 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) +} diff --git a/src/deb/sbuild.rs b/src/deb/sbuild.rs index a8fbaad..89bf9f0 100644 --- a/src/deb/sbuild.rs +++ b/src/deb/sbuild.rs @@ -1,24 +1,10 @@ /// Sbuild binary package building /// Call 'sbuild' with the dsc file to build the package with unshare use crate::context; +use crate::deb::find_dsc_file; use std::error::Error; -use std::path::{Path, PathBuf}; - -fn find_dsc_file(cwd: &Path, package: &str, version: &str) -> Result> { - let parent = cwd.parent().ok_or("Cannot find parent directory")?; - // 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 = parent.join(&dsc_name); - - if !dsc_path.exists() { - return Err(format!("Could not find .dsc file at {}", dsc_path.display()).into()); - } - Ok(dsc_path) -} pub fn build( - cwd: &Path, package: &str, version: &str, arch: &str, @@ -26,7 +12,7 @@ pub fn build( build_root: &str, cross: bool, ) -> Result<(), Box> { - let dsc_path = find_dsc_file(cwd, package, version)?; + let dsc_path = find_dsc_file(build_root, package, version)?; let ctx = context::current(); let mut cmd = ctx.command("sbuild");