diff --git a/src/cache.rs b/src/cache.rs index a4ce6de6..f5d8d473 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -187,6 +187,7 @@ impl Output { &self.build_configuration.directories.recipe_dir, &self.build_configuration.directories.host_prefix, Some(&self.build_configuration.directories.build_prefix), + None, // TODO fix this to be proper Jinja context ) .await .into_diagnostic()?; diff --git a/src/metadata.rs b/src/metadata.rs index 914a8094..492f32f7 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -29,7 +29,10 @@ use url::Url; use crate::{ console_utils::github_integration_enabled, hash::HashInfo, - recipe::parser::{Recipe, Source}, + recipe::{ + jinja::SelectorConfig, + parser::{Recipe, Source}, + }, render::resolved_dependencies::FinalizedDependencies, system_tools::SystemTools, tool_configuration, @@ -279,6 +282,19 @@ impl BuildConfiguration { pub fn cross_compilation(&self) -> bool { self.target_platform != self.build_platform } + + /// Construct a `SelectorConfig` from the given `BuildConfiguration` + pub fn selector_config(&self) -> SelectorConfig { + SelectorConfig { + target_platform: self.target_platform, + host_platform: self.host_platform, + build_platform: self.build_platform, + variant: self.variant.clone(), + hash: Some(self.hash.clone()), + experimental: false, + allow_undefined: false, + } + } } /// A package identifier diff --git a/src/package_test/run_test.rs b/src/package_test/run_test.rs index 121e24d1..c2b3c5db 100644 --- a/src/package_test/run_test.rs +++ b/src/package_test/run_test.rs @@ -124,7 +124,7 @@ impl Tests { })?; script - .run_script(env_vars, tmp_dir.path(), cwd, environment, None) + .run_script(env_vars, tmp_dir.path(), cwd, environment, None, None) .await .map_err(|e| TestError::TestFailed(e.to_string()))?; } @@ -136,7 +136,7 @@ impl Tests { }; script - .run_script(env_vars, tmp_dir.path(), cwd, environment, None) + .run_script(env_vars, tmp_dir.path(), cwd, environment, None, None) .await .map_err(|e| TestError::TestFailed(e.to_string()))?; } @@ -472,7 +472,7 @@ impl PythonTest { let tmp_dir = tempfile::tempdir()?; script - .run_script(Default::default(), tmp_dir.path(), path, prefix, None) + .run_script(Default::default(), tmp_dir.path(), path, prefix, None, None) .await .map_err(|e| TestError::TestFailed(e.to_string()))?; @@ -487,7 +487,7 @@ impl PythonTest { ..Script::default() }; script - .run_script(Default::default(), path, path, prefix, None) + .run_script(Default::default(), path, path, prefix, None, None) .await .map_err(|e| TestError::TestFailed(e.to_string()))?; @@ -588,7 +588,14 @@ impl CommandsTest { tracing::info!("Testing commands:"); self.script - .run_script(env_vars, tmp_dir.path(), path, &run_env, build_env.as_ref()) + .run_script( + env_vars, + tmp_dir.path(), + path, + &run_env, + build_env.as_ref(), + None, + ) .await .map_err(|e| TestError::TestFailed(e.to_string()))?; diff --git a/src/recipe/custom_yaml/rendered.rs b/src/recipe/custom_yaml/rendered.rs index 34dc6439..1e7251b3 100644 --- a/src/recipe/custom_yaml/rendered.rs +++ b/src/recipe/custom_yaml/rendered.rs @@ -228,6 +228,7 @@ impl TryFrom<&marked_yaml::Node> for RenderedNode { #[derive(Clone)] pub struct RenderedScalarNode { span: marked_yaml::Span, + source: String, value: String, } @@ -241,12 +242,16 @@ impl Serialize for RenderedScalarNode { } impl RenderedScalarNode { - pub fn new(span: marked_yaml::Span, value: String) -> Self { - Self { span, value } + pub fn new(span: marked_yaml::Span, source: String, value: String) -> Self { + Self { + span, + source, + value, + } } pub fn new_blank() -> Self { - Self::new(marked_yaml::Span::new_blank(), String::new()) + Self::new(marked_yaml::Span::new_blank(), String::new(), String::new()) } /// Treat the scalar node as a string @@ -256,6 +261,11 @@ impl RenderedScalarNode { &self.value } + /// Return the source with the original Jinja template + pub fn source(&self) -> &str { + &self.source + } + /// Treat the scalar node as a boolean /// /// If the scalar contains any of the following then it is true: @@ -320,14 +330,18 @@ impl fmt::Debug for RenderedScalarNode { impl<'a> From<&'a str> for RenderedScalarNode { /// Convert from any borrowed string into a node fn from(value: &'a str) -> Self { - Self::new(marked_yaml::Span::new_blank(), value.to_owned()) + Self::new( + marked_yaml::Span::new_blank(), + value.to_owned(), + value.to_owned(), + ) } } impl From for RenderedScalarNode { /// Convert from any owned string into a node fn from(value: String) -> Self { - Self::new(marked_yaml::Span::new_blank(), value) + Self::new(marked_yaml::Span::new_blank(), value.clone(), value) } } @@ -353,7 +367,11 @@ impl From for RenderedScalarNode { impl From<&MarkedScalarNode> for RenderedScalarNode { fn from(value: &MarkedScalarNode) -> Self { - Self::new(*value.span(), value.as_str().to_owned()) + Self::new( + *value.span(), + value.as_str().to_owned(), + value.as_str().to_owned(), + ) } } @@ -614,6 +632,7 @@ impl Render for Node { Node::Null(n) => Ok(RenderedNode::Null(RenderedScalarNode::new( *n.span(), n.as_str().to_owned(), + n.as_str().to_owned(), ))), } } @@ -628,7 +647,7 @@ impl Render for ScalarNode { label = jinja_error_to_label(&err), )] })?; - let rendered = RenderedScalarNode::new(*self.span(), rendered); + let rendered = RenderedScalarNode::new(*self.span(), self.as_str().to_string(), rendered); if rendered.is_empty() { Ok(RenderedNode::Null(rendered)) @@ -652,7 +671,7 @@ impl Render> for ScalarNode { )] })?; - let rendered = RenderedScalarNode::new(*self.span(), rendered); + let rendered = RenderedScalarNode::new(*self.span(), self.as_str().to_string(), rendered); if rendered.is_empty() { Ok(None) @@ -679,7 +698,11 @@ impl Render for MappingNode { let mut rendered = IndexMap::new(); for (key, value) in self.iter() { - let key = RenderedScalarNode::new(*key.span(), key.as_str().to_owned()); + let key = RenderedScalarNode::new( + *key.span(), + key.as_str().to_owned(), + key.as_str().to_owned(), + ); let value: RenderedNode = value.render(jinja, &format!("{name}.{}", key.as_str()))?; if value.is_null() { continue; @@ -700,6 +723,7 @@ impl Render for SequenceNode { return Ok(RenderedNode::Null(RenderedScalarNode::new( *self.span(), String::new(), + String::new(), ))); } diff --git a/src/recipe/parser.rs b/src/recipe/parser.rs index e8e27f81..360cdc0a 100644 --- a/src/recipe/parser.rs +++ b/src/recipe/parser.rs @@ -60,6 +60,8 @@ use crate::recipe::custom_yaml::Node; pub struct Recipe { /// The schema version of this recipe YAML file pub schema_version: u64, + /// The context values of this recipe + pub context: IndexMap, /// The package information pub package: Package, /// The cache build that should be used for this package @@ -155,37 +157,38 @@ impl Recipe { })?; // add context values - if let Some(context) = root_node.get("context") { - let context = context.as_mapping().ok_or_else(|| { + let mut context = IndexMap::new(); + + if let Some(context_map) = root_node.get("context") { + let context_map = context_map.as_mapping().ok_or_else(|| { vec![_partialerror!( - *context.span(), + *context_map.span(), ErrorKind::ExpectedMapping, help = "`context` must always be a mapping" )] })?; - context - .iter() - .map(|(k, v)| { - let val = v.as_scalar().ok_or_else(|| { - vec![_partialerror!( - *v.span(), - ErrorKind::ExpectedScalar, - help = "`context` values must always be scalars (strings)" - )] - })?; - let rendered: Option = - val.render(&jinja, &format!("context.{}", k.as_str()))?; - - if let Some(rendered) = rendered { - jinja.context_mut().insert( - k.as_str().to_owned(), - Value::from_safe_string(rendered.as_str().to_string()), - ); - } - Ok(()) - }) - .flatten_errors()?; + for (k, v) in context_map.iter() { + let val = v.as_scalar().ok_or_else(|| { + vec![_partialerror!( + *v.span(), + ErrorKind::ExpectedScalar, + help = "`context` values must always be scalars (strings)" + )] + })?; + let rendered: Option = + val.render(&jinja, &format!("context.{}", k.as_str()))?; + + if let Some(rendered) = rendered { + context.insert(k.as_str().to_string(), rendered.as_str().to_string()); + // also immediately insert into jinja context so that the value can be used + // in later jinja expressions + jinja.context_mut().insert( + k.as_str().to_string(), + Value::from_safe_string(rendered.as_str().to_string()), + ); + } + } } let rendered_node: RenderedMappingNode = root_node.render(&jinja, "ROOT")?; @@ -261,6 +264,7 @@ impl Recipe { let recipe = Recipe { schema_version, + context, package: package.ok_or_else(|| { vec![_partialerror!( *root_node.span(), diff --git a/src/recipe/parser/script.rs b/src/recipe/parser/script.rs index aeb43300..abcb721e 100644 --- a/src/recipe/parser/script.rs +++ b/src/recipe/parser/script.rs @@ -217,16 +217,19 @@ impl TryConvertNode