deb: skip quilt patch application for single-debian-patch trees
This commit is contained in:
@@ -512,6 +512,23 @@ fn apply_quilt_patches(
|
|||||||
return Ok(());
|
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
|
||||||
|
// 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"
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
// 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...");
|
||||||
let status = cap(
|
let status = cap(
|
||||||
@@ -552,6 +569,39 @@ fn apply_quilt_patches(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the source options of the package tree at `package_dir` (inside
|
||||||
|
/// the build context) declare the `single-debian-patch` mode: either
|
||||||
|
/// `debian/source/local-options` or `debian/source/options` contains a
|
||||||
|
/// line whose trimmed content — after stripping a leading `--` long-option
|
||||||
|
/// dash — is exactly `single-debian-patch` (both spellings exist in the
|
||||||
|
/// wild). A line merely containing the token as a substring (e.g.
|
||||||
|
/// `--single-debian-patch-foo`) does not count.
|
||||||
|
fn uses_single_debian_patch(ctx: &Context, package_dir: &Path) -> bool {
|
||||||
|
let local_options = ctx
|
||||||
|
.read_file(&package_dir.join("debian/source/local-options"))
|
||||||
|
.ok();
|
||||||
|
let options = ctx
|
||||||
|
.read_file(&package_dir.join("debian/source/options"))
|
||||||
|
.ok();
|
||||||
|
options_declare_single_debian_patch(local_options.as_deref(), options.as_deref())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pure decision core of [`uses_single_debian_patch`]: do the (optional)
|
||||||
|
/// contents of `debian/source/local-options` / `debian/source/options`
|
||||||
|
/// declare `single-debian-patch`?
|
||||||
|
fn options_declare_single_debian_patch(local_options: Option<&str>, options: Option<&str>) -> bool {
|
||||||
|
let declares = |content: Option<&str>| {
|
||||||
|
content.is_some_and(|content| {
|
||||||
|
content.lines().any(|line| {
|
||||||
|
let line = line.trim();
|
||||||
|
let line = line.strip_prefix("--").unwrap_or(line);
|
||||||
|
line == "single-debian-patch"
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
declares(local_options) || declares(options)
|
||||||
|
}
|
||||||
|
|
||||||
/// Pin a 'NotAutomatic' pocket (e.g. '-proposed') so apt takes it into
|
/// Pin a 'NotAutomatic' pocket (e.g. '-proposed') so apt takes it into
|
||||||
/// account during dependency resolution.
|
/// account during dependency resolution.
|
||||||
///
|
///
|
||||||
@@ -685,3 +735,116 @@ fn dose3_explain_dependencies(
|
|||||||
cmd.status()?;
|
cmd.status()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::context::ContextConfig;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn detector_matches_local_options_options_and_dashed_spellings() {
|
||||||
|
// The pkh scaffold spelling: bare token in local-options.
|
||||||
|
assert!(options_declare_single_debian_patch(
|
||||||
|
Some("single-debian-patch\n"),
|
||||||
|
None
|
||||||
|
));
|
||||||
|
// The other common spelling in either file: `--`-prefixed.
|
||||||
|
assert!(options_declare_single_debian_patch(
|
||||||
|
Some("--single-debian-patch\n"),
|
||||||
|
None
|
||||||
|
));
|
||||||
|
assert!(options_declare_single_debian_patch(
|
||||||
|
None,
|
||||||
|
Some("--single-debian-patch\n")
|
||||||
|
));
|
||||||
|
// Trailing whitespace and blank lines around the token.
|
||||||
|
assert!(options_declare_single_debian_patch(
|
||||||
|
Some("\n single-debian-patch \n"),
|
||||||
|
None
|
||||||
|
));
|
||||||
|
// Declared in options while local-options carries other options.
|
||||||
|
assert!(options_declare_single_debian_patch(
|
||||||
|
Some("--extend-diff-ignore='^vendor/'\n"),
|
||||||
|
Some("single-debian-patch\n")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn detector_rejects_substrings_empty_and_absent_files() {
|
||||||
|
// The token must not match as a substring of another option.
|
||||||
|
assert!(!options_declare_single_debian_patch(
|
||||||
|
Some("--single-debian-patch-ignore=^foo\n"),
|
||||||
|
None
|
||||||
|
));
|
||||||
|
assert!(!options_declare_single_debian_patch(
|
||||||
|
Some("--no-single-debian-patch\n"),
|
||||||
|
Some("single-debian-patching\n")
|
||||||
|
));
|
||||||
|
// Absent or empty files: not single-debian-patch.
|
||||||
|
assert!(!options_declare_single_debian_patch(None, None));
|
||||||
|
assert!(!options_declare_single_debian_patch(Some(""), Some("")));
|
||||||
|
// Unrelated content.
|
||||||
|
assert!(!options_declare_single_debian_patch(
|
||||||
|
Some("--extend-diff-ignore='^vendor/'\n"),
|
||||||
|
Some("tar-ignore = .git\n")
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A tree with `single-debian-patch` in its local-options is detected
|
||||||
|
/// through the real build-context file access; a tree without the
|
||||||
|
/// declaration is not.
|
||||||
|
#[test]
|
||||||
|
fn detector_on_a_real_tree_through_the_context() {
|
||||||
|
let ctx = Context::new(ContextConfig::Local).unwrap();
|
||||||
|
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let tree = dir.path().join("pkg");
|
||||||
|
std::fs::create_dir_all(tree.join("debian/source")).unwrap();
|
||||||
|
std::fs::write(
|
||||||
|
tree.join("debian/source/local-options"),
|
||||||
|
"single-debian-patch\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(uses_single_debian_patch(&ctx, &tree));
|
||||||
|
|
||||||
|
// No source options at all.
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let tree = dir.path().join("pkg");
|
||||||
|
std::fs::create_dir_all(tree.join("debian/source")).unwrap();
|
||||||
|
assert!(!uses_single_debian_patch(&ctx, &tree));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Direct call of the patch-application phase on a tree simulating the
|
||||||
|
/// `dpkg-source -b` state of a single-debian-patch scaffold:
|
||||||
|
/// 'debian-changes' is registered in the series while its content is
|
||||||
|
/// already ambient in the working tree (raw `quilt push -a` would
|
||||||
|
/// refuse it with 'file already exists'). The phase must skip the
|
||||||
|
/// application entirely and succeed.
|
||||||
|
#[test]
|
||||||
|
fn apply_phase_skips_single_debian_patch_trees() {
|
||||||
|
let ctx = Arc::new(Context::new(ContextConfig::Local).unwrap());
|
||||||
|
|
||||||
|
// Dashed spelling through debian/source/options this time.
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let tree = dir.path().join("pkg");
|
||||||
|
std::fs::create_dir_all(tree.join("debian/patches")).unwrap();
|
||||||
|
std::fs::create_dir_all(tree.join("debian/source")).unwrap();
|
||||||
|
// The touched file already carries the patched content: applying
|
||||||
|
// the patch would fail ('README' already exists / differs only in
|
||||||
|
// the patch's imagination).
|
||||||
|
std::fs::write(tree.join("README"), "patched\n").unwrap();
|
||||||
|
std::fs::write(
|
||||||
|
tree.join("debian/patches/debian-changes"),
|
||||||
|
"--- a/README\n+++ b/README\n@@ -1 +1 @@\n-orig\n+patched\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
std::fs::write(tree.join("debian/patches/series"), "debian-changes\n").unwrap();
|
||||||
|
std::fs::write(
|
||||||
|
tree.join("debian/source/options"),
|
||||||
|
"--single-debian-patch\n",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
apply_quilt_patches(tree.to_str().unwrap(), &HashMap::new(), ctx, &None, &None).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user