deb: change ui/ux of pkh deb
This commit is contained in:
+117
-55
@@ -1,7 +1,9 @@
|
||||
/// Local binary package building
|
||||
/// Directly calling 'debian/rules' in current context
|
||||
use crate::context::Context;
|
||||
use crate::context::{Context, ContextCommand, LineSink};
|
||||
use crate::deb::find_dsc_file;
|
||||
use crate::ui::deb::{DebUi, Phase};
|
||||
use crate::ui::logfmt::QuiltClassifier;
|
||||
use log::warn;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
@@ -11,6 +13,17 @@ use std::sync::Arc;
|
||||
use crate::apt;
|
||||
use crate::deb::cross;
|
||||
|
||||
/// Attach the capture sink to a command when the live UI is active
|
||||
fn cap<'a>(
|
||||
cmd: &'a mut ContextCommand<'a>,
|
||||
sink: &Option<Arc<dyn LineSink>>,
|
||||
) -> &'a mut ContextCommand<'a> {
|
||||
if let Some(s) = sink {
|
||||
cmd.capture(s.clone());
|
||||
}
|
||||
cmd
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn build(
|
||||
package: &str,
|
||||
@@ -23,7 +36,10 @@ pub async fn build(
|
||||
ppa: Option<&[&str]>,
|
||||
inject_packages: Option<&[&str]>,
|
||||
ctx: Arc<Context>,
|
||||
ui: Option<Arc<DebUi>>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let sink: Option<Arc<dyn LineSink>> = ui.as_ref().map(|u| u.sink());
|
||||
|
||||
// Environment
|
||||
let mut env = HashMap::<String, String>::new();
|
||||
env.insert("LANG".to_string(), "C".to_string());
|
||||
@@ -155,19 +171,22 @@ pub async fn build(
|
||||
|
||||
// Update package lists
|
||||
log::debug!("Updating package lists for local build...");
|
||||
let status = ctx
|
||||
.command("apt-get")
|
||||
.envs(env.clone())
|
||||
.arg("update")
|
||||
.status()
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Failed to run 'apt-get update' inside the build context: {}. \
|
||||
if let Some(u) = &ui {
|
||||
u.phase(Phase::UpdatingPackageLists);
|
||||
}
|
||||
let status = cap(
|
||||
ctx.command("apt-get").envs(env.clone()).arg("update"),
|
||||
&sink,
|
||||
)
|
||||
.status()
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Failed to run 'apt-get update' inside the build context: {}. \
|
||||
If this is a local build, make sure apt-get is available and \
|
||||
try executing with sudo.",
|
||||
e
|
||||
)
|
||||
})?;
|
||||
e
|
||||
)
|
||||
})?;
|
||||
if !status.success() {
|
||||
return Err("apt-get update failed inside the build context. \
|
||||
If this is a local build, try executing with sudo, \
|
||||
@@ -193,7 +212,10 @@ pub async fn build(
|
||||
cmd.arg(format!("libc6:{arch}"));
|
||||
cmd.arg(format!("libc6-dev:{arch}"));
|
||||
}
|
||||
let status = cmd.status()?;
|
||||
if let Some(u) = &ui {
|
||||
u.phase(Phase::InstallingEssentials);
|
||||
}
|
||||
let status = cap(&mut cmd, &sink).status()?;
|
||||
if !status.success() {
|
||||
return Err("Could not install essential packages for the build".into());
|
||||
}
|
||||
@@ -206,15 +228,18 @@ pub async fn build(
|
||||
.ok_or("Invalid package directory path")?;
|
||||
|
||||
// Apply quilt patches if the package provides a patch series
|
||||
apply_quilt_patches(package_dir_str, &env, ctx.clone())?;
|
||||
apply_quilt_patches(package_dir_str, &env, ctx.clone(), &ui, &sink)?;
|
||||
|
||||
// Install injected packages if specified
|
||||
if let Some(packages) = inject_packages {
|
||||
install_injected_packages(packages, &env, ctx.clone())?;
|
||||
install_injected_packages(packages, &env, ctx.clone(), &ui, &sink)?;
|
||||
}
|
||||
|
||||
// Install arch-specific build dependencies
|
||||
log::debug!("Installing arch-specific build dependencies...");
|
||||
if let Some(u) = &ui {
|
||||
u.phase(Phase::InstallingBuildDeps);
|
||||
}
|
||||
let mut cmd = ctx.command("apt-get");
|
||||
cmd.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
@@ -224,51 +249,69 @@ pub async fn build(
|
||||
cmd.arg(format!("--host-architecture={arch}"));
|
||||
}
|
||||
cmd.arg("--arch-only");
|
||||
let status = cmd.arg("./").status()?;
|
||||
let status = cap(&mut cmd, &sink).arg("./").status()?;
|
||||
|
||||
// If build-dep fails, we try to explain the failure using dose-debcheck
|
||||
if !status.success() {
|
||||
if let Some(u) = &ui {
|
||||
u.suspend();
|
||||
}
|
||||
dose3_explain_dependencies(package, version, arch, build_root, cross, ctx.clone())?;
|
||||
return Err("Could not install build-dependencies for the build".into());
|
||||
}
|
||||
|
||||
// Install arch-independant build dependencies
|
||||
log::debug!("Installing arch-independant build dependencies...");
|
||||
let status = ctx
|
||||
.command("apt-get")
|
||||
.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
.arg("-y")
|
||||
.arg("build-dep")
|
||||
.arg("./")
|
||||
.status()?;
|
||||
let status = cap(
|
||||
ctx.command("apt-get")
|
||||
.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
.arg("-y")
|
||||
.arg("build-dep")
|
||||
.arg("./"),
|
||||
&sink,
|
||||
)
|
||||
.status()?;
|
||||
|
||||
// If build-dep fails, we try to explain the failure using dose-debcheck
|
||||
if !status.success() {
|
||||
if let Some(u) = &ui {
|
||||
u.suspend();
|
||||
}
|
||||
dose3_explain_dependencies(package, version, arch, build_root, cross, ctx.clone())?;
|
||||
return Err("Could not install build-dependencies for the build".into());
|
||||
}
|
||||
|
||||
// Run the build step
|
||||
log::debug!("Building (debian/rules build) package...");
|
||||
let status = ctx
|
||||
.command("debian/rules")
|
||||
.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
.arg("build")
|
||||
.status()?;
|
||||
if let Some(u) = &ui {
|
||||
u.phase(Phase::Building);
|
||||
}
|
||||
let status = cap(
|
||||
ctx.command("debian/rules")
|
||||
.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
.arg("build"),
|
||||
&sink,
|
||||
)
|
||||
.status()?;
|
||||
if !status.success() {
|
||||
return Err("Error while building the package".into());
|
||||
}
|
||||
|
||||
// Run the 'binary' step to produce deb
|
||||
let status = ctx
|
||||
.command("fakeroot")
|
||||
.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
.arg("debian/rules")
|
||||
.arg("binary")
|
||||
.status()?;
|
||||
if let Some(u) = &ui {
|
||||
u.phase(Phase::ProducingBinaries);
|
||||
}
|
||||
let status = cap(
|
||||
ctx.command("fakeroot")
|
||||
.current_dir(package_dir_str)
|
||||
.envs(env.clone())
|
||||
.arg("debian/rules")
|
||||
.arg("binary"),
|
||||
&sink,
|
||||
)
|
||||
.status()?;
|
||||
if !status.success() {
|
||||
return Err(
|
||||
"Error while building the binary artifacts (.deb) from the built package".into(),
|
||||
@@ -284,6 +327,8 @@ fn apply_quilt_patches(
|
||||
package_dir: &str,
|
||||
env: &HashMap<String, String>,
|
||||
ctx: Arc<Context>,
|
||||
ui: &Option<Arc<DebUi>>,
|
||||
sink: &Option<Arc<dyn LineSink>>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let series_path = Path::new(package_dir).join("debian/patches/series");
|
||||
if !ctx.exists(&series_path)? {
|
||||
@@ -296,9 +341,11 @@ fn apply_quilt_patches(
|
||||
|
||||
// Skip patch application if the series file contains no patches
|
||||
let series_content = ctx.read_file(&series_path)?;
|
||||
let has_patches = series_content
|
||||
let total_patches = series_content
|
||||
.lines()
|
||||
.any(|line| !line.trim().is_empty() && !line.trim().starts_with('#'));
|
||||
.filter(|line| !line.trim().is_empty() && !line.trim().starts_with('#'))
|
||||
.count();
|
||||
let has_patches = total_patches > 0;
|
||||
if !has_patches {
|
||||
log::debug!(
|
||||
"'{}' contains no patches, skipping quilt patch application",
|
||||
@@ -309,28 +356,37 @@ fn apply_quilt_patches(
|
||||
|
||||
// Make sure quilt is available in the build context
|
||||
log::debug!("Installing quilt for patch application...");
|
||||
let status = ctx
|
||||
.command("apt-get")
|
||||
.envs(env.clone())
|
||||
.arg("-y")
|
||||
.arg("install")
|
||||
.arg("quilt")
|
||||
.status()?;
|
||||
let status = cap(
|
||||
ctx.command("apt-get")
|
||||
.envs(env.clone())
|
||||
.arg("-y")
|
||||
.arg("install")
|
||||
.arg("quilt"),
|
||||
sink,
|
||||
)
|
||||
.status()?;
|
||||
if !status.success() {
|
||||
return Err("Could not install 'quilt', required to apply patches".into());
|
||||
}
|
||||
|
||||
// Apply all patches listed in the series
|
||||
log::info!("Applying quilt patches from debian/patches/series...");
|
||||
if let Some(u) = ui {
|
||||
u.phase_with(
|
||||
Phase::ApplyingPatches,
|
||||
Box::new(QuiltClassifier::new(total_patches)),
|
||||
);
|
||||
}
|
||||
let mut patch_env = env.clone();
|
||||
patch_env.insert("QUILT_PATCHES".to_string(), "debian/patches".to_string());
|
||||
let status = ctx
|
||||
.command("quilt")
|
||||
.current_dir(package_dir)
|
||||
.envs(patch_env)
|
||||
.arg("push")
|
||||
.arg("-a")
|
||||
.status()?;
|
||||
let status = cap(
|
||||
ctx.command("quilt")
|
||||
.current_dir(package_dir)
|
||||
.envs(patch_env)
|
||||
.arg("push")
|
||||
.arg("-a"),
|
||||
sink,
|
||||
)
|
||||
.status()?;
|
||||
if !status.success() {
|
||||
return Err("Failed to apply quilt patches ('quilt push -a')".into());
|
||||
}
|
||||
@@ -360,9 +416,15 @@ fn install_injected_packages(
|
||||
packages: &[&str],
|
||||
env: &HashMap<String, String>,
|
||||
ctx: Arc<Context>,
|
||||
ui: &Option<Arc<DebUi>>,
|
||||
sink: &Option<Arc<dyn LineSink>>,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
log::info!("Installing injected packages: {:?}", packages);
|
||||
|
||||
if let Some(u) = ui {
|
||||
u.phase(Phase::InjectingPackages);
|
||||
}
|
||||
|
||||
// Separate .deb files from package names
|
||||
let mut deb_files: Vec<String> = Vec::new();
|
||||
let mut package_names: Vec<&str> = Vec::new();
|
||||
@@ -400,7 +462,7 @@ fn install_injected_packages(
|
||||
if !package_names.is_empty() {
|
||||
cmd.args(&package_names);
|
||||
}
|
||||
let status = cmd.status()?;
|
||||
let status = cap(&mut cmd, sink).status()?;
|
||||
if !status.success() {
|
||||
return Err(format!("Could not install injected packages: {:?}", deb_files).into());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user