deb: apply real patches in single-debian-patch trees
The f055b70 skip assumed a single-debian-patch tree carries all patch
content ambient in the working tree. A tree that declares
single-debian-patch while listing real quilt patches in
debian/patches/series (e.g. a pkh-scaffolded package that grew patches,
like lite-xl) was built from unpatched sources: the binary silently
lost the patch content, while plain dpkg-buildpackage - and Launchpad,
which never sees debian/source/local-options - applies the series.
Mirror dpkg-source --before-build semantics: apply the series minus the
ambient 'debian-changes[-<version>]' entries, dropping those entries
from the staged series so 'quilt push -a' tolerates the ambient content.
A series holding only the ambient entry keeps skipping, as before
(covered by apply_phase_skips_single_debian_patch_trees); the
classification core is unit-tested in
series_patches_to_apply_classification.
This commit is contained in:
+117
-24
@@ -950,6 +950,29 @@ fn generate_upload_metadata(
|
||||
Ok((buildinfo, changes))
|
||||
}
|
||||
|
||||
/// Is this series entry the ambient single-debian-patch placeholder?
|
||||
/// `dpkg-source -b` names it 'debian-changes' or 'debian-changes-<version>'.
|
||||
fn is_debian_changes(entry: &str) -> bool {
|
||||
entry == "debian-changes" || entry.starts_with("debian-changes-")
|
||||
}
|
||||
|
||||
/// Pure decision core of the series classification in
|
||||
/// [`apply_quilt_patches`]: the entries quilt must apply. Blank and comment
|
||||
/// lines never apply; in a `single-debian-patch` tree the ambient
|
||||
/// 'debian-changes[-<version>]' entries are dropped (their content stays in
|
||||
/// the working tree — `quilt push -a` would refuse them).
|
||||
fn series_patches_to_apply<'a>(series_content: &'a str, single_debian_patch: bool) -> Vec<&'a str> {
|
||||
series_content
|
||||
.lines()
|
||||
.map(str::trim)
|
||||
.filter(|entry| {
|
||||
!entry.is_empty()
|
||||
&& !entry.starts_with('#')
|
||||
&& !(single_debian_patch && is_debian_changes(entry))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Apply quilt patches before building, if the package provides a
|
||||
/// 'debian/patches/series' file. `pop_first` un-applies the previous
|
||||
/// attempt's patches first (resumed session whose patch tree changed).
|
||||
@@ -972,35 +995,56 @@ fn apply_quilt_patches(
|
||||
|
||||
// Skip patch application if the series file contains no patches
|
||||
let series_content = ctx.read_file(&series_path)?;
|
||||
let total_patches = series_content
|
||||
.lines()
|
||||
.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",
|
||||
series_path.display()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// A `single-debian-patch` tree is already patched by construction:
|
||||
// `dpkg-source -b` folds the working-tree delta into
|
||||
// 'debian/patches/debian-changes' and registers it in the series
|
||||
// Classify the series entries. In a `single-debian-patch` tree,
|
||||
// `dpkg-source --before-build` applies every series entry except the
|
||||
// ambient 'debian-changes[-<version>]' one: `dpkg-source -b` folded
|
||||
// the working-tree delta into it and registered it in the series
|
||||
// WITHOUT applying it — the content stays ambient in the working tree
|
||||
// (e.g. the vendored rust '.cargo/config.toml'). Raw `quilt push -a`
|
||||
// would then refuse the patch ('file already exists'); only dpkg's own
|
||||
// patch(1)-based application tolerates that. Since `dpkg-source -b`
|
||||
// regenerates the patch from the working tree at source-build time,
|
||||
// applying patches is wrong in principle here: skip the step.
|
||||
if uses_single_debian_patch(&ctx, Path::new(package_dir)) {
|
||||
log::info!(
|
||||
"Tree uses single-debian-patch: the working tree already \
|
||||
carries the patch content, skipping quilt patch application"
|
||||
);
|
||||
// would refuse that entry ('file already exists'); only dpkg's own
|
||||
// patch(1)-based application tolerates it. Real patches listed
|
||||
// alongside it — or in a tree that declares `single-debian-patch`
|
||||
// while carrying an unapplied quilt series, like a pkh-scaffolded
|
||||
// package that grew real patches — must still be applied.
|
||||
let single_debian_patch = uses_single_debian_patch(&ctx, Path::new(package_dir));
|
||||
let real_patches = series_patches_to_apply(&series_content, single_debian_patch);
|
||||
let total_patches = real_patches.len();
|
||||
if total_patches == 0 {
|
||||
if single_debian_patch {
|
||||
log::info!(
|
||||
"Tree uses single-debian-patch: the working tree already \
|
||||
carries the patch content, skipping quilt patch application"
|
||||
);
|
||||
} else {
|
||||
log::debug!(
|
||||
"'{}' contains no patches, skipping quilt patch application",
|
||||
series_path.display()
|
||||
);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if single_debian_patch
|
||||
&& series_content
|
||||
.lines()
|
||||
.any(|line| is_debian_changes(line.trim()))
|
||||
{
|
||||
// Mirror dpkg-source: drop the ambient debian-changes entries from
|
||||
// the staged series so `quilt push -a` below only sees the real
|
||||
// patches. The staged copy is per-session, so the host tree keeps
|
||||
// the pristine series.
|
||||
let filtered = series_content
|
||||
.lines()
|
||||
.filter(|line| !is_debian_changes(line.trim()))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
ctx.write_file(&series_path, &format!("{}\n", filtered))?;
|
||||
log::info!(
|
||||
"Tree uses single-debian-patch: dropped the ambient \
|
||||
'debian-changes' entries from the staged series, applying \
|
||||
the real patches"
|
||||
);
|
||||
}
|
||||
|
||||
// Make sure quilt is available in the build context
|
||||
log::debug!("Installing quilt for patch application...");
|
||||
@@ -1529,4 +1573,53 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Series classification for `apply_quilt_patches`: real patches apply;
|
||||
/// blank and comment lines never do; a `single-debian-patch` tree's
|
||||
/// ambient 'debian-changes' entries are dropped while real patches
|
||||
/// listed alongside them still apply (pkh must mirror dpkg-source's
|
||||
/// `--before-build` behavior).
|
||||
#[test]
|
||||
fn series_patches_to_apply_classification() {
|
||||
// Plain quilt tree: every entry applies.
|
||||
assert_eq!(
|
||||
series_patches_to_apply("0001-a.patch\n0002-b.patch\n", false),
|
||||
vec!["0001-a.patch", "0002-b.patch"]
|
||||
);
|
||||
|
||||
// Comments and blank lines never apply.
|
||||
assert_eq!(
|
||||
series_patches_to_apply("# header\n\n0001-a.patch\n \n", false),
|
||||
vec!["0001-a.patch"]
|
||||
);
|
||||
|
||||
// single-debian-patch tree with only the ambient entry: nothing to
|
||||
// apply — the working tree already carries the content.
|
||||
assert_eq!(
|
||||
series_patches_to_apply("debian-changes-2.1.8\n", true),
|
||||
Vec::<&str>::new()
|
||||
);
|
||||
|
||||
// single-debian-patch tree whose series grew real patches: apply
|
||||
// the real ones, drop the ambient entries.
|
||||
assert_eq!(
|
||||
series_patches_to_apply(
|
||||
"0001-real.patch\ndebian-changes-2.1.8\n0002-real.patch\n",
|
||||
true
|
||||
),
|
||||
vec!["0001-real.patch", "0002-real.patch"]
|
||||
);
|
||||
|
||||
// The same series in a plain tree keeps the debian-changes entry:
|
||||
// without the single-debian-patch declaration it is a real patch.
|
||||
assert_eq!(
|
||||
series_patches_to_apply("debian-changes-2.1.8\n", false),
|
||||
vec!["debian-changes-2.1.8"]
|
||||
);
|
||||
|
||||
// The versioned and unversioned ambient names are both recognized.
|
||||
assert!(is_debian_changes("debian-changes"));
|
||||
assert!(is_debian_changes("debian-changes-1.0"));
|
||||
assert!(!is_debian_changes("0001-real.patch"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user