From a28628bc6d29e227dffc1a64c27fa5f0b50f7dc9 Mon Sep 17 00:00:00 2001 From: Bas Zalmstra Date: Fri, 16 Aug 2024 16:35:43 +0200 Subject: [PATCH] refactor: make build string optional in recipe (#1020) --- src/bin/generate-cli-docs.rs | 2 +- src/build.rs | 41 ++++----- src/env_vars.rs | 4 +- src/lib.rs | 4 +- src/metadata.rs | 72 +++++++++------- src/opt.rs | 3 +- src/packaging.rs | 44 +++++----- src/packaging/metadata.rs | 5 +- src/recipe/parser.rs | 24 ------ src/recipe/parser/build.rs | 84 ++++++++++++++++++- ...recipe__parser__tests__recipe_windows.snap | 2 +- ...d__recipe__parser__tests__unix_recipe.snap | 2 +- src/tui/render.rs | 27 +++--- src/tui/state.rs | 4 +- src/variant_config.rs | 8 +- 15 files changed, 190 insertions(+), 136 deletions(-) diff --git a/src/bin/generate-cli-docs.rs b/src/bin/generate-cli-docs.rs index a6ce8399..7a4ac65d 100644 --- a/src/bin/generate-cli-docs.rs +++ b/src/bin/generate-cli-docs.rs @@ -12,7 +12,7 @@ fn main() { let target_default_platform = format!("Default value: `{}`", Platform::current()); let help = help.replace( target_default_platform.as_str(), - format!("Default value: current platform").as_str(), + "Default value: current platform", ); print!("{}", help); diff --git a/src/build.rs b/src/build.rs index f085ee0b..61d63515 100644 --- a/src/build.rs +++ b/src/build.rs @@ -1,19 +1,19 @@ -//! The build module contains the code for running the build process for a given [`Output`] -use rattler_conda_types::{Channel, MatchSpec, ParseStrictness}; -use std::path::PathBuf; -use std::vec; +//! The build module contains the code for running the build process for a given +//! [`Output`] +use std::{path::PathBuf, vec}; use miette::IntoDiagnostic; +use rattler_conda_types::{Channel, MatchSpec, ParseStrictness}; use rattler_index::index; use rattler_solve::{ChannelPriority, SolveStrategy}; -use crate::metadata::Output; -use crate::package_test::TestConfiguration; -use crate::recipe::parser::TestType; -use crate::render::solver::load_repodatas; -use crate::{package_test, tool_configuration}; +use crate::{ + metadata::Output, package_test, package_test::TestConfiguration, recipe::parser::TestType, + render::solver::load_repodatas, tool_configuration, +}; -/// Check if the build should be skipped because it already exists in any of the channels +/// Check if the build should be skipped because it already exists in any of the +/// channels pub async fn skip_existing( mut outputs: Vec, tool_configuration: &tool_configuration::Configuration, @@ -78,14 +78,11 @@ pub async fn skip_existing( "{}-{}-{}", output.name().as_normalized(), output.version(), - output.build_string().unwrap_or_default() + &output.build_string() )); if exists { // The identifier should always be set at this point - tracing::info!( - "Skipping build for {}", - output.identifier().as_deref().unwrap_or("unknown") - ); + tracing::info!("Skipping build for {}", output.identifier()); } !exists }); @@ -93,23 +90,20 @@ pub async fn skip_existing( Ok(outputs) } -/// Run the build for the given output. This will fetch the sources, resolve the dependencies, -/// and execute the build script. Returns the path to the resulting package. +/// Run the build for the given output. This will fetch the sources, resolve the +/// dependencies, and execute the build script. Returns the path to the +/// resulting package. pub async fn run_build( output: Output, tool_configuration: &tool_configuration::Configuration, ) -> miette::Result<(Output, PathBuf)> { - if output.build_string().is_none() { - miette::bail!("Build string is not set for {:?}", output.name()); - } - output .build_configuration .directories .create_build_dir() .into_diagnostic()?; - let span = tracing::info_span!("Running build for", recipe = output.identifier().unwrap()); + let span = tracing::info_span!("Running build for", recipe = output.identifier()); let _enter = span.enter(); output.record_build_start(); @@ -148,7 +142,8 @@ pub async fn run_build( // We run all the package content tests for test in output.recipe.tests() { - // TODO we could also run each of the (potentially multiple) test scripts and collect the errors + // TODO we could also run each of the (potentially multiple) test scripts and + // collect the errors if let TestType::PackageContents { package_contents } = test { package_contents .run_test(&paths_json, &output.build_configuration.target_platform) diff --git a/src/env_vars.rs b/src/env_vars.rs index 2ed0a866..c1e1f0c4 100644 --- a/src/env_vars.rs +++ b/src/env_vars.rs @@ -292,8 +292,8 @@ pub fn vars(output: &Output, build_state: &str) -> HashMap { .recipe .build() .string() - .map(|s| s.to_string()) - .unwrap_or_else(|| hash.to_string()) + .resolve(&hash, output.recipe.build().number) + .into_owned() ); insert!(vars, "PKG_HASH", hash); diff --git a/src/lib.rs b/src/lib.rs index 5fb7356c..ecf76bba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -271,8 +271,8 @@ pub async fn get_build_output( build_string: recipe .build() .string() - .expect("Shouldn't be unset, needs major refactoring, for handling this better") - .to_owned(), + .resolve(&hash, recipe.build().number()) + .into_owned(), }, ); diff --git a/src/metadata.rs b/src/metadata.rs index c5211aa6..f2f084c8 100644 --- a/src/metadata.rs +++ b/src/metadata.rs @@ -1,5 +1,6 @@ //! All the metadata that makes up a recipe file use std::{ + borrow::Cow, collections::BTreeMap, fmt::{self, Display, Formatter}, io::Write, @@ -72,7 +73,8 @@ pub struct Directories { /// Exposed as `$PREFIX` (or `%PREFIX%` on Windows) in the build script pub host_prefix: PathBuf, /// The build prefix is the directory where build dependencies are installed - /// Exposed as `$BUILD_PREFIX` (or `%BUILD_PREFIX%` on Windows) in the build script + /// Exposed as `$BUILD_PREFIX` (or `%BUILD_PREFIX%` on Windows) in the build + /// script pub build_prefix: PathBuf, /// The work directory is the directory where the source code is copied to pub work_dir: PathBuf, @@ -210,7 +212,8 @@ fn default_true() -> bool { pub struct PackagingSettings { /// The archive type, currently supported are `tar.bz2` and `conda` pub archive_type: ArchiveType, - /// The compression level from 1-9 or -7-22 for `tar.bz2` and `conda` archives + /// The compression level from 1-9 or -7-22 for `tar.bz2` and `conda` + /// archives pub compression_level: i32, } @@ -235,7 +238,8 @@ impl PackagingSettings { pub struct BuildConfiguration { /// The target platform for the build pub target_platform: Platform, - /// The host platform (usually target platform, but for `noarch` it's the build platform) + /// The host platform (usually target platform, but for `noarch` it's the + /// build platform) pub host_platform: Platform, /// The build platform (the platform that the build is running on) pub build_platform: Platform, @@ -253,14 +257,17 @@ pub struct BuildConfiguration { pub solve_strategy: SolveStrategy, /// The timestamp to use for the build pub timestamp: chrono::DateTime, - /// All subpackages coming from this output or other outputs from the same recipe + /// All subpackages coming from this output or other outputs from the same + /// recipe pub subpackages: BTreeMap, /// Package format (.tar.bz2 or .conda) pub packaging_settings: PackagingSettings, - /// Whether to store the recipe and build instructions in the final package or not + /// Whether to store the recipe and build instructions in the final package + /// or not #[serde(skip_serializing, default = "default_true")] pub store_recipe: bool, - /// Whether to set additional environment variables to force colors in the build script or not + /// Whether to set additional environment variables to force colors in the + /// build script or not #[serde(skip_serializing, default = "default_true")] pub force_colors: bool, } @@ -301,21 +308,24 @@ pub struct BuildSummary { pub failed: bool, } -/// A output. This is the central element that is passed to the `run_build` function -/// and fully specifies all the options and settings to run the build. +/// A output. This is the central element that is passed to the `run_build` +/// function and fully specifies all the options and settings to run the build. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Output { /// The rendered recipe that is used to build this output pub recipe: Recipe, - /// The build configuration for this output (e.g. target_platform, channels, and other settings) + /// The build configuration for this output (e.g. target_platform, channels, + /// and other settings) pub build_configuration: BuildConfiguration, - /// The finalized dependencies for this output. If this is `None`, the dependencies have not been resolved yet. - /// During the `run_build` functions, the dependencies are resolved and this field is filled. + /// The finalized dependencies for this output. If this is `None`, the + /// dependencies have not been resolved yet. During the `run_build` + /// functions, the dependencies are resolved and this field is filled. pub finalized_dependencies: Option, - /// The finalized dependencies from the cache (if there is a cache instruction) + /// The finalized dependencies from the cache (if there is a cache + /// instruction) pub finalized_cache_dependencies: Option, - /// The finalized sources for this output. Contain the exact git hashes for the sources that are used - /// to build this output. + /// The finalized sources for this output. Contain the exact git hashes for + /// the sources that are used to build this output. pub finalized_sources: Option>, /// Summary of the build @@ -341,9 +351,13 @@ impl Output { self.recipe.package().version() } - /// The build string is usually set automatically as the hash of the variant configuration. - pub fn build_string(&self) -> Option<&str> { - self.recipe.build().string() + /// The build string is either the build string from the recipe or computed + /// from the hash and build number. + pub fn build_string(&self) -> Cow<'_, str> { + self.recipe + .build() + .string + .resolve(&self.build_configuration.hash, self.recipe.build().number) } /// The channels to use when resolving dependencies @@ -358,13 +372,13 @@ impl Output { } /// retrieve an identifier for this output ({name}-{version}-{build_string}) - pub fn identifier(&self) -> Option { - Some(format!( + pub fn identifier(&self) -> String { + format!( "{}-{}-{}", self.name().as_normalized(), self.version(), - self.build_string()? - )) + &self.build_string() + ) } /// Record a warning during the build @@ -420,7 +434,8 @@ impl Output { } /// Search for the resolved package with the given name in the host prefix - /// Returns a tuple of the package and a boolean indicating whether the package is directly requested + /// Returns a tuple of the package and a boolean indicating whether the + /// package is directly requested pub fn find_resolved_package(&self, name: &str) -> Option<(&RepoDataRecord, bool)> { let host = self.finalized_dependencies.as_ref()?.host.as_ref()?; let record = host @@ -442,7 +457,7 @@ impl Output { /// Print a nice summary of the build pub fn log_build_summary(&self) -> Result<(), std::io::Error> { let summary = self.build_summary.lock().unwrap(); - let identifier = self.identifier().unwrap_or_default(); + let identifier = self.identifier(); let span = tracing::info_span!("Build summary for", recipe = identifier); let _enter = span.enter(); @@ -574,11 +589,7 @@ impl Output { table }; - writeln!( - f, - "Variant configuration (hash: {}):", - self.build_string().unwrap_or_default() - )?; + writeln!(f, "Variant configuration (hash: {}):", self.build_string())?; let mut table = template(); if table_format != comfy_table::presets::UTF8_FULL { table.set_header(vec!["Key", "Value"]); @@ -642,7 +653,6 @@ mod tests { #[cfg(test)] mod test { - use rstest::*; use std::str::FromStr; use chrono::TimeZone; @@ -652,11 +662,11 @@ mod test { VersionWithSource, }; use rattler_digest::{parse_digest_from_hex, Md5, Sha256}; + use rstest::*; use url::Url; - use crate::render::resolved_dependencies::{self, SourceDependency}; - use super::{Directories, Output}; + use crate::render::resolved_dependencies::{self, SourceDependency}; #[test] fn test_directories_yaml_rendering() { diff --git a/src/opt.rs b/src/opt.rs index aa1909b9..9b339d68 100644 --- a/src/opt.rs +++ b/src/opt.rs @@ -21,6 +21,7 @@ use url::Url; /// Application subcommands. #[derive(Parser)] +#[allow(clippy::large_enum_variant)] pub enum SubCommands { /// Build a package from a recipe Build(BuildOpts), @@ -358,7 +359,7 @@ fn is_dir(dir: &str) -> Result { /// Parse a single key-value pair fn parse_key_val(s: &str) -> Result<(String, Value), Box> { let (key, value) = s - .split_once("=") + .split_once('=') .ok_or_else(|| format!("invalid KEY=value: no `=` found in `{}`", s))?; Ok((key.to_string(), json!(value))) } diff --git a/src/packaging.rs b/src/packaging.rs index 784335ea..15ef95e5 100644 --- a/src/packaging.rs +++ b/src/packaging.rs @@ -1,13 +1,17 @@ -//! This module contains the functions to package a conda package from a given output. +//! This module contains the functions to package a conda package from a given +//! output. +use std::{ + collections::HashSet, + io::Write, + path::{Component, Path, PathBuf}, +}; + use fs_err as fs; use fs_err::File; -use rattler_conda_types::Platform; -use std::collections::HashSet; -use std::io::Write; -use std::path::{Component, Path, PathBuf}; - -use rattler_conda_types::package::PathsJson; -use rattler_conda_types::package::{ArchiveType, PackageFile}; +use rattler_conda_types::{ + package::{ArchiveType, PackageFile, PathsJson}, + Platform, +}; use rattler_package_streaming::write::{ write_conda_package, write_tar_bz2_package, CompressionLevel, }; @@ -18,9 +22,7 @@ mod metadata; pub use file_finder::{content_type, Files, TempFiles}; pub use metadata::{contains_prefix_binary, contains_prefix_text, create_prefix_placeholder}; -use crate::metadata::Output; -use crate::package_test::write_test_files; -use crate::{post_process, tool_configuration}; +use crate::{metadata::Output, package_test::write_test_files, post_process, tool_configuration}; #[allow(missing_docs)] #[derive(Debug, thiserror::Error)] @@ -157,7 +159,8 @@ fn write_recipe_folder( .write_all(serde_yaml::to_string(&output.build_configuration.variant)?.as_bytes())?; files.push(variant_config_file); - // Write out the "rendered" recipe as well (the recipe with all the variables replaced with their values) + // Write out the "rendered" recipe as well (the recipe with all the variables + // replaced with their values) let rendered_recipe_file = recipe_folder.join("rendered_recipe.yaml"); let mut rendered_recipe = File::create(&rendered_recipe_file)?; rendered_recipe.write_all(serde_yaml::to_string(&output)?.as_bytes())?; @@ -277,9 +280,7 @@ pub fn package_conda( )?; } - let identifier = output - .identifier() - .ok_or(PackagingError::BuildStringNotSet)?; + let identifier = output.identifier(); let out_path = output_folder.join(format!( "{}{}", identifier, @@ -326,10 +327,10 @@ pub fn package_conda( Ok((out_path, paths_json)) } -/// When building package for noarch, we don't create another build-platform folder -/// together with noarch but conda-build does -/// because of this we have a failure in conda-smithy CI so we also *mimic* this behaviour -/// until this behaviour is changed +/// When building package for noarch, we don't create another build-platform +/// folder together with noarch but conda-build does +/// because of this we have a failure in conda-smithy CI so we also *mimic* this +/// behaviour until this behaviour is changed /// https://github.com/conda-forge/conda-forge-ci-setup-feedstock/blob/main/recipe/conda_forge_ci_setup/feedstock_outputs.py#L164 fn create_empty_build_folder( local_channel_dir: &Path, @@ -345,8 +346,9 @@ fn create_empty_build_folder( } impl Output { - /// Create a conda package from any new files in the host prefix. Note: the previous stages should have been - /// completed before calling this function. + /// Create a conda package from any new files in the host prefix. Note: the + /// previous stages should have been completed before calling this + /// function. pub async fn create_package( &self, tool_configuration: &tool_configuration::Configuration, diff --git a/src/packaging/metadata.rs b/src/packaging/metadata.rs index 595a5f0b..54176dc9 100644 --- a/src/packaging/metadata.rs +++ b/src/packaging/metadata.rs @@ -276,10 +276,7 @@ impl Output { Ok(IndexJson { name: self.name().clone(), version: self.version().clone().into(), - build: self - .build_string() - .ok_or(PackagingError::BuildStringNotSet)? - .to_string(), + build: self.build_string().into_owned(), build_number: recipe.build().number(), arch, platform, diff --git a/src/recipe/parser.rs b/src/recipe/parser.rs index 01f1f8a0..e8e27f81 100644 --- a/src/recipe/parser.rs +++ b/src/recipe/parser.rs @@ -138,28 +138,11 @@ impl Recipe { }) } - /// Build a recipe from a YAML string and use a given package hash string as default value. - pub fn from_yaml_with_default_hash_str( - yaml: &str, - default_pkg_hash: &str, - jinja_opt: SelectorConfig, - ) -> Result> { - let mut recipe = Self::from_yaml(yaml, jinja_opt)?; - - // Set the build string to the package hash if it is not set - if recipe.build.string.is_none() { - recipe.build.string = Some(format!("{}_{}", default_pkg_hash, recipe.build.number)); - } - - Ok(recipe) - } - /// Create recipes from a YAML [`Node`] structure. pub fn from_node( root_node: &Node, jinja_opt: SelectorConfig, ) -> Result> { - let hash = jinja_opt.hash.clone(); let experimental = jinja_opt.experimental; let mut jinja = Jinja::new(jinja_opt); @@ -268,13 +251,6 @@ impl Recipe { }) .flatten_errors()?; - // Add hash to build.string if it is not set - if build.string.is_none() { - if let Some(hash) = hash { - build.string = Some(format!("{}_{}", hash, build.number)); - } - } - // evaluate the skip conditions build.skip = build.skip.with_eval(&jinja)?; diff --git a/src/recipe/parser/build.rs b/src/recipe/parser/build.rs index 9d8ba7d8..8de160de 100644 --- a/src/recipe/parser/build.rs +++ b/src/recipe/parser/build.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::str::FromStr; use rattler_conda_types::{package::EntryPoint, NoArchType}; @@ -9,6 +10,7 @@ use crate::recipe::custom_yaml::RenderedSequenceNode; use crate::recipe::parser::script::Script; use crate::recipe::parser::skip::Skip; +use crate::hash::HashInfo; use crate::validate_keys; use crate::{ _partialerror, @@ -72,8 +74,8 @@ pub struct Build { pub number: u64, /// The build string is usually set automatically as the hash of the variant configuration. /// It's possible to override this by setting it manually, but not recommended. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub string: Option, + #[serde(default, skip_serializing_if = "BuildString::is_derived")] + pub string: BuildString, /// List of conditions under which to skip the build of the package. #[serde(default, skip)] pub skip: Skip, @@ -113,6 +115,80 @@ pub struct Build { pub files: GlobVec, } +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(from = "Option", into = "Option")] +pub enum BuildString { + /// The build string is explicitly set by the user. + UserSpecified(String), + + /// The build string should be derived from the variants + #[default] + Derived, +} + +impl From> for BuildString { + fn from(value: Option) -> Self { + value.map_or_else(|| BuildString::Derived, BuildString::UserSpecified) + } +} + +impl From for Option { + fn from(value: BuildString) -> Self { + match value { + BuildString::UserSpecified(s) => Some(s), + BuildString::Derived => None, + } + } +} + +impl From for BuildString { + fn from(value: String) -> Self { + BuildString::UserSpecified(value) + } +} + +impl BuildString { + /// Returns true if the build string should be derived from the variants. + pub fn is_derived(&self) -> bool { + matches!(self, BuildString::Derived) + } + + /// Returns the user specified build string. + pub fn as_deref(&self) -> Option<&str> { + match self { + BuildString::UserSpecified(s) => Some(s), + BuildString::Derived => None, + } + } + + /// Returns the final build string, either based on the user defined value or by computing the derived value. + pub fn resolve(&self, hash: &HashInfo, build_number: u64) -> Cow<'_, str> { + match self { + BuildString::UserSpecified(s) => s.as_str().into(), + BuildString::Derived => Self::compute(hash, build_number).into(), + } + } + + /// Compute the build string based on the hash and build number + pub fn compute(hash: &HashInfo, build_number: u64) -> String { + format!("{}_{}", hash, build_number) + } +} + +impl TryConvertNode for RenderedNode { + fn try_convert(&self, name: &str) -> Result> { + self.as_scalar() + .ok_or_else(|| vec![_partialerror!(*self.span(), ErrorKind::ExpectedScalar)]) + .and_then(|m| m.try_convert(name)) + } +} + +impl TryConvertNode for RenderedScalarNode { + fn try_convert(&self, _name: &str) -> Result> { + Ok(BuildString::UserSpecified(self.as_str().to_owned())) + } +} + /// Post process operations for regex based replacements #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PostProcess { @@ -138,8 +214,8 @@ impl Build { } /// Get the build string. - pub fn string(&self) -> Option<&str> { - self.string.as_deref() + pub fn string(&self) -> &BuildString { + &self.string } /// Get the skip conditions. 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 d41f2922..6ff2765b 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 @@ -85,7 +85,7 @@ Recipe { ], build: Build { number: 0, - string: None, + string: Derived, skip: Skip( [ ( 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 9642740e..b092b625 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 @@ -85,7 +85,7 @@ Recipe { ], build: Build { number: 0, - string: None, + string: Derived, skip: Skip( [ ( diff --git a/src/tui/render.rs b/src/tui/render.rs index e8051c3e..85e15911 100644 --- a/src/tui/render.rs +++ b/src/tui/render.rs @@ -1,24 +1,21 @@ -use super::event::Event; -use super::state::TuiState; use ansi_to_tui::IntoText; use crossterm::event::{ Event as CrosstermEvent, KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind, }; use miette::IntoDiagnostic; -use ratatui::layout::Position; -use ratatui::prelude::*; -use ratatui::style::Stylize; -use ratatui::widgets::{Scrollbar, ScrollbarOrientation, ScrollbarState}; use ratatui::{ - layout::Alignment, - style::{Color, Style}, - widgets::{Block, BorderType, Paragraph}, + layout::{Alignment, Position}, + prelude::*, + style::{Color, Style, Stylize}, + widgets::{Block, BorderType, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState}, Frame, }; use tokio::sync::mpsc; use tui_input::backend::crossterm::EventHandler; +use super::{event::Event, state::TuiState}; + /// Key bindings. const KEY_BINDINGS: &[(&str, &str)] = &[ ("⏎ ", "Build"), @@ -263,12 +260,12 @@ pub(crate) fn render_widgets(state: &mut TuiState, frame: &mut Frame) { package.name.clone().into(), "-".fg(Color::Rgb(100, 100, 100)), package.version.clone().into(), - package - .build_string - .clone() - .map(|v| format!("{}{v}", "-".fg(Color::Rgb(100, 100, 100)))) - .unwrap_or_default() - .into(), + format!( + "{}{}", + "-".fg(Color::Rgb(100, 100, 100)), + &package.build_string + ) + .into(), ]); if item[1].width < line.width() as u16 { line = Line::from(vec![ diff --git a/src/tui/state.rs b/src/tui/state.rs index afd89464..1b785b94 100644 --- a/src/tui/state.rs +++ b/src/tui/state.rs @@ -14,7 +14,7 @@ use crate::{ pub struct Package { pub name: String, pub version: String, - pub build_string: Option, + pub build_string: String, pub subpackages: Vec, pub build_progress: BuildProgress, pub build_log: Vec, @@ -33,7 +33,7 @@ impl Package { Package { name: name.clone(), version: output.version().to_string(), - build_string: output.build_string().map(String::from), + build_string: output.build_string().into_owned(), subpackages: output .build_configuration .subpackages diff --git a/src/variant_config.rs b/src/variant_config.rs index 04d287f8..7931f4ba 100644 --- a/src/variant_config.rs +++ b/src/variant_config.rs @@ -715,8 +715,8 @@ impl VariantConfig { let build_string = parsed_recipe .build() .string() - .unwrap_or(&hash.to_string()) - .to_string(); + .resolve(&hash, parsed_recipe.build().number) + .into_owned(); other_recipes.insert( parsed_recipe.package().name().as_normalized().to_string(), @@ -725,8 +725,8 @@ impl VariantConfig { parsed_recipe .build() .string() - .unwrap_or(&hash.to_string()) - .to_string(), + .resolve(&hash, parsed_recipe.build().number) + .into_owned(), used_filtered.clone(), ), );