build/binary: native .buildinfo/.changes for binary builds (pkh deb)
Extend the metadata writers to binary-only uploads and wire them into the 'pkh deb' flow: - build/binary.rs generates <pkg>_<ver>_<arch>.buildinfo/.changes through any Context: debian/files consumption, encounter-order Architecture accumulation (sorted in .buildinfo like dpkg-genbuildinfo), sorted Binary lists, dpkg-formatted Description lines with udeb suffixes, Installed-Build-Depends closure over the context status DB, and binNMU handling (Source: pkg (prev), Binary-Only-Changes, previous .dsc redistribution); - artifact digests are computed inside the context via coreutils (md5sum/sha1sum/sha256sum/stat) so chrooted/remote trees work; - deb/local.rs runs the generation after 'rules binary', exports SOURCE_DATE_EPOCH from the changelog (reproducibility), and resolves vendor/profiles inside the context; deb/mod.rs retrieves the new artifacts alongside the debs; - reusable helpers added: FilesList::parse/render, parse_changelog_entry_from_str, parse_previous_version_from_str, installed_build_depends_from_content. Differential gate: same tree built with real 'dpkg-buildpackage -b' and with the pkh flow; .changes/.buildinfo compared field-by-field modulo machine-dependent fields, artifact checksums included.
This commit is contained in:
+110
-1
@@ -6,6 +6,7 @@
|
||||
//! source-tree work (tarballs, diffs, patches) to `dpkg-source` as a
|
||||
//! subprocess.
|
||||
|
||||
pub mod binary;
|
||||
pub mod buildinfo;
|
||||
pub mod buildtype;
|
||||
pub mod changes;
|
||||
@@ -771,7 +772,7 @@ mod differential_tests {
|
||||
if matches!(key, "Checksums-Sha1" | "Checksums-Sha256" | "Files") {
|
||||
let without_buildinfo = |v: &str| -> String {
|
||||
v.lines()
|
||||
.filter(|l| !l.trim_end().ends_with("_source.buildinfo"))
|
||||
.filter(|l| !l.trim_end().ends_with(".buildinfo"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
};
|
||||
@@ -1125,6 +1126,114 @@ Provides: virtual-thing (= 2.0), plain-virtual
|
||||
}
|
||||
}
|
||||
|
||||
/// Differential check of the binary-build metadata generation against
|
||||
/// real `dpkg-buildpackage -b`: both sides build the same tree (rules
|
||||
/// driving dpkg-gencontrol/dpkg-deb directly, no debhelper needed),
|
||||
/// then the produced `.changes`/`.buildinfo` are compared field by
|
||||
/// field modulo machine-dependent values.
|
||||
#[test]
|
||||
fn diff_binary_build_metadata() {
|
||||
const NAME: &str = "pkh-diff-m";
|
||||
let control = format!(
|
||||
"Source: {NAME}\nSection: utils\nPriority: optional\nMaintainer: {MAINTAINER}\nBuild-Depends: libc6\n\n\
|
||||
Package: {NAME}\nArchitecture: any\nDescription: test package main\n long description\n\n\
|
||||
Package: {NAME}-u\nPackage-Type: udeb\nArchitecture: all\nDescription: test udeb\n short\n"
|
||||
);
|
||||
let changelog = format!(
|
||||
"{NAME} (1.0-1) unstable; urgency=medium\n\n * Binary build test.\n\n -- {MAINTAINER} {DATE}\n"
|
||||
);
|
||||
let rules = format!(
|
||||
"#!/usr/bin/make -f\nV = $(shell dpkg-parsechangelog -S Version)\nA = $(shell dpkg-architecture -qDEB_HOST_ARCH)\n\nbuild:\n\tmkdir -p debian/tmp/usr/bin\n\tprintf '#!/bin/sh\\necho hi\\n' > debian/tmp/usr/bin/hello\n\tchmod 755 debian/tmp/usr/bin/hello\n\ttouch $@\n\nbinary: build\n\trm -rf debian/{NAME} debian/{NAME}-u\n\tmkdir -p debian/{NAME}/usr/bin debian/{NAME}/DEBIAN\n\tcp -r debian/tmp/. debian/{NAME}/\n\tdpkg-gencontrol -p{NAME} -Pdebian/{NAME}\n\tdpkg-deb --build debian/{NAME} ..\n\tmkdir -p debian/{NAME}-u/usr/share debian/{NAME}-u/DEBIAN\n\techo data > debian/{NAME}-u/usr/share/data.txt\n\tdpkg-gencontrol -p{NAME}-u -Pdebian/{NAME}-u\n\tdpkg-deb --build debian/{NAME}-u ..\n\tmv ../{NAME}-u_$(V)_all.deb ../{NAME}-u_$(V)_all.udeb\n\nclean:\n\trm -rf debian/tmp debian/{NAME} debian/{NAME}-u build-stamp debian/files debian/*.substvars\n\n.PHONY: build binary clean\n"
|
||||
);
|
||||
|
||||
let write_tree = |root: &Path| {
|
||||
fs::create_dir_all(root.join(format!("{NAME}/debian/source"))).expect("mkdir tree");
|
||||
let tree = root.join(NAME);
|
||||
fs::write(tree.join("debian/control"), &control).expect("write control");
|
||||
fs::write(tree.join("debian/changelog"), &changelog).expect("write changelog");
|
||||
fs::write(tree.join("debian/source/format"), "3.0 (native)\n")
|
||||
.expect("write format");
|
||||
fs::write(tree.join("debian/rules"), &rules).expect("write rules");
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
fs::set_permissions(tree.join("debian/rules"), fs::Permissions::from_mode(0o755))
|
||||
.expect("chmod rules");
|
||||
}
|
||||
tree
|
||||
};
|
||||
|
||||
let base = tempfile::tempdir().expect("tempdir");
|
||||
let golden_root = base.path().join("golden");
|
||||
let ours_root = base.path().join("ours");
|
||||
fs::create_dir_all(&golden_root).expect("mkdir golden");
|
||||
fs::create_dir_all(&ours_root).expect("mkdir ours");
|
||||
|
||||
let golden_tree = write_tree(&golden_root);
|
||||
let ours_tree = write_tree(&ours_root);
|
||||
|
||||
// Golden side: real dpkg-buildpackage binary build.
|
||||
let status = Command::new("dpkg-buildpackage")
|
||||
.current_dir(&golden_tree)
|
||||
.args(["-b", "-d", "--no-sign"])
|
||||
.status()
|
||||
.expect("run dpkg-buildpackage (is dpkg-dev installed?)");
|
||||
assert!(status.success(), "golden dpkg-buildpackage -b failed");
|
||||
|
||||
// Ours: emulate the pkh deb flow (rules build + rules binary with a
|
||||
// dpkg-buildpackage-like environment), then run the native metadata
|
||||
// generation through a local context. dpkg-buildpackage runs the
|
||||
// rules targets directly by default (missing Rules-Requires-Root is
|
||||
// treated as 'no'), so no fakeroot wrapper here either.
|
||||
let entry =
|
||||
crate::debian::parse_changelog_entry_from_str(&changelog).expect("parse changelog");
|
||||
let vendor = env::current_vendor();
|
||||
let profiles = env::resolve_build_profiles(&[], &vendor);
|
||||
let parallel = env::num_parallel();
|
||||
let build_env_vars: Vec<(String, String)> = [
|
||||
("LANG".to_string(), "C".to_string()),
|
||||
("DEB_BUILD_OPTIONS".to_string(), format!("parallel={parallel}")),
|
||||
("SOURCE_DATE_EPOCH".to_string(), entry.timestamp.to_string()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
for target in ["build", "binary"] {
|
||||
let status = Command::new("debian/rules")
|
||||
.current_dir(&ours_tree)
|
||||
.envs(build_env_vars.clone())
|
||||
.arg(target)
|
||||
.status()
|
||||
.expect("run rules target");
|
||||
assert!(status.success(), "debian/rules {target} failed");
|
||||
}
|
||||
|
||||
let ctx = std::sync::Arc::new(crate::context::Context::new(
|
||||
crate::context::ContextConfig::Local,
|
||||
));
|
||||
let native_arch = crate::debian::arch::native().unwrap_or_else(|_| "amd64".into());
|
||||
let opts = crate::build::binary::BinaryMetadataOptions {
|
||||
profiles,
|
||||
vendor,
|
||||
parallel,
|
||||
source_date_epoch: entry.timestamp,
|
||||
build_arch: native_arch.clone(),
|
||||
host_arch: native_arch,
|
||||
};
|
||||
crate::build::binary::generate_binary_metadata(&ctx, &ours_tree, &ours_root, &opts)
|
||||
.expect("native binary metadata generation failed");
|
||||
|
||||
// Compare artifacts.
|
||||
assert_changes_equivalent(
|
||||
&golden_root.join(format!("{NAME}_1.0-1_amd64.changes")),
|
||||
&ours_root.join(format!("{NAME}_1.0-1_amd64.changes")),
|
||||
);
|
||||
assert_buildinfo_equivalent(
|
||||
&golden_root.join(format!("{NAME}_1.0-1_amd64.buildinfo")),
|
||||
&ours_root.join(format!("{NAME}_1.0-1_amd64.buildinfo")),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_native_minimal() {
|
||||
differential_case(&FixtureSpec::new("pkh-diff-a", "1.0-1", "unstable"));
|
||||
|
||||
Reference in New Issue
Block a user