From 731358edad4533c93abd5ab96273afa8dc8ef7a9 Mon Sep 17 00:00:00 2001 From: Wolf Vollprecht Date: Fri, 31 May 2024 09:42:57 +0200 Subject: [PATCH] feat: `stdlib` implementation and compiler refactor (#892) --- src/lib.rs | 11 +- src/recipe/jinja.rs | 147 +++++++++++++--- src/recipe/parser.rs | 10 +- src/recipe/parser/requirements.rs | 94 +++-------- ..._requirements__test__compiler_serde-2.snap | 3 +- ...r__requirements__test__compiler_serde.snap | 3 +- ...rser__requirements__test__pin_package.snap | 2 - ...ecipe__parser__tests__complete_recipe.snap | 2 +- ...recipe__parser__tests__recipe_windows.snap | 20 ++- ...d__recipe__parser__tests__unix_recipe.snap | 20 ++- src/render/pin.rs | 60 ------- src/render/resolved_dependencies.rs | 157 ++++-------------- ...encies__tests__dependency_info_render.snap | 3 - src/selectors.rs | 3 + ...ild__metadata__test__curl_recipe.yaml.snap | 6 +- src/used_variables.rs | 8 + src/variant_config.rs | 2 - test-data/rendered_recipes/curl_recipe.yaml | 5 +- 18 files changed, 242 insertions(+), 314 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 07c60e0eb..db6e9dffd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,7 @@ use metadata::Output; use miette::{IntoDiagnostic, WrapErr}; use petgraph::{algo::toposort, graph::DiGraph, visit::DfsPostOrder}; use rattler_conda_types::{package::ArchiveType, Channel, ChannelConfig, Platform}; +use recipe::parser::Dependency; use std::{ collections::{BTreeMap, HashMap}, env::current_dir, @@ -168,6 +169,8 @@ pub async fn get_build_output( build_platform: args.build_platform, variant: BTreeMap::new(), experimental: args.common.experimental, + // allow undefined while finding the variants + allow_undefined: true, }; let span = tracing::info_span!("Finding outputs from recipe"); @@ -216,6 +219,7 @@ pub async fn get_build_output( host_platform: selector_config.host_platform, build_platform: selector_config.build_platform, experimental: args.common.experimental, + allow_undefined: false, }; let recipe = @@ -550,13 +554,12 @@ pub fn sort_build_outputs_topologically( .expect("We just inserted it"); for dep in output.recipe.requirements().all() { let dep_name = match dep { - recipe::parser::Dependency::Spec(spec) => spec + Dependency::Spec(spec) => spec .name .clone() .expect("MatchSpec should always have a name"), - recipe::parser::Dependency::PinSubpackage(pin) => pin.pin_value().name.clone(), - recipe::parser::Dependency::PinCompatible(pin) => pin.pin_value().name.clone(), - recipe::parser::Dependency::Compiler(_) => continue, + Dependency::PinSubpackage(pin) => pin.pin_value().name.clone(), + Dependency::PinCompatible(pin) => pin.pin_value().name.clone(), }; if let Some(&dep_idx) = name_to_index.get(&dep_name) { // do not point to self (circular dependency) - this can happen with diff --git a/src/recipe/jinja.rs b/src/recipe/jinja.rs index 16a723ac3..30afda4fc 100644 --- a/src/recipe/jinja.rs +++ b/src/recipe/jinja.rs @@ -1,16 +1,19 @@ //! Module for types and functions related to miniJinja setup for recipes. use std::process::Command; +use std::sync::Arc; use std::{collections::BTreeMap, str::FromStr}; use minijinja::value::Object; use minijinja::{Environment, Value}; -use rattler_conda_types::{PackageName, ParseStrictness, Version}; +use rattler_conda_types::{PackageName, ParseStrictness, Platform, Version, VersionSpec}; use crate::render::pin::PinArgs; pub use crate::render::pin::{Pin, PinExpression}; pub use crate::selectors::SelectorConfig; +use super::parser::{Dependency, PinCompatible, PinSubpackage}; + /// A type that hold the miniJinja environment and context for Jinja template processing. #[derive(Debug, Clone)] pub struct Jinja<'a> { @@ -93,8 +96,7 @@ fn jinja_pin_function( ) })?; - // we translate the compiler into a YAML string - let mut pin_subpackage = Pin { + let mut pin = Pin { name, args: PinArgs::default(), }; @@ -112,30 +114,121 @@ fn jinja_pin_function( let max_pin = kwargs.get_attr("max_pin")?; if max_pin != minijinja::value::Value::UNDEFINED { let pin_expr = pin_expr_from_value(&max_pin)?; - pin_subpackage.args.max_pin = Some(pin_expr); + pin.args.max_pin = Some(pin_expr); } let min = kwargs.get_attr("min_pin")?; if min != minijinja::value::Value::UNDEFINED { let pin_expr = pin_expr_from_value(&min)?; - pin_subpackage.args.min_pin = Some(pin_expr); + pin.args.min_pin = Some(pin_expr); } let exact = kwargs.get_attr("exact")?; if exact != minijinja::value::Value::UNDEFINED { - pin_subpackage.args.exact = exact.is_true(); + pin.args.exact = exact.is_true(); } } - Ok(format!( - "{} {}", - internal_repr, - pin_subpackage.internal_repr() - )) + if internal_repr == "__PIN_SUBPACKAGE" { + Ok( + serde_json::to_string(&Dependency::PinSubpackage(PinSubpackage { + pin_subpackage: pin, + })) + .unwrap(), + ) + } else { + Ok( + serde_json::to_string(&Dependency::PinCompatible(PinCompatible { + pin_compatible: pin, + })) + .unwrap(), + ) + } +} + +fn default_compiler(platform: Platform, language: &str) -> Option { + if platform.is_windows() { + match language { + "c" => Some("vs2017"), + "cxx" => Some("vs2017"), + "fortran" => Some("gfortran"), + "rust" => Some("rust"), + _ => None, + } + } else if platform.is_osx() { + match language { + "c" => Some("clang"), + "cxx" => Some("clangxx"), + "fortran" => Some("gfortran"), + "rust" => Some("rust"), + _ => None, + } + } else { + match language { + "c" => Some("gcc"), + "cxx" => Some("gxx"), + "fortran" => Some("gfortran"), + "rust" => Some("rust"), + _ => None, + } + } + .map(|s| s.to_string()) +} + +fn compiler_stdlib_eval( + lang: &str, + platform: Platform, + variant: &Arc>, + prefix: &str, +) -> Result { + let variant_key = format!("{lang}_{prefix}"); + let variant_key_version = format!("{lang}_{prefix}_version"); + + let default_fn = if prefix == "compiler" { + default_compiler + } else { + |_: Platform, _: &str| None + }; + + let res = if let Some(name) = variant + .get(&variant_key) + .cloned() + .or_else(|| default_fn(platform, lang)) + { + // check if we also have a compiler version + if let Some(version) = variant.get(&variant_key_version) { + Some(format!("{name}_{platform} {version}")) + } else { + Some(format!("{name}_{platform}")) + } + } else { + None + }; + + if let Some(res) = res { + Ok(res) + } else { + Err(minijinja::Error::new( + minijinja::ErrorKind::UndefinedError, + format!( + "No {prefix} found for language: {lang}\nYou should add `{lang}_{prefix}` to your variant config file.", + ), + )) + } } fn set_jinja(config: &SelectorConfig) -> minijinja::Environment<'static> { - use rattler_conda_types::version_spec::VersionSpec; + let SelectorConfig { + target_platform, + build_platform, + variant, + experimental, + allow_undefined, + .. + } = config.clone(); + let mut env = minijinja::Environment::new(); + let variant = Arc::new(variant.clone()); + // Ok to unwrap here because we know that the syntax is valid env.set_syntax(minijinja::Syntax { block_start: "{%".into(), @@ -174,19 +267,13 @@ fn set_jinja(config: &SelectorConfig) -> minijinja::Environment<'static> { } }); - let SelectorConfig { - target_platform, - build_platform, - variant, - experimental, - .. - } = config.clone(); + let variant_clone = variant.clone(); env.add_function("cdt", move |package_name: String| { use rattler_conda_types::Arch; let arch = build_platform.arch().or_else(|| target_platform.arch()); let arch_str = arch.map(|arch| format!("{arch}")); - let cdt_arch = if let Some(s) = variant.get("cdt_arch") { + let cdt_arch = if let Some(s) = variant_clone.get("cdt_arch") { s.as_str() } else { match arch { @@ -203,7 +290,7 @@ fn set_jinja(config: &SelectorConfig) -> minijinja::Environment<'static> { } }; - let cdt_name = variant.get("cdt_name").map_or_else( + let cdt_name = variant_clone.get("cdt_name").map_or_else( || match arch { Some(Arch::S390X | Arch::Aarch64 | Arch::Ppc64le | Arch::Ppc64) => "cos7", _ => "cos6", @@ -219,12 +306,24 @@ fn set_jinja(config: &SelectorConfig) -> minijinja::Environment<'static> { Ok(res) }); - env.add_function("compiler", |lang: String| { - // we translate the compiler into a YAML string - Ok(format!("__COMPILER {}", lang.to_lowercase())) + // "${{ PREFIX }}" delay the expansion. -> $PREFIX on unix and %PREFIX% on windows? + let variant_clone = variant.clone(); + env.add_function("compiler", move |lang: String| { + compiler_stdlib_eval(&lang, target_platform, &variant_clone, "compiler") + }); + + let variant_clone = variant.clone(); + env.add_function("stdlib", move |lang: String| { + let res = compiler_stdlib_eval(&lang, target_platform, &variant_clone, "stdlib"); + if allow_undefined { + Ok(res.unwrap_or_else(|_| "undefined".to_string())) + } else { + res + } }); env.add_function("pin_subpackage", |name: String, kwargs: Option| { + // return "{ pin_subpackage: { name: foo, max_pin: x.x.x, min_pin: x.x, exact: true }}".to_string(); jinja_pin_function(name, kwargs, "__PIN_SUBPACKAGE") }); diff --git a/src/recipe/parser.rs b/src/recipe/parser.rs index 42b78a107..e7b93db56 100644 --- a/src/recipe/parser.rs +++ b/src/recipe/parser.rs @@ -39,7 +39,8 @@ pub use self::{ package::{OutputPackage, Package}, regex::SerializableRegex, requirements::{ - Compiler, Dependency, IgnoreRunExports, PinSubpackage, Requirements, RunExports, + Dependency, IgnoreRunExports, Language, PinCompatible, PinSubpackage, Requirements, + RunExports, }, script::{Script, ScriptContent}, source::{GitRev, GitSource, GitUrl, PathSource, Source, UrlSource}, @@ -453,8 +454,13 @@ mod tests { #[test] fn test_complete_recipe() { + let selector_config = SelectorConfig { + target_platform: Platform::Linux64, + host_platform: Platform::Linux64, + ..SelectorConfig::default() + }; let recipe = include_str!("../../test-data/recipes/test-parsing/single_output.yaml"); - let recipe = Recipe::from_yaml(recipe, SelectorConfig::default()).unwrap(); + let recipe = Recipe::from_yaml(recipe, selector_config).unwrap(); assert_snapshot!(serde_yaml::to_string(&recipe).unwrap()); } } diff --git a/src/recipe/parser/requirements.rs b/src/recipe/parser/requirements.rs index 0c6cdbe10..f77f19d33 100644 --- a/src/recipe/parser/requirements.rs +++ b/src/recipe/parser/requirements.rs @@ -150,7 +150,7 @@ impl TryConvertNode for RenderedMappingNode { pub struct PinSubpackage { /// The pin value. #[serde(flatten)] - pin_subpackage: Pin, + pub pin_subpackage: Pin, } impl PinSubpackage { @@ -168,7 +168,7 @@ impl PinSubpackage { pub struct PinCompatible { /// The pin value. #[serde(flatten)] - pin_compatible: Pin, + pub pin_compatible: Pin, } impl PinCompatible { @@ -186,15 +186,12 @@ impl PinCompatible { /// it is always resolved with the target_platform. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[serde(transparent)] -pub struct Compiler { - /// The language such as c, cxx, rust, etc. - language: String, -} +pub struct Language(String); -impl Compiler { +impl Language { /// Get the language value as a string. pub fn language(&self) -> &str { - &self.language + &self.0 } } @@ -207,8 +204,6 @@ pub enum Dependency { PinSubpackage(PinSubpackage), /// A pin_compatible dependency PinCompatible(PinCompatible), - /// A compiler dependency - Compiler(Compiler), } impl TryConvertNode> for RenderedNode { @@ -238,39 +233,14 @@ impl TryConvertNode> for RenderedNode { impl TryConvertNode for RenderedScalarNode { fn try_convert(&self, name: &str) -> Result> { - // compiler - if self.contains("__COMPILER") { - let compiler: String = self.try_convert(name)?; - let language = compiler - .strip_prefix("__COMPILER ") - .expect("compiler without prefix"); - // Panic should never happen from this strip unless the prefix magic for the compiler - Ok(Dependency::Compiler(Compiler { - language: language.to_string(), - })) - } else if self.contains("__PIN_SUBPACKAGE") { - let pin_subpackage: String = self.try_convert(name)?; - - // Panic should never happen from this strip unless the - // prefix magic for the pin subpackage changes - let internal_repr = pin_subpackage - .strip_prefix("__PIN_SUBPACKAGE ") - .expect("pin subpackage without prefix __PIN_SUBPACKAGE "); - let pin_subpackage = Pin::from_internal_repr(internal_repr); - Ok(Dependency::PinSubpackage(PinSubpackage { pin_subpackage })) - } else if self.contains("__PIN_COMPATIBLE") { - let pin_compatible: String = self.try_convert(name)?; - - // Panic should never happen from this strip unless the - // prefix magic for the pin compatible changes - let internal_repr = pin_compatible - .strip_prefix("__PIN_COMPATIBLE ") - .expect("pin compatible without prefix __PIN_COMPATIBLE "); - let pin_compatible = Pin::from_internal_repr(internal_repr); - Ok(Dependency::PinCompatible(PinCompatible { pin_compatible })) + // Pin subpackage and pin compatible are serialized into JSON by the `jinja` converter + if self.starts_with('{') { + // try to convert from a YAML dictionary + let dependency: Dependency = + serde_yaml::from_str(self.as_str()).expect("Internal repr error"); + Ok(dependency) } else { let spec = self.try_convert(name)?; - Ok(Dependency::Spec(spec)) } } @@ -286,7 +256,6 @@ impl<'de> Deserialize<'de> for Dependency { enum RawDependency { PinSubpackage(PinSubpackage), PinCompatible(PinCompatible), - Compiler(Compiler), } #[derive(Deserialize)] @@ -301,7 +270,6 @@ impl<'de> Deserialize<'de> for Dependency { RawSpec::String(spec) => Dependency::Spec(spec.parse().map_err(D::Error::custom)?), RawSpec::Explicit(RawDependency::PinSubpackage(dep)) => Dependency::PinSubpackage(dep), RawSpec::Explicit(RawDependency::PinCompatible(dep)) => Dependency::PinCompatible(dep), - RawSpec::Explicit(RawDependency::Compiler(dep)) => Dependency::Compiler(dep), }) } } @@ -316,7 +284,6 @@ impl Serialize for Dependency { enum RawDependency<'a> { PinSubpackage(&'a PinSubpackage), PinCompatible(&'a PinCompatible), - Compiler(&'a Compiler), } #[derive(Serialize)] @@ -330,7 +297,6 @@ impl Serialize for Dependency { Dependency::Spec(dep) => RawSpec::String(dep.to_string()), Dependency::PinSubpackage(dep) => RawSpec::Explicit(RawDependency::PinSubpackage(dep)), Dependency::PinCompatible(dep) => RawSpec::Explicit(RawDependency::PinCompatible(dep)), - Dependency::Compiler(dep) => RawSpec::Explicit(RawDependency::Compiler(dep)), }; raw.serialize(serializer) @@ -352,12 +318,13 @@ impl TryConvertNode for RenderedNode { } impl TryConvertNode for RenderedScalarNode { - fn try_convert(&self, name: &str) -> Result> { + fn try_convert(&self, _name: &str) -> Result> { MatchSpec::from_str(self.as_str(), ParseStrictness::Strict).map_err(|err| { + let str = self.as_str(); vec![_partialerror!( *self.span(), ErrorKind::from(err), - label = format!("error parsing `{name}` as a match spec") + label = format!("error parsing `{str}` as a match spec") )] }) } @@ -542,29 +509,6 @@ mod test { use super::*; - #[test] - fn test_compiler_serde() { - let compiler = Compiler { - language: "gcc".to_string(), - }; - - let serialized = serde_yaml::to_string(&compiler).unwrap(); - assert_eq!(serialized, "gcc\n"); - - let requirements = Requirements { - build: vec![Dependency::Compiler(compiler)], - ..Default::default() - }; - - insta::assert_yaml_snapshot!(requirements); - - let yaml = serde_yaml::to_string(&requirements).unwrap(); - assert_eq!(yaml, "build:\n- compiler: gcc\n"); - - let deserialized: Requirements = serde_yaml::from_str(&yaml).unwrap(); - insta::assert_yaml_snapshot!(deserialized); - } - #[test] fn test_pin_package() { let pin_subpackage = PinSubpackage { @@ -601,9 +545,6 @@ mod test { }; let spec = MatchSpec::from_str("foo >=3.1", ParseStrictness::Strict).unwrap(); - let compiler = Compiler { - language: "gcc".to_string(), - }; let requirements = Requirements { build: vec![ @@ -611,11 +552,16 @@ mod test { Dependency::PinSubpackage(pin_subpackage), Dependency::PinCompatible(pin_compatible), Dependency::PinCompatible(pin_compatible_2), - Dependency::Compiler(compiler), ], ..Default::default() }; insta::assert_snapshot!(serde_yaml::to_string(&requirements).unwrap()); } + + #[test] + fn test_deserialize_pin() { + let pin = "{ pin_subpackage: { name: foo, max_pin: x.x.x, min_pin: x.x, exact: true, spec: foo }}"; + let _: Dependency = serde_yaml::from_str(pin).unwrap(); + } } diff --git a/src/recipe/parser/snapshots/rattler_build__recipe__parser__requirements__test__compiler_serde-2.snap b/src/recipe/parser/snapshots/rattler_build__recipe__parser__requirements__test__compiler_serde-2.snap index 8864f4898..03daa7e6d 100644 --- a/src/recipe/parser/snapshots/rattler_build__recipe__parser__requirements__test__compiler_serde-2.snap +++ b/src/recipe/parser/snapshots/rattler_build__recipe__parser__requirements__test__compiler_serde-2.snap @@ -3,5 +3,4 @@ source: src/recipe/parser/requirements.rs expression: deserialized --- build: - - compiler: gcc - + - compiler: cxx diff --git a/src/recipe/parser/snapshots/rattler_build__recipe__parser__requirements__test__compiler_serde.snap b/src/recipe/parser/snapshots/rattler_build__recipe__parser__requirements__test__compiler_serde.snap index 5bddf5d4a..cecf4b0fa 100644 --- a/src/recipe/parser/snapshots/rattler_build__recipe__parser__requirements__test__compiler_serde.snap +++ b/src/recipe/parser/snapshots/rattler_build__recipe__parser__requirements__test__compiler_serde.snap @@ -3,5 +3,4 @@ source: src/recipe/parser/requirements.rs expression: requirements --- build: - - compiler: gcc - + - compiler: cxx diff --git a/src/recipe/parser/snapshots/rattler_build__recipe__parser__requirements__test__pin_package.snap b/src/recipe/parser/snapshots/rattler_build__recipe__parser__requirements__test__pin_package.snap index e09619c8c..fd916d994 100644 --- a/src/recipe/parser/snapshots/rattler_build__recipe__parser__requirements__test__pin_package.snap +++ b/src/recipe/parser/snapshots/rattler_build__recipe__parser__requirements__test__pin_package.snap @@ -1,6 +1,5 @@ --- source: src/recipe/parser/requirements.rs -assertion_line: 619 expression: "serde_yaml::to_string(&requirements).unwrap()" --- build: @@ -17,4 +16,3 @@ build: name: bar min_pin: x.x exact: true -- compiler: gcc diff --git a/src/recipe/snapshots/rattler_build__recipe__parser__tests__complete_recipe.snap b/src/recipe/snapshots/rattler_build__recipe__parser__tests__complete_recipe.snap index 989522bf5..0871d3cfa 100644 --- a/src/recipe/snapshots/rattler_build__recipe__parser__tests__complete_recipe.snap +++ b/src/recipe/snapshots/rattler_build__recipe__parser__tests__complete_recipe.snap @@ -84,7 +84,7 @@ build: replacement: simple_string requirements: build: - - compiler: c + - gcc_linux-64 - cmake host: - openssl diff --git a/src/recipe/snapshots/rattler_build__recipe__parser__tests__recipe_windows.snap b/src/recipe/snapshots/rattler_build__recipe__parser__tests__recipe_windows.snap index 30e51a9f5..f570ef74e 100644 --- a/src/recipe/snapshots/rattler_build__recipe__parser__tests__recipe_windows.snap +++ b/src/recipe/snapshots/rattler_build__recipe__parser__tests__recipe_windows.snap @@ -147,9 +147,23 @@ Recipe { }, requirements: Requirements { build: [ - Compiler( - Compiler { - language: "cxx", + Spec( + MatchSpec { + name: Some( + PackageName { + normalized: None, + source: "vs2017_win-64", + }, + ), + version: None, + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + md5: None, + sha256: None, }, ), Spec( diff --git a/src/recipe/snapshots/rattler_build__recipe__parser__tests__unix_recipe.snap b/src/recipe/snapshots/rattler_build__recipe__parser__tests__unix_recipe.snap index 26c2d04f9..0354c40b7 100644 --- a/src/recipe/snapshots/rattler_build__recipe__parser__tests__unix_recipe.snap +++ b/src/recipe/snapshots/rattler_build__recipe__parser__tests__unix_recipe.snap @@ -147,9 +147,23 @@ Recipe { }, requirements: Requirements { build: [ - Compiler( - Compiler { - language: "cxx", + Spec( + MatchSpec { + name: Some( + PackageName { + normalized: None, + source: "gxx_linux-64", + }, + ), + version: None, + build: None, + build_number: None, + file_name: None, + channel: None, + subdir: None, + namespace: None, + md5: None, + sha256: None, }, ), Spec( diff --git a/src/render/pin.rs b/src/render/pin.rs index c0b35e403..2c5608290 100644 --- a/src/render/pin.rs +++ b/src/render/pin.rs @@ -152,66 +152,6 @@ impl Pin { Ok(MatchSpec::from_str(spec.as_str(), ParseStrictness::Strict) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?) } - - pub(crate) fn internal_repr(&self) -> String { - let max_pin_str = if let Some(max_pin) = &self.args.max_pin { - format!("{}", max_pin) - } else { - "".to_string() - }; - - let min_pin_str = if let Some(min_pin) = &self.args.min_pin { - format!("{}", min_pin) - } else { - "".to_string() - }; - - format!( - "{} MAX_PIN={} MIN_PIN={} EXACT={}", - self.name.as_normalized(), - max_pin_str, - min_pin_str, - self.args.exact - ) - } - - pub(crate) fn from_internal_repr(s: &str) -> Self { - let parts = s.split(' ').collect::>(); - let name = parts[0].to_string(); - let max_pin = parts[1]; - let min_pin = parts[2]; - let exact = parts[3]; - - let max_pin = if max_pin == "MAX_PIN=" { - None - } else { - let max_pin = max_pin - .strip_prefix("MAX_PIN=") - .expect("Could not parse max pin: invalid prefix"); - Some(PinExpression::from_str(max_pin).expect("Could not parse max pin")) - }; - - let min_pin = if min_pin == "MIN_PIN=" { - None - } else { - let min_pin = min_pin - .strip_prefix("MIN_PIN=") - .expect("Could not parse min pin: invalid prefix"); - Some(PinExpression::from_str(min_pin).expect("Could not parse min pin")) - }; - - let exact = exact == "EXACT=true"; - let package_name = PackageName::try_from(name) - .expect("could not parse back package name from internal representation"); - Pin { - name: package_name, - args: PinArgs { - max_pin, - min_pin, - exact, - }, - } - } } #[cfg(test)] diff --git a/src/render/resolved_dependencies.rs b/src/render/resolved_dependencies.rs index 2890fc252..4919f7397 100644 --- a/src/render/resolved_dependencies.rs +++ b/src/render/resolved_dependencies.rs @@ -37,9 +37,6 @@ pub enum DependencyInfo { /// from the variant config Variant(VariantDependency), - /// This is a special compiler dependency (e.g. `{{ compiler('c') }}` - Compiler(CompilerDependency), - /// This is a special pin dependency (e.g. `{{ pin_subpackage('foo', exact=True) }}` PinSubpackage(PinSubpackageDependency), @@ -73,26 +70,6 @@ impl From for DependencyInfo { } } -/// This is a special compiler dependency (e.g. `{{ compiler('c') }}` -#[serde_as] -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(deny_unknown_fields)] -pub struct CompilerDependency { - /// The language in the `{{ compiler('c') }}` call. - #[serde(rename = "compiler")] - pub language: String, - - /// The resolved compiler spec - #[serde_as(as = "DisplayFromStr")] - pub spec: MatchSpec, -} - -impl From for DependencyInfo { - fn from(value: CompilerDependency) -> Self { - DependencyInfo::Compiler(value) - } -} - /// This is a special pin dependency (e.g. `{{ pin_subpackage('foo', exact=True) }}` #[serde_as] #[derive(Debug, Clone, Serialize, Deserialize)] @@ -174,7 +151,6 @@ impl DependencyInfo { pub fn spec(&self) -> &MatchSpec { match self { DependencyInfo::Variant(spec) => &spec.spec, - DependencyInfo::Compiler(spec) => &spec.spec, DependencyInfo::PinSubpackage(spec) => &spec.spec, DependencyInfo::PinCompatible(spec) => &spec.spec, DependencyInfo::RunExport(spec) => &spec.spec, @@ -186,7 +162,6 @@ impl DependencyInfo { if !long { match self { DependencyInfo::Variant(spec) => format!("{} (V)", &spec.spec), - DependencyInfo::Compiler(spec) => format!("{} (C)", &spec.spec), DependencyInfo::PinSubpackage(spec) => format!("{} (PS)", &spec.spec), DependencyInfo::PinCompatible(spec) => format!("{} (PC)", &spec.spec), DependencyInfo::RunExport(spec) => format!( @@ -198,7 +173,6 @@ impl DependencyInfo { } else { match self { DependencyInfo::Variant(spec) => format!("{} (from variant config)", &spec.spec), - DependencyInfo::Compiler(spec) => format!("{} (from compiler)", &spec.spec), DependencyInfo::PinSubpackage(spec) => { format!("{} (from pin subpackage)", &spec.spec) } @@ -248,13 +222,6 @@ impl DependencyInfo { _ => None, } } - - pub fn as_compiler(&self) -> Option<&CompilerDependency> { - match self { - DependencyInfo::Compiler(spec) => Some(spec), - _ => None, - } - } } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -450,7 +417,6 @@ pub fn apply_variant( ) -> Result, ResolveError> { let variant = &build_configuration.variant; let subpackages = &build_configuration.subpackages; - let target_platform = &build_configuration.target_platform; raw_specs .iter() @@ -475,8 +441,12 @@ pub fn apply_variant( // we split at whitespace to separate into version and build let mut splitter = spec.split_whitespace(); - let version_spec = splitter.next().map(|v| VersionSpec::from_str(v, ParseStrictness::Strict)).transpose()?; - let build_spec = splitter.next().map(StringMatcher::from_str).transpose()?; + let version_spec = splitter + .next() + .map(|v| VersionSpec::from_str(v, ParseStrictness::Strict)) + .transpose()?; + let build_spec = + splitter.next().map(StringMatcher::from_str).transpose()?; let variant = name.as_normalized().to_string(); let final_spec = MatchSpec { version: version_spec, @@ -486,7 +456,8 @@ pub fn apply_variant( return Ok(VariantDependency { spec: final_spec, variant, - }.into()); + } + .into()); } } } @@ -494,93 +465,35 @@ pub fn apply_variant( } Dependency::PinSubpackage(pin) => { let name = &pin.pin_value().name; - let subpackage = subpackages.get(name).ok_or(ResolveError::SubpackageNotFound(name.to_owned()))?; - let pinned = pin - .pin_value() - .apply( - &Version::from_str(&subpackage.version)?, - &subpackage.build_string, - )?; - Ok(PinSubpackageDependency { spec: pinned, name: name.as_normalized().to_string(), args: pin.pin_value().args.clone() }.into()) + let subpackage = subpackages + .get(name) + .ok_or(ResolveError::SubpackageNotFound(name.to_owned()))?; + let pinned = pin.pin_value().apply( + &Version::from_str(&subpackage.version)?, + &subpackage.build_string, + )?; + Ok(PinSubpackageDependency { + spec: pinned, + name: name.as_normalized().to_string(), + args: pin.pin_value().args.clone(), + } + .into()) } Dependency::PinCompatible(pin) => { let name = &pin.pin_value().name; - let pin_package = compatibility_specs.get(name) + let pin_package = compatibility_specs + .get(name) .ok_or(ResolveError::SubpackageNotFound(name.to_owned()))?; let pinned = pin .pin_value() - .apply( - &pin_package.version, - &pin_package.build, - )?; - Ok(PinCompatibleDependency { spec: pinned, name: name.as_normalized().to_string(), args: pin.pin_value().args.clone() }.into()) - } - Dependency::Compiler(compiler) => { - if target_platform == &Platform::NoArch { - return Err(ResolveError::CompilerError("Noarch packages cannot have compilers".to_string())) + .apply(&pin_package.version, &pin_package.build)?; + Ok(PinCompatibleDependency { + spec: pinned, + name: name.as_normalized().to_string(), + args: pin.pin_value().args.clone(), } - - let compiler_variant = format!("{}_compiler", compiler.language()); - let compiler_name = variant - .get(&compiler_variant) - .map(|s| s.to_string()) - .unwrap_or_else(|| { - if target_platform.is_linux() { - let default_compiler = match compiler.language() { - "c" => "gcc".to_string(), - "cxx" => "gxx".to_string(), - "fortran" => "gfortran".to_string(), - "rust" => "rust".to_string(), - _ => "".to_string() - }; - default_compiler - } else if target_platform.is_osx() { - let default_compiler = match compiler.language() { - "c" => "clang".to_string(), - "cxx" => "clangxx".to_string(), - "fortran" => "gfortran".to_string(), - "rust" => "rust".to_string(), - _ => "".to_string() - }; - default_compiler - } else if target_platform.is_windows() { - let default_compiler = match compiler.language() { - // note with conda-build, these are dependent on the python version - // we could also check the variant for the python version here! - "c" => "vs2017".to_string(), - "cxx" => "vs2017".to_string(), - "fortran" => "gfortran".to_string(), - "rust" => "rust".to_string(), - _ => "".to_string() - }; - default_compiler - } else { - "".to_string() - } - }); - - if compiler_name.is_empty() { - return Err(ResolveError::CompilerError( - format!("Could not find compiler for {}. Configure {}_compiler in your variant config file for {target_platform}.", compiler.language(), compiler.language()))); - } - - let compiler_version_variant = format!("{}_version", compiler_variant); - let compiler_version = variant.get(&compiler_version_variant); - - let final_compiler = if let Some(compiler_version) = compiler_version { - format!( - "{}_{} ={}", - compiler_name, target_platform, compiler_version - ) - } else { - format!("{}_{}", compiler_name, target_platform) - }; - - Ok(CompilerDependency { - language: compiler_name, - spec: MatchSpec::from_str(&final_compiler, ParseStrictness::Strict)?, - }.into()) + .into()) } } }) @@ -967,11 +880,6 @@ mod tests { variant: "bar".to_string(), } .into(), - CompilerDependency { - language: "c".to_string(), - spec: MatchSpec::from_str("foo", ParseStrictness::Strict).unwrap(), - } - .into(), PinSubpackageDependency { name: "baz".to_string(), spec: MatchSpec::from_str("baz", ParseStrictness::Strict).unwrap(), @@ -998,11 +906,10 @@ mod tests { // test deserialize let dep_info: Vec = serde_yaml::from_str(&yaml_str).unwrap(); - assert_eq!(dep_info.len(), 5); + assert_eq!(dep_info.len(), 4); assert!(matches!(dep_info[0], DependencyInfo::Source(_))); assert!(matches!(dep_info[1], DependencyInfo::Variant(_))); - assert!(matches!(dep_info[2], DependencyInfo::Compiler(_))); - assert!(matches!(dep_info[3], DependencyInfo::PinSubpackage(_))); - assert!(matches!(dep_info[4], DependencyInfo::PinCompatible(_))); + assert!(matches!(dep_info[2], DependencyInfo::PinSubpackage(_))); + assert!(matches!(dep_info[3], DependencyInfo::PinCompatible(_))); } } diff --git a/src/render/snapshots/rattler_build__render__resolved_dependencies__tests__dependency_info_render.snap b/src/render/snapshots/rattler_build__render__resolved_dependencies__tests__dependency_info_render.snap index 9b765c4c3..20229e8fd 100644 --- a/src/render/snapshots/rattler_build__render__resolved_dependencies__tests__dependency_info_render.snap +++ b/src/render/snapshots/rattler_build__render__resolved_dependencies__tests__dependency_info_render.snap @@ -1,13 +1,10 @@ --- source: src/render/resolved_dependencies.rs -assertion_line: 994 expression: yaml_str --- - source: xyz - variant: bar spec: foo -- compiler: c - spec: foo - pin_subpackage: baz max_pin: x.x min_pin: x.x.x diff --git a/src/selectors.rs b/src/selectors.rs index 8da2b9e2e..129a8fcce 100644 --- a/src/selectors.rs +++ b/src/selectors.rs @@ -22,6 +22,8 @@ pub struct SelectorConfig { pub variant: BTreeMap, /// Enable experimental features pub experimental: bool, + /// Allow undefined variables + pub allow_undefined: bool, } impl SelectorConfig { @@ -98,6 +100,7 @@ impl Default for SelectorConfig { hash: None, variant: Default::default(), experimental: false, + allow_undefined: false, } } } diff --git a/src/snapshots/rattler_build__metadata__test__curl_recipe.yaml.snap b/src/snapshots/rattler_build__metadata__test__curl_recipe.yaml.snap index 389f0f339..9c34ce4d2 100644 --- a/src/snapshots/rattler_build__metadata__test__curl_recipe.yaml.snap +++ b/src/snapshots/rattler_build__metadata__test__curl_recipe.yaml.snap @@ -1,6 +1,5 @@ --- source: src/metadata.rs -assertion_line: 732 expression: output --- recipe: @@ -16,7 +15,7 @@ recipe: string: h60d57d3_0 requirements: build: - - compiler: c + - clang_osx-arm64 - make - perl - pkg-config @@ -58,8 +57,7 @@ build_configuration: finalized_dependencies: build: specs: - - compiler: c - spec: clang_osx-arm64 + - source: clang_osx-arm64 - source: make - source: perl - source: pkg-config diff --git a/src/used_variables.rs b/src/used_variables.rs index b82328772..61254bb21 100644 --- a/src/used_variables.rs +++ b/src/used_variables.rs @@ -66,6 +66,11 @@ fn extract_variable_from_expression(expr: &Expr, variables: &mut HashSet variables.insert(format!("{}_compiler", &constant.value)); variables.insert(format!("{}_compiler_version", &constant.value)); } + } else if function == "stdlib" { + if let Expr::Const(constant) = &call.args[0] { + variables.insert(format!("{}_stdlib", &constant.value)); + variables.insert(format!("{}_stdlib_version", &constant.value)); + } } else if function == "pin_subpackage" { if let Expr::Const(constant) = &call.args[0] { variables.insert(format!("{}", &constant.value)); @@ -298,6 +303,7 @@ mod test { - if: osx then: osx-clang - ${{ compiler('c') }} + - ${{ stdlib('c') }} - ${{ pin_subpackage('abcdef') }} "#; @@ -308,6 +314,8 @@ mod test { assert!(used_vars.contains("osx")); assert!(used_vars.contains("c_compiler")); assert!(used_vars.contains("c_compiler_version")); + assert!(used_vars.contains("c_stdlib")); + assert!(used_vars.contains("c_stdlib_version")); assert!(used_vars.contains("abcdef")); } diff --git a/src/variant_config.rs b/src/variant_config.rs index 9577ab169..52975547b 100644 --- a/src/variant_config.rs +++ b/src/variant_config.rs @@ -642,8 +642,6 @@ impl VariantConfig { exact_pins.insert(val); } } - // Be explicit about the other cases, so we can add them later - Dependency::Compiler(_) => (), }); // actually used vars diff --git a/test-data/rendered_recipes/curl_recipe.yaml b/test-data/rendered_recipes/curl_recipe.yaml index 9d2500e22..d91f11cce 100644 --- a/test-data/rendered_recipes/curl_recipe.yaml +++ b/test-data/rendered_recipes/curl_recipe.yaml @@ -11,7 +11,7 @@ recipe: string: h60d57d3_0 requirements: build: - - compiler: c + - clang_osx-arm64 - make - perl - pkg-config @@ -55,8 +55,7 @@ build_configuration: finalized_dependencies: build: specs: - - compiler: c - spec: clang_osx-arm64 + - source: clang_osx-arm64 - source: make - source: perl - source: pkg-config