exp: cross #3
This commit is contained in:
@@ -78,12 +78,20 @@ impl ContextDriver for UnshareDriver {
|
||||
))
|
||||
}
|
||||
|
||||
fn list_files(&self, _path: &Path) -> io::Result<Vec<PathBuf>> {
|
||||
// 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<Vec<PathBuf>> {
|
||||
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);
|
||||
|
||||
121
src/deb/cross.rs
121
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<dyn Error>> {
|
||||
// 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<dyn Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clean_native_context() -> Result<(), Box<dyn Error>> {
|
||||
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<String, String>,
|
||||
@@ -109,6 +123,23 @@ pub fn ensure_repositories(arch: &str, series: &str) -> Result<(), Box<dyn Error
|
||||
.arg(deb822_path)
|
||||
.status()?;
|
||||
|
||||
// Ensure all components are enabled for the primary architecture
|
||||
ctx.command("sed")
|
||||
.arg("-i")
|
||||
.arg("/URIs:.*\\(archive\\|security\\)\\.ubuntu\\.com/,/Components:/ s/^Components:.*/Components: main restricted universe multiverse/")
|
||||
.arg(deb822_path)
|
||||
.status()?;
|
||||
|
||||
// Ensure all suites (pockets) are enabled for the primary architecture
|
||||
let suites = format!(
|
||||
"{series} {series}-updates {series}-backports {series}-security {series}-proposed"
|
||||
);
|
||||
ctx.command("sed")
|
||||
.arg("-i")
|
||||
.arg(format!("/URIs:.*\\(archive\\|security\\)\\.ubuntu\\.com/,/Suites:/ s/^Suites:.*/Suites: {}/", suites))
|
||||
.arg(deb822_path)
|
||||
.status()?;
|
||||
|
||||
// Add ports if not already present
|
||||
let has_ports = ctx
|
||||
.command("grep")
|
||||
@@ -130,17 +161,46 @@ pub fn ensure_repositories(arch: &str, series: &str) -> Result<(), Box<dyn Error
|
||||
} else {
|
||||
// Traditional sources.list
|
||||
let sources_path = "/etc/apt/sources.list";
|
||||
|
||||
|
||||
// Scope archive.ubuntu.com and security.ubuntu.com to amd64 if not already scoped
|
||||
// 1. For lines without [], insert [arch=amd64]
|
||||
// 2. For lines with [], insert arch=amd64 inside the brackets
|
||||
// Both only if arch= is not already present on the line
|
||||
ctx.command("sed")
|
||||
.arg("-i")
|
||||
.arg(r"/archive.ubuntu.com\|security.ubuntu.com/ { /arch=/ ! { /^deb \[/ ! s/^deb /deb [arch=amd64] /; /^deb \[/ s/^deb \[\([^]]*\)\]/deb [arch=amd64 \1]/ } }")
|
||||
.arg(sources_path)
|
||||
.status()?;
|
||||
|
||||
// Ensure all components (main restricted universe multiverse) are present for all archive/security lines
|
||||
// We match any combination of components at the end and replace with the full set
|
||||
ctx.command("sed")
|
||||
.arg("-i")
|
||||
.arg(r"/archive.ubuntu.com\|security.ubuntu.com/ s/\( main\)\?\([ ]\+restricted\)\?\([ ]\+universe\)\?\([ ]\+multiverse\)\?$/ main restricted universe multiverse/")
|
||||
.arg(sources_path)
|
||||
.status()?;
|
||||
|
||||
// Ensure all pockets exist. If not, we append them.
|
||||
// We ignore 'proposed' as it contains unstable software
|
||||
for pocket in ["", "-updates", "-backports", "-security"] {
|
||||
let suite = format!("{}{}", series, pocket);
|
||||
let has_suite = ctx
|
||||
.command("grep")
|
||||
.arg("-q")
|
||||
.arg(format!(" {}", suite))
|
||||
.arg(sources_path)
|
||||
.status()?
|
||||
.success();
|
||||
|
||||
if !has_suite {
|
||||
let line = format!(
|
||||
"deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ {} main restricted universe multiverse",
|
||||
suite
|
||||
);
|
||||
ctx.command("sh")
|
||||
.arg("-c")
|
||||
.arg(format!("echo '{}' >> {}", 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<dyn Error
|
||||
"deb [arch={arch}] http://ports.ubuntu.com/ubuntu-ports {series} main restricted universe multiverse\n\
|
||||
deb [arch={arch}] http://ports.ubuntu.com/ubuntu-ports {series}-updates main restricted universe multiverse\n\
|
||||
deb [arch={arch}] http://ports.ubuntu.com/ubuntu-ports {series}-backports main restricted universe multiverse\n\
|
||||
deb [arch={arch}] http://ports.ubuntu.com/ubuntu-ports {series}-security main restricted universe multiverse\n\
|
||||
deb [arch={arch}] http://ports.ubuntu.com/ubuntu-ports {series}-proposed main restricted universe multiverse"
|
||||
deb [arch={arch}] http://ports.ubuntu.com/ubuntu-ports {series}-security main restricted universe multiverse"
|
||||
);
|
||||
ctx.command("sh")
|
||||
.arg("-c")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/// Local binary package building
|
||||
/// Directly calling 'debian/rules' in current context
|
||||
use crate::context;
|
||||
use crate::deb::find_dsc_file;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::path::Path;
|
||||
@@ -8,9 +9,8 @@ use std::path::Path;
|
||||
use crate::deb::cross;
|
||||
|
||||
pub fn build(
|
||||
_cwd: &Path,
|
||||
package: &str,
|
||||
_version: &str,
|
||||
version: &str,
|
||||
arch: &str,
|
||||
series: &str,
|
||||
build_root: &str,
|
||||
@@ -19,6 +19,7 @@ pub fn build(
|
||||
// Environment
|
||||
let mut env = HashMap::<String, String>::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<dyn Error>> {
|
||||
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(())
|
||||
}
|
||||
|
||||
@@ -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<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)
|
||||
}
|
||||
|
||||
@@ -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<PathBuf, Box<dyn Error>> {
|
||||
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<dyn Error>> {
|
||||
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");
|
||||
|
||||
Reference in New Issue
Block a user