deb,quirks: scope deb entries per series with an entry list

One deb block per package cannot express two series needing different
rules. Make pull and deb lists of entries, each with its own series
scope: every matching entry applies, in file order, so a stonking
entry can carry its own dependency rules next to the resolute one.
Thread the series through find_package_directory for the
package_directory lookup, which keeps its deb-then-pull fallback.
This commit is contained in:
2026-09-20 19:40:19 +02:00
parent ff1f8c7ccd
commit 5b0cc08d8f
4 changed files with 94 additions and 64 deletions
+17 -13
View File
@@ -1,10 +1,12 @@
# Quirks configuration for package-specific workarounds
# This file defines package-specific quirks that are applied during pull and deb operations
#
# Entries can be scoped with `series`: an empty list applies to every
# series, otherwise only the listed ones. Packaging workarounds should
# carry the series they were verified against so they can be dropped once
# the upstream packaging catches up.
# `pull` and `deb` hold one entry per scope: several entries can carry
# different `series` lists, and every matching entry applies in file
# order. Entries can be scoped with `series`: an empty list applies to
# every series, otherwise only the listed ones. Packaging workarounds
# should carry the series they were verified against so they can be
# dropped once the upstream packaging catches up.
quirks:
@@ -15,13 +17,13 @@ quirks:
# until the control is fixed upstream.
linux:
deb:
series: [resolute]
- series: [resolute]
dependencies:
replace:
llvm-21-dev: llvm-21-dev:native <!stage1>
linux-riscv:
deb:
series: [resolute]
- series: [resolute]
dependencies:
replace:
llvm-21-dev: llvm-21-dev:native <!stage1>
@@ -29,15 +31,17 @@ quirks:
# Add more packages and their quirks as needed
# example-package:
# pull:
# method: archive
# - series: [noble]
# package_directory:
# - linux-main
# deb:
# series: [noble]
# - series: [resolute]
# dependencies:
# replace:
# old-dep: new-dep (>= 2) [linux-any]
# inject:
# - missing-dep
# drop:
# - broken-dep
# llvm-21-dev: llvm-21-dev:native <!stage1>
# - series: [stonking]
# dependencies:
# replace:
# llvm-22-dev: llvm-22-dev:native <!stage1>
# parameters:
# key: value
+2 -1
View File
@@ -231,9 +231,10 @@ pub async fn build(
return Err("Could not install essential packages for the build".into());
}
// Find the actual package directory
// Find the actual package directory
let package_dir =
crate::deb::find_package_directory(Path::new(build_root), package, version, &ctx)?;
crate::deb::find_package_directory(Path::new(build_root), package, version, series, &ctx)?;
let package_dir_str = package_dir
.to_str()
.ok_or("Invalid package directory path")?;
+9 -3
View File
@@ -337,10 +337,11 @@ pub(crate) fn find_package_directory(
parent_dir: &Path,
package: &str,
version: &str,
series: &str,
ctx: &context::Context,
) -> Result<PathBuf, Box<dyn Error>> {
// Check quirks first for custom package directories
let custom_dirs = crate::quirks::get_package_directories(package);
let custom_dirs = crate::quirks::get_package_directories(package, series);
for custom_dir in custom_dirs {
let package_dir = parent_dir.join(&custom_dir);
if ctx.exists(&package_dir)? && ctx.exists(&package_dir.join("debian"))? {
@@ -533,8 +534,13 @@ mod tests {
let ctx = Arc::new(Context::new(crate::context::ContextConfig::Local).unwrap());
// Change directory to the package directory
let cwd =
crate::deb::find_package_directory(cwd, package, &package_info.stanza.version, &ctx)
let cwd = crate::deb::find_package_directory(
cwd,
package,
&package_info.stanza.version,
series,
&ctx,
)
.expect("Cannot find package directory");
log::debug!("Package directory: {}", cwd.display());
+57 -38
View File
@@ -57,15 +57,19 @@ pub struct OperationQuirks {
}
/// Quirks for a specific package
///
/// `pull` and `deb` hold one entry per scope: an operation can carry
/// several entries with different `series` lists; every matching entry
/// applies, in file order.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PackageQuirks {
/// Quirks to apply during pull operation
#[serde(default)]
pub pull: Option<OperationQuirks>,
pub pull: Vec<OperationQuirks>,
/// Quirks to apply during deb operation
#[serde(default)]
pub deb: Option<OperationQuirks>,
pub deb: Vec<OperationQuirks>,
}
/// Top-level quirks configuration
@@ -102,49 +106,64 @@ fn entry_applies_to_series(quirks: &OperationQuirks, series: &str) -> bool {
/// Get the build-dependency resolution rules of a package for a series
///
/// Every deb entry whose series list matches contributes its rules; the
/// returned rules apply in file order.
///
/// # Arguments
/// * `package` - The package name
/// * `series` - The distribution series (e.g. "resolute")
///
/// # Returns
/// * `Option<DependencyQuirks>` - The rules, or None when the package has
/// no deb entry or the entry does not apply to the series
pub fn get_deb_dependency_quirks(package: &str, series: &str) -> Option<DependencyQuirks> {
let quirks = get_package_quirks(&QUIRKS_DATA, package)?;
let deb = quirks.deb.as_ref()?;
if entry_applies_to_series(deb, series) {
deb.dependencies.clone()
} else {
None
}
/// * `Vec<DependencyQuirks>` - The matching rules, empty when the package
/// has no deb entry or none applies to the series
pub fn get_deb_dependency_quirks(package: &str, series: &str) -> Vec<DependencyQuirks> {
let Some(quirks) = get_package_quirks(&QUIRKS_DATA, package) else {
return Vec::new();
};
quirks
.deb
.iter()
.filter(|deb| entry_applies_to_series(deb, series))
.filter_map(|deb| deb.dependencies.clone())
.collect()
}
/// Get package directories from quirks configuration
///
/// This function returns the list of custom package directories to try
/// when looking for the package source directory.
/// when looking for the package source directory: every matching deb
/// entry contributes its directories, falling back to the pull entries
/// when no deb entry carries any.
///
/// # Arguments
/// * `package` - The package name
/// * `series` - The distribution series (e.g. "resolute")
///
/// # Returns
/// * `Vec<String>` - List of package directories to try, or empty vector if none
pub fn get_package_directories(package: &str) -> Vec<String> {
if let Some(quirks) = get_package_quirks(&QUIRKS_DATA, package) {
// Check deb quirks first, then pull quirks
if let Some(deb_quirks) = &quirks.deb
&& !deb_quirks.package_directory.is_empty()
{
return deb_quirks.package_directory.clone();
}
if let Some(pull_quirks) = &quirks.pull
&& !pull_quirks.package_directory.is_empty()
{
return pull_quirks.package_directory.clone();
}
}
pub fn get_package_directories(package: &str, series: &str) -> Vec<String> {
let Some(quirks) = get_package_quirks(&QUIRKS_DATA, package) else {
return Vec::new();
};
Vec::new()
let mut directories = Vec::new();
for deb in quirks
.deb
.iter()
.filter(|q| entry_applies_to_series(q, series))
{
directories.extend(deb.package_directory.iter().cloned());
}
if directories.is_empty() {
for pull in quirks
.pull
.iter()
.filter(|q| entry_applies_to_series(q, series))
{
directories.extend(pull.package_directory.iter().cloned());
}
}
directories
}
/// Apply the dependency quirks of `package` in `series` to parsed
@@ -160,10 +179,10 @@ pub fn apply_dependency_quirks(
clauses: &mut Vec<Vec<PkgRelation>>,
opts: &ParseOpts,
) -> Result<(), String> {
let Some(deps) = get_deb_dependency_quirks(package, series) else {
return Ok(());
};
apply_rules(clauses, &deps, opts)
for deps in get_deb_dependency_quirks(package, series) {
apply_rules(clauses, &deps, opts)?;
}
Ok(())
}
/// Apply one set of dependency rules to parsed clauses.
@@ -236,8 +255,8 @@ mod tests {
fn test_unknown_package_has_no_quirks() {
// A package absent from quirks.yml has no dependency rules nor
// custom directories, and must not panic
assert!(get_deb_dependency_quirks("not-in-quirks", "resolute").is_none());
assert!(get_package_directories("not-in-quirks").is_empty());
assert!(get_deb_dependency_quirks("not-in-quirks", "resolute").is_empty());
assert!(get_package_directories("not-in-quirks", "resolute").is_empty());
}
/// The linux dependency quirks are scoped to the series they were
@@ -245,13 +264,13 @@ mod tests {
#[test]
fn linux_dependency_quirks_are_series_scoped() {
for package in ["linux", "linux-riscv"] {
let deps =
get_deb_dependency_quirks(package, "resolute").expect("the resolute entry applies");
let rules = get_deb_dependency_quirks(package, "resolute");
assert_eq!(rules.len(), 1, "the resolute entry applies");
assert_eq!(
deps.replace.get("llvm-21-dev").map(String::as_str),
rules[0].replace.get("llvm-21-dev").map(String::as_str),
Some("llvm-21-dev:native <!stage1>")
);
assert!(get_deb_dependency_quirks(package, "noble").is_none());
assert!(get_deb_dependency_quirks(package, "noble").is_empty());
}
}