use indexmap::IndexMap;
use minijinja::Value;
use rattler_build_jinja::Jinja;
use rattler_conda_types::Platform;
use std::{collections::HashMap, collections::HashSet};
pub use rattler_build_script::{
ExecutionArgs, InterpreterError, ResolvedScriptContents, SandboxArguments,
SandboxConfiguration, Script, ScriptContent, platform_script_extensions,
};
use crate::{
env_vars::{self},
metadata::Output,
};
impl Output {
fn env_vars_from_variant(&self) -> HashMap<String, Option<String>> {
let languages: HashSet<&str> =
HashSet::from(["PERL", "LUA", "R", "NUMPY", "PYTHON", "RUBY", "NODEJS"]);
self.variant()
.iter()
.filter_map(|(k, v)| {
let key_upper = k.normalize().to_uppercase();
if !languages.contains(key_upper.as_str()) {
Some((k.normalize(), Some(v.to_string())))
} else {
None
}
})
.collect()
}
pub(crate) fn jinja_renderer(&self) -> impl Fn(&str) -> Result<String, String> {
let selector_config = self.build_configuration.selector_config();
let jinja = Jinja::new(selector_config.clone()).with_context(&self.recipe.context);
move |template: &str| jinja.render_str(template).map_err(|e| e.to_string())
}
async fn prepare_build_script(&self) -> Result<ExecutionArgs, std::io::Error> {
let host_prefix = self.build_configuration.directories.host_prefix.clone();
let target_platform = self.build_configuration.target_platform;
let mut env_vars = env_vars::vars(self, "BUILD");
env_vars.extend(env_vars::os_vars(&host_prefix, &target_platform));
env_vars.extend(self.env_vars_from_variant());
let jinja_renderer = self.jinja_renderer();
let build_prefix = if self.recipe.build().merge_build_and_host_envs {
None
} else {
Some(&self.build_configuration.directories.build_prefix)
};
let work_dir = &self.build_configuration.directories.work_dir;
Ok(ExecutionArgs {
script: self.recipe.build().script.resolve_content(
&self.build_configuration.directories.recipe_dir,
Some(jinja_renderer),
platform_script_extensions(),
)?,
env_vars: env_vars
.into_iter()
.filter_map(|(k, v)| v.map(|v| (k, v)))
.collect(),
secrets: IndexMap::new(),
build_prefix: build_prefix.map(|p| p.to_owned()),
run_prefix: host_prefix,
execution_platform: Platform::current(),
work_dir: work_dir.clone(),
sandbox_config: self.build_configuration.sandbox_config().cloned(),
})
}
pub async fn run_build_script(&self) -> Result<(), InterpreterError> {
let span = tracing::info_span!("Running build script");
let _enter = span.enter();
let exec_args = self.prepare_build_script().await?;
let build_prefix = if self.recipe.build().merge_build_and_host_envs {
None
} else {
Some(&self.build_configuration.directories.build_prefix)
};
let mut jinja = Jinja::new(self.build_configuration.selector_config())
.with_context(&self.recipe.context);
for (k, v) in &exec_args.env_vars {
jinja
.context_mut()
.insert(k.clone(), Value::from_safe_string(v.clone()));
}
let jinja_renderer = |template: &str| -> Result<String, String> {
jinja.render_str(template).map_err(|e| e.to_string())
};
self.recipe
.build()
.script
.run_script(
exec_args
.env_vars
.into_iter()
.map(|(k, v)| (k, Some(v)))
.collect(),
&self.build_configuration.directories.work_dir,
&self.build_configuration.directories.recipe_dir,
&self.build_configuration.directories.host_prefix,
build_prefix,
Some(jinja_renderer),
self.build_configuration.sandbox_config(),
)
.await?;
Ok(())
}
pub async fn create_build_script(&self) -> Result<(), std::io::Error> {
let span = tracing::info_span!("Creating build script");
let _enter = span.enter();
let exec_args = self.prepare_build_script().await?;
rattler_build_script::create_build_script(exec_args).await
}
}