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:
2026-08-24 11:58:19 +02:00
parent dfaab0606a
commit 42fcfc2dfa
9 changed files with 808 additions and 45 deletions
+14 -8
View File
@@ -28,13 +28,6 @@ struct StatusDb {
}
impl StatusDb {
/// Parse a dpkg status file (e.g. `/var/lib/dpkg/status`).
fn load(path: &Path) -> Result<StatusDb, Box<dyn std::error::Error>> {
let content = std::fs::read_to_string(path)
.map_err(|e| format!("cannot read status file '{}': {}", path.display(), e))?;
Ok(Self::from_str(&content))
}
fn from_str(content: &str) -> StatusDb {
let mut db = StatusDb::default();
for para in parse_paragraphs(content) {
@@ -132,7 +125,20 @@ pub fn installed_build_depends(
status_path: &Path,
build_depends_fields: &[&str],
) -> Result<String, Box<dyn std::error::Error>> {
let db = StatusDb::load(status_path)?;
let content = std::fs::read_to_string(status_path)
.map_err(|e| format!("cannot read status file '{}': {}", status_path.display(), e))?;
installed_build_depends_from_content(&content, build_depends_fields)
.map_err(|e| e.into())
}
/// Compute the `Installed-Build-Depends` value from the textual content of a
/// dpkg status database (used when the database lives in another context,
/// e.g. inside a chroot).
pub fn installed_build_depends_from_content(
status_content: &str,
build_depends_fields: &[&str],
) -> Result<String, String> {
let db = StatusDb::from_str(status_content);
let mut work: VecDeque<String> = VecDeque::new();
for name in &db.essential {