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))
|
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
|
/// Apply quilt patches before building, if the package provides a
|
||||||
/// 'debian/patches/series' file. `pop_first` un-applies the previous
|
/// 'debian/patches/series' file. `pop_first` un-applies the previous
|
||||||
/// attempt's patches first (resumed session whose patch tree changed).
|
/// 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
|
// Skip patch application if the series file contains no patches
|
||||||
let series_content = ctx.read_file(&series_path)?;
|
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:
|
// Classify the series entries. In a `single-debian-patch` tree,
|
||||||
// `dpkg-source -b` folds the working-tree delta into
|
// `dpkg-source --before-build` applies every series entry except the
|
||||||
// 'debian/patches/debian-changes' and registers it in the series
|
// 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
|
// WITHOUT applying it — the content stays ambient in the working tree
|
||||||
// (e.g. the vendored rust '.cargo/config.toml'). Raw `quilt push -a`
|
// (e.g. the vendored rust '.cargo/config.toml'). Raw `quilt push -a`
|
||||||
// would then refuse the patch ('file already exists'); only dpkg's own
|
// would refuse that entry ('file already exists'); only dpkg's own
|
||||||
// patch(1)-based application tolerates that. Since `dpkg-source -b`
|
// patch(1)-based application tolerates it. Real patches listed
|
||||||
// regenerates the patch from the working tree at source-build time,
|
// alongside it — or in a tree that declares `single-debian-patch`
|
||||||
// applying patches is wrong in principle here: skip the step.
|
// while carrying an unapplied quilt series, like a pkh-scaffolded
|
||||||
if uses_single_debian_patch(&ctx, Path::new(package_dir)) {
|
// package that grew real patches — must still be applied.
|
||||||
log::info!(
|
let single_debian_patch = uses_single_debian_patch(&ctx, Path::new(package_dir));
|
||||||
"Tree uses single-debian-patch: the working tree already \
|
let real_patches = series_patches_to_apply(&series_content, single_debian_patch);
|
||||||
carries the patch content, skipping quilt patch application"
|
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(());
|
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
|
// Make sure quilt is available in the build context
|
||||||
log::debug!("Installing quilt for patch application...");
|
log::debug!("Installing quilt for patch application...");
|
||||||
@@ -1529,4 +1573,53 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.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