exp: cross #5
This commit is contained in:
348
src/deb/cross.rs
348
src/deb/cross.rs
@@ -3,59 +3,89 @@ use crate::context::{Context, ContextConfig};
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
|
||||
/// Setup a specific chroot context for native cross builds
|
||||
pub fn setup_native_context(series: &str) -> Result<(), Box<dyn Error>> {
|
||||
// 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);
|
||||
use std::path::PathBuf;
|
||||
|
||||
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()?;
|
||||
|
||||
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
|
||||
context::manager().set_current_ephemeral(Context::new(ContextConfig::Unshare {
|
||||
path: chroot_path.to_string_lossy().to_string(),
|
||||
parent: Some(context::manager().current_name()),
|
||||
}));
|
||||
|
||||
Ok(())
|
||||
pub struct EphemeralContextGuard {
|
||||
previous_context: String,
|
||||
chroot_path: PathBuf,
|
||||
}
|
||||
|
||||
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();
|
||||
impl EphemeralContextGuard {
|
||||
pub fn new(series: &str) -> Result<Self, Box<dyn Error>> {
|
||||
let current_context_name = context::manager().current_name();
|
||||
|
||||
// Create a temporary directory for the chroot
|
||||
let chroot_path_str = context::current().create_temp_dir()?;
|
||||
let chroot_path = PathBuf::from(chroot_path_str);
|
||||
|
||||
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()?;
|
||||
|
||||
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
|
||||
context::manager().set_current_ephemeral(Context::new(ContextConfig::Unshare {
|
||||
path: chroot_path.to_string_lossy().to_string(),
|
||||
parent: Some(current_context_name.clone()),
|
||||
}));
|
||||
|
||||
Ok(Self {
|
||||
previous_context: current_context_name,
|
||||
chroot_path,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EphemeralContextGuard {
|
||||
fn drop(&mut self) {
|
||||
log::debug!("Cleaning up ephemeral context...");
|
||||
// Reset to normal context
|
||||
context::manager().set_current(&context::manager().current_name())?;
|
||||
if let Err(e) = context::manager().set_current(&self.previous_context) {
|
||||
log::error!("Failed to restore context {}: {}", self.previous_context, e);
|
||||
}
|
||||
|
||||
// Remove chroot directory
|
||||
context::current()
|
||||
// We use the restored context to execute the cleanup command
|
||||
let result = context::current()
|
||||
.command("sudo")
|
||||
.arg("rm")
|
||||
.arg("-rf")
|
||||
.arg(chroot_path)
|
||||
.status()?;
|
||||
}
|
||||
.arg(&self.chroot_path)
|
||||
.status();
|
||||
|
||||
Ok(())
|
||||
match result {
|
||||
Ok(status) => {
|
||||
if !status.success() {
|
||||
log::error!(
|
||||
"Failed to remove chroot directory {}",
|
||||
self.chroot_path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Failed to execute cleanup command for {}: {}",
|
||||
self.chroot_path.display(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set environment variables for cross-compilation
|
||||
@@ -92,6 +122,7 @@ pub fn setup_environment(
|
||||
/// This also handles the 'ports.ubuntu.com' vs 'archive.ubuntu.com' on Ubuntu
|
||||
pub fn ensure_repositories(arch: &str, series: &str) -> Result<(), Box<dyn Error>> {
|
||||
let ctx = context::current();
|
||||
let local_arch = crate::get_current_arch();
|
||||
|
||||
// Add target ('host') architecture
|
||||
ctx.command("dpkg")
|
||||
@@ -115,114 +146,135 @@ pub fn ensure_repositories(arch: &str, series: &str) -> Result<(), Box<dyn Error
|
||||
.success();
|
||||
|
||||
if has_deb822 {
|
||||
// Scope existing to amd64 if not already scoped
|
||||
// This looks for URIs lines for archive/security and adds Architectures: amd64 on the next line if missing
|
||||
ctx.command("sed")
|
||||
.arg("-i")
|
||||
.arg("/URIs:.*\\(archive\\|security\\)\\.ubuntu\\.com/ { n; /^Architectures:/ ! i Architectures: amd64 }")
|
||||
.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")
|
||||
.arg("-q")
|
||||
.arg("ports.ubuntu.com")
|
||||
.arg(deb822_path)
|
||||
.status()?
|
||||
.success();
|
||||
|
||||
if !has_ports {
|
||||
let ports_block = format!(
|
||||
"\nTypes: deb\nURIs: http://ports.ubuntu.com/ubuntu-ports\nSuites: {series} {series}-updates {series}-backports {series}-security {series}-proposed\nComponents: main restricted universe multiverse\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\nArchitectures: {arch}\n"
|
||||
);
|
||||
ctx.command("sh")
|
||||
.arg("-c")
|
||||
.arg(format!("echo '{}' >> {}", ports_block, deb822_path))
|
||||
.status()?;
|
||||
}
|
||||
ensure_repositories_deb822(&ctx, arch, &local_arch, series, deb822_path)?;
|
||||
} 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
|
||||
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")
|
||||
.arg("-q")
|
||||
.arg("ports.ubuntu.com")
|
||||
.arg(sources_path)
|
||||
.status()?
|
||||
.success();
|
||||
|
||||
if !has_ports {
|
||||
let ports_lines = format!(
|
||||
"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"
|
||||
);
|
||||
ctx.command("sh")
|
||||
.arg("-c")
|
||||
.arg(format!("echo '{}' >> {}", ports_lines, sources_path))
|
||||
.status()?;
|
||||
}
|
||||
ensure_repositories_legacy(&ctx, arch, &local_arch, series, "/etc/apt/sources.list")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_repositories_deb822(
|
||||
ctx: &context::Context,
|
||||
arch: &str,
|
||||
local_arch: &str,
|
||||
series: &str,
|
||||
deb822_path: &str,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// Scope existing to local_arch if not already scoped
|
||||
ctx.command("sed")
|
||||
.arg("-i")
|
||||
.arg(format!("/URIs:.*\\(archive\\|security\\)\\.ubuntu\\.com/ {{ n; /^Architectures:/ ! i Architectures: {} }}", local_arch))
|
||||
.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
|
||||
// Excluding 'proposed' as it contains unstable software
|
||||
let suites = format!("{series} {series}-updates {series}-backports {series}-security");
|
||||
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")
|
||||
.arg("-q")
|
||||
.arg("ports.ubuntu.com")
|
||||
.arg(deb822_path)
|
||||
.status()?
|
||||
.success();
|
||||
|
||||
if !has_ports {
|
||||
let ports_block = format!(
|
||||
"\nTypes: deb\nURIs: http://ports.ubuntu.com/ubuntu-ports\nSuites: {series} {series}-updates {series}-backports {series}-security\nComponents: main restricted universe multiverse\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\nArchitectures: {arch}\n"
|
||||
);
|
||||
ctx.command("sh")
|
||||
.arg("-c")
|
||||
.arg(format!("echo '{}' >> {}", ports_block, deb822_path))
|
||||
.status()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_repositories_legacy(
|
||||
ctx: &context::Context,
|
||||
arch: &str,
|
||||
local_arch: &str,
|
||||
series: &str,
|
||||
sources_path: &str,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// Scope archive.ubuntu.com and security.ubuntu.com to local_arch if not already scoped
|
||||
ctx.command("sed")
|
||||
.arg("-i")
|
||||
.arg(format!(
|
||||
r"/archive.ubuntu.com\|security.ubuntu.com/ {{ /arch=/ ! {{ /^deb \[/ ! s/^deb /deb [arch={}] /; /^deb \[/ s/^deb \[\([^]]*\)\]/deb [arch={} \1]/ }} }}",
|
||||
local_arch, local_arch
|
||||
))
|
||||
.arg(sources_path)
|
||||
.status()?;
|
||||
|
||||
// Ensure all components (main restricted universe multiverse) are present for all archive/security lines
|
||||
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.
|
||||
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={}] http://archive.ubuntu.com/ubuntu/ {} main restricted universe multiverse",
|
||||
local_arch, 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")
|
||||
.arg("-q")
|
||||
.arg("ports.ubuntu.com")
|
||||
.arg(sources_path)
|
||||
.status()?
|
||||
.success();
|
||||
|
||||
if !has_ports {
|
||||
let ports_lines = format!(
|
||||
"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"
|
||||
);
|
||||
ctx.command("sh")
|
||||
.arg("-c")
|
||||
.arg(format!("echo '{}' >> {}", ports_lines, sources_path))
|
||||
.status()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user