diff --git a/data/quirks.yml b/data/quirks.yml index a57d0e6..6c989e9 100644 --- a/data/quirks.yml +++ b/data/quirks.yml @@ -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,29 +17,31 @@ quirks: # until the control is fixed upstream. linux: deb: - series: [resolute] - dependencies: - replace: - llvm-21-dev: llvm-21-dev:native + - series: [resolute] + dependencies: + replace: + llvm-21-dev: llvm-21-dev:native linux-riscv: deb: - series: [resolute] - dependencies: - replace: - llvm-21-dev: llvm-21-dev:native + - series: [resolute] + dependencies: + replace: + llvm-21-dev: llvm-21-dev:native # Add more packages and their quirks as needed # example-package: # pull: - # method: archive + # - series: [noble] + # package_directory: + # - linux-main # deb: - # series: [noble] - # dependencies: - # replace: - # old-dep: new-dep (>= 2) [linux-any] - # inject: - # - missing-dep - # drop: - # - broken-dep - # parameters: - # key: value + # - series: [resolute] + # dependencies: + # replace: + # llvm-21-dev: llvm-21-dev:native + # - series: [stonking] + # dependencies: + # replace: + # llvm-22-dev: llvm-22-dev:native + # parameters: + # key: value diff --git a/src/deb/local.rs b/src/deb/local.rs index 8390231..ff60971 100644 --- a/src/deb/local.rs +++ b/src/deb/local.rs @@ -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")?; diff --git a/src/deb/mod.rs b/src/deb/mod.rs index 1805d92..1aab157 100644 --- a/src/deb/mod.rs +++ b/src/deb/mod.rs @@ -337,10 +337,11 @@ pub(crate) fn find_package_directory( parent_dir: &Path, package: &str, version: &str, + series: &str, ctx: &context::Context, ) -> Result> { // 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,9 +534,14 @@ 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) - .expect("Cannot find package directory"); + 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()); log::info!("Starting binary package build..."); diff --git a/src/quirks.rs b/src/quirks.rs index dd7b1e1..769d162 100644 --- a/src/quirks.rs +++ b/src/quirks.rs @@ -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, + pub pull: Vec, /// Quirks to apply during deb operation #[serde(default)] - pub deb: Option, + pub deb: Vec, } /// 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` - 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 { - 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` - 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 { + 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` - List of package directories to try, or empty vector if none -pub fn get_package_directories(package: &str) -> Vec { - 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() +pub fn get_package_directories(package: &str, series: &str) -> Vec { + let Some(quirks) = get_package_quirks(&QUIRKS_DATA, package) else { + return 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)) { - 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(); + directories.extend(pull.package_directory.iter().cloned()); } } - - Vec::new() + directories } /// Apply the dependency quirks of `package` in `series` to parsed @@ -160,10 +179,10 @@ pub fn apply_dependency_quirks( clauses: &mut Vec>, 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 ") ); - assert!(get_deb_dependency_quirks(package, "noble").is_none()); + assert!(get_deb_dependency_quirks(package, "noble").is_empty()); } }