From b152943d4be721069db29b18137b040df69844e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20H=C3=A4fele?= Date: Fri, 28 Aug 2026 19:56:10 +0200 Subject: [PATCH] fix(pixi-build-r): resolve recommended R packages --- crates/pixi_build_r/src/main.rs | 69 +++++++++++++++++++++++++++++ crates/pixi_build_r/src/metadata.rs | 31 +++---------- 2 files changed, 75 insertions(+), 25 deletions(-) diff --git a/crates/pixi_build_r/src/main.rs b/crates/pixi_build_r/src/main.rs index 2801acc8fd..eb2acc6b26 100644 --- a/crates/pixi_build_r/src/main.rs +++ b/crates/pixi_build_r/src/main.rs @@ -651,6 +651,75 @@ Imports: }); } + #[tokio::test] + async fn test_recommended_packages_are_dependencies() { + let temp_dir = TempDir::new().unwrap(); + + fs::write( + temp_dir.path().join("DESCRIPTION"), + r#"Package: nlraa +Version: 1.9.11 +Imports: + boot, + MASS, + Matrix, + mgcv, + nlme, + stats +"#, + ) + .await + .unwrap(); + + let project_model = project_fixture!({ + "name": "r-nlraa", + "version": "1.9.11", + "targets": { + "defaultTarget": {} + } + }); + + let generated_recipe = RGenerator::default() + .generate_recipe( + &project_model, + &RBackendConfig::default(), + temp_dir.path().to_path_buf(), + Platform::Linux64, + None, + &HashSet::new(), + vec![], + None, + None, + None, + None, + ) + .await + .expect("Failed to generate recipe"); + + let host_reqs = &generated_recipe.recipe.requirements.host; + let run_reqs = &generated_recipe.recipe.requirements.run; + + for package in ["r-boot", "r-mass", "r-matrix", "r-mgcv", "r-nlme"] { + assert!( + host_reqs.iter().any(|req| req.to_string() == package), + "{package} should be in host requirements" + ); + assert!( + run_reqs.iter().any(|req| req.to_string() == package), + "{package} should be in run requirements" + ); + } + + assert!( + host_reqs.iter().all(|req| req.to_string() != "r-stats"), + "base packages should not be separate host requirements" + ); + assert!( + run_reqs.iter().all(|req| req.to_string() != "r-stats"), + "base packages should not be separate run requirements" + ); + } + #[tokio::test] async fn test_package_name_derived_from_description_is_r_prefixed() { // When the model carries no name (the inline source-dependency flow), diff --git a/crates/pixi_build_r/src/metadata.rs b/crates/pixi_build_r/src/metadata.rs index 8335f043de..8bcb3ca02a 100644 --- a/crates/pixi_build_r/src/metadata.rs +++ b/crates/pixi_build_r/src/metadata.rs @@ -15,11 +15,8 @@ static CRAN_SPDX_MAP: LazyLock> = LazyLock::new(|| { /// R packages that are built into r-base and should not be listed as separate dependencies. /// -/// This includes: -/// - Base packages (14): ship with every R installation -/// - Recommended packages (15): included in all binary distributions of R -/// -/// See: +/// Recommended packages are not listed because conda-forge packages them +/// separately from r-base. pub const R_BUILTIN_PACKAGES: &[&str] = &[ // Base packages (Priority: base) "base", @@ -36,23 +33,6 @@ pub const R_BUILTIN_PACKAGES: &[&str] = &[ "tcltk", "tools", "utils", - // Recommended packages (Priority: recommended) - // These are included in all binary distributions of R - "KernSmooth", - "MASS", - "Matrix", - "boot", - "class", - "cluster", - "codetools", - "foreign", - "lattice", - "mgcv", - "nlme", - "nnet", - "rpart", - "spatial", - "survival", ]; /// Check if an R package is built into r-base @@ -715,10 +695,11 @@ Imports: } #[test] - fn test_is_builtin_package() { + fn test_only_base_packages_are_builtin() { assert!(is_builtin_package("stats")); - assert!(is_builtin_package("MASS")); - assert!(is_builtin_package("mass")); // case insensitive + assert!(is_builtin_package("STATS")); + assert!(!is_builtin_package("MASS")); + assert!(!is_builtin_package("boot")); assert!(!is_builtin_package("curl")); assert!(!is_builtin_package("ggplot2")); }