1use {
6 crate::py_packaging::distribution::DistributionCache,
7 anyhow::{Context, Result},
8 starlark::{
9 environment::{Environment, EnvironmentError, TypeValues},
10 values::{
11 error::{RuntimeError, ValueError},
12 none::NoneType,
13 {Mutable, TypedValue, Value, ValueResult},
14 },
15 },
16 starlark_dialect_build_targets::{get_context_value, EnvironmentContext},
17 std::{
18 collections::HashMap,
19 path::{Path, PathBuf},
20 sync::Arc,
21 },
22 tugger::starlark::TuggerContext,
23};
24
25#[derive(Debug)]
27pub struct PyOxidizerEnvironmentContext {
28 env: crate::environment::Environment,
30
31 pub verbose: bool,
33
34 pub cwd: PathBuf,
38
39 pub config_path: PathBuf,
41
42 pub build_host_triple: String,
44
45 pub build_target_triple: String,
47
48 pub build_release: bool,
50
51 pub build_opt_level: String,
53
54 pub distribution_cache: Arc<DistributionCache>,
59
60 extra_vars: HashMap<String, Option<String>>,
62}
63
64impl PyOxidizerEnvironmentContext {
65 #[allow(clippy::too_many_arguments)]
66 pub fn new(
67 env: &crate::environment::Environment,
68 verbose: bool,
69 config_path: &Path,
70 build_host_triple: &str,
71 build_target_triple: &str,
72 build_release: bool,
73 build_opt_level: &str,
74 distribution_cache: Option<Arc<DistributionCache>>,
75 extra_vars: HashMap<String, Option<String>>,
76 ) -> Result<PyOxidizerEnvironmentContext> {
77 let parent = config_path
78 .parent()
79 .with_context(|| "resolving parent directory of config".to_string())?;
80
81 let parent = if parent.is_relative() {
82 std::env::current_dir()?.join(parent)
83 } else {
84 parent.to_path_buf()
85 };
86
87 let distribution_cache = distribution_cache.unwrap_or_else(|| {
88 Arc::new(DistributionCache::new(Some(
89 &env.python_distributions_dir(),
90 )))
91 });
92
93 Ok(PyOxidizerEnvironmentContext {
94 env: env.clone(),
95 verbose,
96 cwd: parent,
97 config_path: config_path.to_path_buf(),
98 build_host_triple: build_host_triple.to_string(),
99 build_target_triple: build_target_triple.to_string(),
100 build_release,
101 build_opt_level: build_opt_level.to_string(),
102 distribution_cache,
103 extra_vars,
104 })
105 }
106
107 pub fn env(&self) -> &crate::environment::Environment {
108 &self.env
109 }
110
111 pub fn build_path(&self, type_values: &TypeValues) -> Result<PathBuf, ValueError> {
112 let build_targets_context_value = get_context_value(type_values)?;
113 let context = build_targets_context_value
114 .downcast_ref::<EnvironmentContext>()
115 .ok_or(ValueError::IncorrectParameterType)?;
116
117 Ok(context.build_path().to_path_buf())
118 }
119
120 pub fn python_distributions_path(&self) -> Result<PathBuf, ValueError> {
121 Ok(self.env.python_distributions_dir())
122 }
123
124 pub fn get_output_path(
125 &self,
126 type_values: &TypeValues,
127 target: &str,
128 ) -> Result<PathBuf, ValueError> {
129 let build_targets_context_value = get_context_value(type_values)?;
130 let context = build_targets_context_value
131 .downcast_ref::<EnvironmentContext>()
132 .ok_or(ValueError::IncorrectParameterType)?;
133
134 Ok(context.target_build_path(target))
135 }
136}
137
138impl TypedValue for PyOxidizerEnvironmentContext {
139 type Holder = Mutable<PyOxidizerEnvironmentContext>;
140 const TYPE: &'static str = "EnvironmentContext";
141
142 fn values_for_descendant_check_and_freeze(&self) -> Box<dyn Iterator<Item = Value>> {
143 Box::new(std::iter::empty())
144 }
145}
146
147#[derive(Default)]
149pub struct PyOxidizerContext {}
150
151impl TypedValue for PyOxidizerContext {
152 type Holder = Mutable<PyOxidizerContext>;
153 const TYPE: &'static str = "PyOxidizer";
154
155 fn values_for_descendant_check_and_freeze(&self) -> Box<dyn Iterator<Item = Value>> {
156 Box::new(std::iter::empty())
157 }
158}
159
160pub fn get_context(type_values: &TypeValues) -> ValueResult {
162 type_values
163 .get_type_value(&Value::new(PyOxidizerContext::default()), "CONTEXT")
164 .ok_or_else(|| {
165 ValueError::from(RuntimeError {
166 code: "PYOXIDIZER",
167 message: "Unable to resolve context (this should never happen)".to_string(),
168 label: "".to_string(),
169 })
170 })
171}
172
173pub fn register_starlark_dialect(
175 env: &mut Environment,
176 type_values: &mut TypeValues,
177) -> Result<(), EnvironmentError> {
178 starlark_dialect_build_targets::register_starlark_dialect(env, type_values)?;
179 tugger::starlark::register_starlark_dialect(env, type_values)?;
180 super::file_resource::file_resource_env(env, type_values);
181 super::python_distribution::python_distribution_module(env, type_values);
182 super::python_embedded_resources::python_embedded_resources_module(env, type_values);
183 super::python_executable::python_executable_env(env, type_values);
184 super::python_packaging_policy::python_packaging_policy_module(env, type_values);
185
186 Ok(())
187}
188
189pub fn populate_environment(
190 env: &mut Environment,
191 type_values: &mut TypeValues,
192 context: PyOxidizerEnvironmentContext,
193 resolve_targets: Option<Vec<String>>,
194 build_script_mode: bool,
195) -> Result<(), EnvironmentError> {
196 let mut build_targets_context = EnvironmentContext::new(context.cwd.clone());
197
198 if let Some(targets) = resolve_targets {
199 build_targets_context.set_resolve_targets(targets);
200 }
201
202 build_targets_context.build_script_mode = build_script_mode;
203
204 build_targets_context.set_target_build_path_prefix(Some(
205 PathBuf::from(&context.build_target_triple).join(if context.build_release {
206 "release"
207 } else {
208 "debug"
209 }),
210 ));
211
212 let tugger_context = TuggerContext::new();
213
214 starlark_dialect_build_targets::populate_environment(env, type_values, build_targets_context)?;
215 tugger::starlark::populate_environment(env, type_values, tugger_context)?;
216
217 let mut vars = starlark::values::dict::Dictionary::default();
218
219 for (k, v) in context.extra_vars.iter() {
220 vars.insert(
221 Value::from(k.as_str()),
222 match v {
223 Some(v) => Value::from(v.as_str()),
224 None => Value::from(NoneType::None),
225 },
226 )
227 .expect("error inserting variable; this should not happen");
228 }
229
230 env.set("VARS", Value::try_from(vars.get_content().clone()).unwrap())?;
231 env.set("CWD", Value::from(context.cwd.display().to_string()))?;
232 env.set(
233 "CONFIG_PATH",
234 Value::from(context.config_path.display().to_string()),
235 )?;
236 env.set(
237 "BUILD_TARGET_TRIPLE",
238 Value::from(context.build_target_triple.clone()),
239 )?;
240
241 env.set("CONTEXT", Value::new(context))?;
242
243 for f in &["CONTEXT", "CWD", "CONFIG_PATH", "BUILD_TARGET_TRIPLE"] {
248 type_values.add_type_value(PyOxidizerContext::TYPE, f, env.get(f)?);
249 }
250
251 Ok(())
252}
253
254#[cfg(test)]
255pub mod tests {
256 use crate::{environment::default_target_triple, starlark::testutil::*};
257
258 #[test]
259 fn test_cwd() {
260 let cwd = starlark_ok("CWD");
261 let pwd = std::env::current_dir().unwrap();
262 assert_eq!(cwd.to_str(), pwd.display().to_string());
263 }
264
265 #[test]
266 fn test_build_target() {
267 let target = starlark_ok("BUILD_TARGET_TRIPLE");
268 assert_eq!(target.to_str(), default_target_triple());
269 }
270
271 #[test]
272 fn test_print() {
273 starlark_ok("print('hello, world')");
274 }
275}