1pub mod testutil;
6
7use {
8 anyhow::{anyhow, Result},
9 linked_hash_map::LinkedHashMap,
10 log::warn,
11 path_dedot::ParseDot,
12 starlark::{
13 environment::{Environment, EnvironmentError, TypeValues},
14 eval::call_stack::CallStack,
15 values::{
16 error::{RuntimeError, ValueError, INCORRECT_PARAMETER_TYPE_ERROR_CODE},
17 none::NoneType,
18 {Mutable, TypedValue, Value, ValueResult},
19 },
20 {
21 starlark_fun, starlark_module, starlark_parse_param_type, starlark_signature,
22 starlark_signature_extraction, starlark_signatures,
23 },
24 },
25 std::{
26 borrow::Cow,
27 collections::{BTreeMap, HashMap},
28 os::raw::c_ulong,
29 path::{Path, PathBuf},
30 },
31};
32
33#[derive(Debug, Clone, PartialEq, Eq)]
35pub enum RunMode {
36 None,
38 Path { path: PathBuf },
40}
41
42#[derive(Debug, Clone)]
44pub struct ResolvedTarget {
45 pub run_mode: RunMode,
47
48 pub output_path: PathBuf,
50}
51
52impl ResolvedTarget {
53 pub fn run(&self) -> Result<()> {
54 match &self.run_mode {
55 RunMode::None => Ok(()),
56 RunMode::Path { path } => {
57 let status = std::process::Command::new(path)
58 .current_dir(path.parent().unwrap())
59 .status()?;
60
61 if status.success() {
62 Ok(())
63 } else {
64 Err(anyhow!("cargo run failed"))
65 }
66 }
67 }
68 }
69}
70
71pub struct ResolvedTargetValue {
72 pub inner: ResolvedTarget,
73}
74
75impl TypedValue for ResolvedTargetValue {
76 type Holder = Mutable<ResolvedTargetValue>;
77 const TYPE: &'static str = "ResolvedTarget";
78
79 fn values_for_descendant_check_and_freeze(&self) -> Box<dyn Iterator<Item = Value>> {
80 Box::new(std::iter::empty())
81 }
82}
83
84impl From<ResolvedTarget> for ResolvedTargetValue {
85 fn from(t: ResolvedTarget) -> Self {
86 Self { inner: t }
87 }
88}
89
90#[derive(Debug, Clone)]
92pub struct Target {
93 pub callable: Value,
95
96 pub depends: Vec<String>,
98
99 pub resolved_value: Option<Value>,
101
102 pub built_target: Option<ResolvedTarget>,
106}
107
108#[derive(Debug)]
110pub struct EnvironmentContext {
111 cwd: PathBuf,
113
114 build_path: PathBuf,
116
117 target_build_path_prefix: Option<PathBuf>,
119
120 targets: BTreeMap<String, Target>,
124
125 targets_order: Vec<String>,
127
128 default_target: Option<String>,
130
131 resolve_targets: Option<Vec<String>>,
133
134 pub default_build_script_target: Option<String>,
137
138 pub build_script_mode: bool,
142}
143
144impl EnvironmentContext {
145 pub fn new(cwd: PathBuf) -> Self {
146 let build_path = cwd.join("build");
147
148 Self {
149 cwd,
150 build_path,
151 target_build_path_prefix: None,
152 targets: BTreeMap::new(),
153 targets_order: vec![],
154 default_target: None,
155 resolve_targets: None,
156 default_build_script_target: None,
157 build_script_mode: false,
158 }
159 }
160
161 pub fn cwd(&self) -> &Path {
163 &self.cwd
164 }
165
166 pub fn build_path(&self) -> &Path {
168 &self.build_path
169 }
170
171 pub fn set_build_path(&mut self, path: &Path) -> Result<()> {
173 let path = if path.is_relative() {
174 self.cwd.join(path)
175 } else {
176 path.to_path_buf()
177 }
178 .parse_dot()?
179 .to_path_buf();
180
181 self.build_path = path;
182
183 Ok(())
184 }
185
186 pub fn resolve_path(&self, path: impl AsRef<Path>) -> PathBuf {
191 let path = path.as_ref();
192
193 if path.is_absolute() || path.to_string_lossy().starts_with('/') {
194 path.to_path_buf()
195 } else {
196 self.build_path.join(path)
197 }
198 }
199
200 pub fn set_target_build_path_prefix<P: AsRef<Path>>(&mut self, prefix: Option<P>) {
205 self.target_build_path_prefix = prefix.map(|p| p.as_ref().to_path_buf());
206 }
207
208 pub fn target_build_path(&self, target: &str) -> PathBuf {
210 if let Some(prefix) = &self.target_build_path_prefix {
211 self.build_path.join(prefix).join(target)
212 } else {
213 self.build_path.join(target)
214 }
215 }
216
217 pub fn targets(&self) -> &BTreeMap<String, Target> {
219 &self.targets
220 }
221
222 pub fn default_target(&self) -> Option<&str> {
224 self.default_target.as_deref()
225 }
226
227 pub fn get_target(&self, target: &str) -> Option<&Target> {
229 self.targets.get(target)
230 }
231
232 pub fn get_target_mut(&mut self, target: &str) -> Option<&mut Target> {
234 self.targets.get_mut(target)
235 }
236
237 pub fn set_resolve_targets(&mut self, targets: Vec<String>) {
239 self.resolve_targets = Some(targets);
240 }
241
242 pub fn targets_order(&self) -> &Vec<String> {
244 &self.targets_order
245 }
246
247 pub fn register_target(
249 &mut self,
250 target: String,
251 callable: Value,
252 depends: Vec<String>,
253 default: bool,
254 default_build_script: bool,
255 ) {
256 if !self.targets.contains_key(&target) {
257 self.targets_order.push(target.clone());
258 }
259
260 self.targets.insert(
261 target.clone(),
262 Target {
263 callable,
264 depends,
265 resolved_value: None,
266 built_target: None,
267 },
268 );
269
270 if default || self.default_target.is_none() {
271 self.default_target = Some(target.clone());
272 }
273
274 if default_build_script || self.default_build_script_target.is_none() {
275 self.default_build_script_target = Some(target);
276 }
277 }
278
279 pub fn targets_to_resolve(&self) -> Vec<String> {
284 if let Some(targets) = &self.resolve_targets {
285 targets.clone()
286 } else if self.build_script_mode && self.default_build_script_target.is_some() {
287 vec![self.default_build_script_target.clone().unwrap()]
288 } else if let Some(target) = &self.default_target {
289 vec![target.to_string()]
290 } else {
291 Vec::new()
292 }
293 }
294}
295
296impl TypedValue for EnvironmentContext {
297 type Holder = Mutable<EnvironmentContext>;
298 const TYPE: &'static str = "EnvironmentContext";
299
300 fn values_for_descendant_check_and_freeze(&self) -> Box<dyn Iterator<Item = Value>> {
301 Box::new(std::iter::empty())
302 }
303}
304
305#[derive(Default)]
306struct PlaceholderContext {}
307
308impl TypedValue for PlaceholderContext {
309 type Holder = Mutable<PlaceholderContext>;
310 const TYPE: &'static str = "BuildTargets";
311
312 fn values_for_descendant_check_and_freeze(&self) -> Box<dyn Iterator<Item = Value>> {
313 Box::new(std::iter::empty())
314 }
315}
316
317pub fn required_type_arg(arg_name: &str, arg_type: &str, value: &Value) -> Result<(), ValueError> {
318 let t = value.get_type();
319 if t == arg_type {
320 Ok(())
321 } else {
322 Err(ValueError::from(RuntimeError {
323 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
324 message: format!(
325 "function expects a {} for {}; got type {}",
326 arg_type, arg_name, t
327 ),
328 label: format!("expect type {}; got {}", arg_type, t),
329 }))
330 }
331}
332
333pub fn optional_type_arg(arg_name: &str, arg_type: &str, value: &Value) -> Result<(), ValueError> {
334 match value.get_type() {
335 "NoneType" => Ok(()),
336 _ => required_type_arg(arg_name, arg_type, value),
337 }
338}
339
340pub fn optional_str_arg(name: &str, value: &Value) -> Result<Option<String>, ValueError> {
341 match value.get_type() {
342 "NoneType" => Ok(None),
343 "string" => Ok(Some(value.to_str())),
344 t => Err(ValueError::from(RuntimeError {
345 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
346 message: format!(
347 "function expects an optional string for {}; got type {}",
348 name, t
349 ),
350 label: format!("expected type string; got {}", t),
351 })),
352 }
353}
354
355pub fn optional_bool_arg(name: &str, value: &Value) -> Result<Option<bool>, ValueError> {
356 match value.get_type() {
357 "NoneType" => Ok(None),
358 "bool" => Ok(Some(value.to_bool())),
359 t => Err(ValueError::from(RuntimeError {
360 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
361 message: format!(
362 "function expects an optional bool for {}; got type {}",
363 name, t
364 ),
365 label: format!("expected type bool; got {}", t),
366 })),
367 }
368}
369
370pub fn optional_int_arg(name: &str, value: &Value) -> Result<Option<i64>, ValueError> {
371 match value.get_type() {
372 "NoneType" => Ok(None),
373 "int" => Ok(Some(value.to_int()?)),
374 t => Err(ValueError::from(RuntimeError {
375 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
376 message: format!(
377 "function expected an optional int for {}; got type {}",
378 name, t
379 ),
380 label: format!("expected type int; got {}", t),
381 })),
382 }
383}
384
385pub fn required_list_arg(
386 arg_name: &str,
387 value_type: &str,
388 value: &Value,
389) -> Result<(), ValueError> {
390 match value.get_type() {
391 "list" => {
392 for v in &value.iter()? {
393 if v.get_type() == value_type {
394 Ok(())
395 } else {
396 Err(ValueError::from(RuntimeError {
397 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
398 message: format!(
399 "list {} expects values of type {}; got {}",
400 arg_name,
401 value_type,
402 v.get_type()
403 ),
404 label: format!("expected type {}; got {}", value_type, v.get_type()),
405 }))
406 }?;
407 }
408 Ok(())
409 }
410 t => Err(ValueError::from(RuntimeError {
411 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
412 message: format!("function expects a list for {}; got type {}", arg_name, t),
413 label: format!("expected type list; got {}", t),
414 })),
415 }
416}
417
418pub fn optional_list_arg(
419 arg_name: &str,
420 value_type: &str,
421 value: &Value,
422) -> Result<(), ValueError> {
423 if value.get_type() == "NoneType" {
424 return Ok(());
425 }
426
427 required_list_arg(arg_name, value_type, value)
428}
429
430pub fn required_dict_arg(
431 arg_name: &str,
432 key_type: &str,
433 value_type: &str,
434 value: &Value,
435) -> Result<(), ValueError> {
436 match value.get_type() {
437 "dict" => {
438 for k in &value.iter()? {
439 if k.get_type() == key_type {
440 Ok(())
441 } else {
442 Err(ValueError::from(RuntimeError {
443 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
444 message: format!(
445 "dict {} expects keys of type {}; got {}",
446 arg_name,
447 key_type,
448 k.get_type()
449 ),
450 label: format!("expected type {}; got {}", key_type, k.get_type()),
451 }))
452 }?;
453
454 let v = value.at(k.clone())?;
455
456 if v.get_type() == value_type {
457 Ok(())
458 } else {
459 Err(ValueError::from(RuntimeError {
460 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
461 message: format!(
462 "dict {} expects values of type {}; got {}",
463 arg_name,
464 value_type,
465 v.get_type(),
466 ),
467 label: format!("expected type {}; got {}", value_type, v.get_type()),
468 }))
469 }?;
470 }
471 Ok(())
472 }
473 t => Err(ValueError::from(RuntimeError {
474 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
475 message: format!("function expects a dict for {}; got type {}", arg_name, t),
476 label: format!("expected type dict; got {}", t),
477 })),
478 }
479}
480
481pub fn optional_dict_arg(
482 arg_name: &str,
483 key_type: &str,
484 value_type: &str,
485 value: &Value,
486) -> Result<(), ValueError> {
487 if value.get_type() == "NoneType" {
488 return Ok(());
489 }
490
491 required_dict_arg(arg_name, key_type, value_type, value)
492}
493
494pub trait ToOptional<T> {
495 fn to_optional(&self) -> Option<T>;
496}
497
498impl ToOptional<bool> for Value {
499 fn to_optional(&self) -> Option<bool> {
500 if self.get_type() == "NoneType" {
501 None
502 } else {
503 Some(self.to_bool())
504 }
505 }
506}
507
508impl ToOptional<String> for Value {
509 fn to_optional(&self) -> Option<String> {
510 if self.get_type() == "NoneType" {
511 None
512 } else {
513 Some(self.to_string())
514 }
515 }
516}
517
518impl ToOptional<Cow<'static, str>> for Value {
519 fn to_optional(&self) -> Option<Cow<'static, str>> {
520 if self.get_type() == "NoneType" {
521 None
522 } else {
523 Some(Cow::Owned(self.to_string()))
524 }
525 }
526}
527
528impl ToOptional<PathBuf> for Value {
529 fn to_optional(&self) -> Option<PathBuf> {
530 if self.get_type() == "NoneType" {
531 None
532 } else {
533 Some(PathBuf::from(self.to_string()))
534 }
535 }
536}
537
538pub trait TryToOptional<T> {
539 fn try_to_optional(&self) -> Result<Option<T>, ValueError>;
540}
541
542impl TryToOptional<c_ulong> for Value {
543 fn try_to_optional(&self) -> Result<Option<c_ulong>, ValueError> {
544 if self.get_type() == "NoneType" {
545 Ok(None)
546 } else {
547 Ok(Some(self.to_int()? as c_ulong))
548 }
549 }
550}
551
552impl TryToOptional<i64> for Value {
553 fn try_to_optional(&self) -> Result<Option<i64>, ValueError> {
554 match self.get_type() {
555 "NoneType" => Ok(None),
556 "int" => Ok(Some(self.to_int()?)),
557 t => Err(ValueError::from(RuntimeError {
558 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
559 message: format!("expected int or NoneType; got {}", t),
560 label: "".to_string(),
561 })),
562 }
563 }
564}
565
566impl TryToOptional<Vec<String>> for Value {
567 fn try_to_optional(&self) -> Result<Option<Vec<String>>, ValueError> {
568 if self.get_type() == "NoneType" {
569 Ok(None)
570 } else {
571 let values = self.to_vec()?;
572
573 Ok(Some(
574 values.iter().map(|x| x.to_string()).collect::<Vec<_>>(),
575 ))
576 }
577 }
578}
579
580impl TryToOptional<Vec<Cow<'static, str>>> for Value {
581 fn try_to_optional(&self) -> Result<Option<Vec<Cow<'static, str>>>, ValueError> {
582 if self.get_type() == "NoneType" {
583 Ok(None)
584 } else {
585 let values = self.to_vec()?;
586
587 Ok(Some(
588 values
589 .iter()
590 .map(|x| Cow::Owned(x.to_string()))
591 .collect::<Vec<_>>(),
592 ))
593 }
594 }
595}
596
597impl TryToOptional<Vec<PathBuf>> for Value {
598 fn try_to_optional(&self) -> Result<Option<Vec<PathBuf>>, ValueError> {
599 if self.get_type() == "NoneType" {
600 Ok(None)
601 } else {
602 let values = self.to_vec()?;
603
604 Ok(Some(
605 values
606 .iter()
607 .map(|x| PathBuf::from(x.to_string()))
608 .collect::<Vec<_>>(),
609 ))
610 }
611 }
612}
613
614type StringHashMap = HashMap<Cow<'static, str>, Cow<'static, str>>;
615
616impl TryToOptional<StringHashMap> for Value {
617 fn try_to_optional(&self) -> Result<Option<StringHashMap>, ValueError> {
618 match self.get_type() {
619 "NoneType" => Ok(None),
620 "dict" => {
621 let mut res = HashMap::new();
622
623 for key in &self.iter()? {
624 let value = self.at(key.clone())?;
625
626 res.insert(Cow::Owned(key.to_string()), Cow::Owned(value.to_string()));
627 }
628
629 Ok(Some(res))
630 }
631 t => Err(ValueError::from(RuntimeError {
632 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
633 message: format!("expected dict or NoneType; got {}", t),
634 label: "".to_string(),
635 })),
636 }
637 }
638}
639
640impl TryToOptional<HashMap<Cow<'static, str>, StringHashMap>> for Value {
641 fn try_to_optional(
642 &self,
643 ) -> Result<Option<HashMap<Cow<'static, str>, StringHashMap>>, ValueError> {
644 match self.get_type() {
645 "NoneType" => Ok(None),
646 "dict" => {
647 let mut res = HashMap::new();
648
649 for key in &self.iter()? {
650 let value = self.at(key.clone())?;
651
652 let value: Option<HashMap<Cow<'static, str>, Cow<'static, str>>> =
653 value.try_to_optional()?;
654
655 match value {
656 Some(v) => {
657 res.insert(Cow::Owned(key.to_string()), v);
658 }
659 None => {
660 return Err(ValueError::from(RuntimeError {
661 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
662 message: "expected dict[string, string], got None".to_string(),
663 label: "".to_string(),
664 }));
665 }
666 }
667 }
668
669 Ok(Some(res))
670 }
671 t => Err(ValueError::from(RuntimeError {
672 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
673 message: format!("expected dict or NoneType; got {}", t),
674 label: "".to_string(),
675 })),
676 }
677 }
678}
679
680impl TryToOptional<Vec<StringHashMap>> for Value {
681 fn try_to_optional(&self) -> Result<Option<Vec<StringHashMap>>, ValueError> {
682 match self.get_type() {
683 "NoneType" => Ok(None),
684 "list" => {
685 let mut res = Vec::new();
686
687 for item in &self.iter()? {
688 match item.get_type() {
689 "dict" => {
690 let value: Option<StringHashMap> = item.try_to_optional()?;
691
692 match value {
693 Some(value) => {
694 res.push(value);
695 }
696 None => {
697 return Err(ValueError::from(RuntimeError {
698 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
699 message: "expected dict[string, string], got None"
700 .to_string(),
701 label: "".to_string(),
702 }));
703 }
704 }
705 }
706 t => {
707 return Err(ValueError::from(RuntimeError {
708 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
709 message: format!("expected dict[string, string], got {}", t),
710 label: "".to_string(),
711 }));
712 }
713 }
714 }
715
716 Ok(Some(res))
717 }
718 t => Err(ValueError::from(RuntimeError {
719 code: INCORRECT_PARAMETER_TYPE_ERROR_CODE,
720 message: format!("expected list or NoneType; got {}", t),
721 label: "".to_string(),
722 })),
723 }
724 }
725}
726
727const ENVIRONMENT_CONTEXT_SYMBOL: &str = "BUILD_CONTEXT";
728
729pub fn get_context_value(type_values: &TypeValues) -> ValueResult {
734 type_values
735 .get_type_value(
736 &Value::new(PlaceholderContext::default()),
737 ENVIRONMENT_CONTEXT_SYMBOL,
738 )
739 .ok_or_else(|| {
740 ValueError::from(RuntimeError {
741 code: "STARLARK_BUILD_CONTEXT",
742 message: "Unable to resolve context (this should never happen)".to_string(),
743 label: "".to_string(),
744 })
745 })
746}
747
748fn starlark_print(args: &[Value]) -> ValueResult {
750 let mut parts = Vec::new();
751 let mut first = true;
752 for arg in args {
753 if !first {
754 parts.push(" ".to_string());
755 }
756 first = false;
757 parts.push(arg.to_string());
758 }
759
760 warn!("{}", parts.join(""));
761
762 Ok(Value::new(NoneType::None))
763}
764
765fn starlark_register_target(
767 type_values: &TypeValues,
768 target: String,
769 callable: Value,
770 depends: Value,
771 default: bool,
772 default_build_script: bool,
773) -> ValueResult {
774 required_type_arg("callable", "function", &callable)?;
775 optional_list_arg("depends", "string", &depends)?;
776
777 let depends = match depends.get_type() {
778 "list" => depends.iter()?.iter().map(|x| x.to_string()).collect(),
779 _ => Vec::new(),
780 };
781
782 let raw_context = get_context_value(type_values)?;
783 let mut context = raw_context
784 .downcast_mut::<EnvironmentContext>()?
785 .ok_or(ValueError::IncorrectParameterType)?;
786
787 context.register_target(target, callable, depends, default, default_build_script);
788
789 Ok(Value::new(NoneType::None))
790}
791
792fn starlark_resolve_target(
802 type_values: &TypeValues,
803 call_stack: &mut CallStack,
804 target: String,
805) -> ValueResult {
806 let target_entry = {
810 let raw_context = get_context_value(type_values)?;
811 let context = raw_context
812 .downcast_ref::<EnvironmentContext>()
813 .ok_or(ValueError::IncorrectParameterType)?;
814
815 if let Some(v) = if let Some(t) = context.get_target(&target) {
817 t.resolved_value.as_ref().cloned()
818 } else {
819 None
820 } {
821 return Ok(v);
822 }
823
824 warn!("resolving target {}", target);
825
826 match context.get_target(&target) {
827 Some(v) => Ok((*v).clone()),
828 None => Err(ValueError::from(RuntimeError {
829 code: "BUILD_TARGETS",
830 message: format!("target {} does not exist", target),
831 label: "resolve_target()".to_string(),
832 })),
833 }?
834 };
835
836 let mut args = Vec::new();
838
839 for depend_target in target_entry.depends {
840 args.push(starlark_resolve_target(
841 type_values,
842 call_stack,
843 depend_target,
844 )?);
845 }
846
847 let res = target_entry.callable.call(
848 call_stack,
849 type_values,
850 args,
851 LinkedHashMap::new(),
852 None,
853 None,
854 )?;
855
856 let raw_context = get_context_value(type_values)?;
862 let mut context = raw_context
863 .downcast_mut::<EnvironmentContext>()?
864 .ok_or(ValueError::IncorrectParameterType)?;
865
866 if let Some(target_entry) = context.get_target_mut(&target) {
867 target_entry.resolved_value = Some(res.clone());
868 }
869
870 Ok(res)
871}
872
873fn starlark_resolve_targets(type_values: &TypeValues, call_stack: &mut CallStack) -> ValueResult {
875 let resolve_target_fn = type_values
876 .get_type_value(&Value::new(PlaceholderContext::default()), "resolve_target")
877 .ok_or_else(|| {
878 ValueError::from(RuntimeError {
879 code: "BUILD_TARGETS",
880 message: "could not find resolve_target() function (this should never happen)"
881 .to_string(),
882 label: "resolve_targets()".to_string(),
883 })
884 })?;
885
886 let targets = {
889 let raw_context = get_context_value(type_values)?;
890 let context = raw_context
891 .downcast_ref::<EnvironmentContext>()
892 .ok_or(ValueError::IncorrectParameterType)?;
893
894 let targets = context.targets_to_resolve();
895 warn!("resolving {} targets", targets.len());
896
897 targets
898 };
899
900 for target in targets {
901 resolve_target_fn.call(
902 call_stack,
903 type_values,
904 vec![Value::new(target)],
905 LinkedHashMap::new(),
906 None,
907 None,
908 )?;
909 }
910
911 Ok(Value::new(NoneType::None))
912}
913
914fn starlark_set_build_path(type_values: &TypeValues, path: String) -> ValueResult {
916 let context_value = get_context_value(type_values)?;
917 let mut context = context_value
918 .downcast_mut::<EnvironmentContext>()?
919 .ok_or(ValueError::IncorrectParameterType)?;
920
921 context.set_build_path(&PathBuf::from(&path)).map_err(|e| {
922 ValueError::from(RuntimeError {
923 code: "BUILD_TARGETS",
924 message: e.to_string(),
925 label: "set_build_path()".to_string(),
926 })
927 })?;
928
929 Ok(Value::new(NoneType::None))
930}
931
932starlark_module! { build_targets_module =>
933 print(*args) {
934 starlark_print(&args)
935 }
936
937 register_target(
938 env env,
939 target: String,
940 callable,
941 depends = NoneType::None,
942 default: bool = false,
943 default_build_script: bool = false
944 ) {
945 starlark_register_target(env, target, callable, depends, default, default_build_script)
946 }
947
948 resolve_target(env env, call_stack cs, target: String) {
949 starlark_resolve_target(env, cs, target)
950 }
951
952 resolve_targets(env env, call_stack cs) {
953 starlark_resolve_targets(env, cs)
954 }
955
956 set_build_path(env env, path: String) {
957 starlark_set_build_path(env, path)
958 }
959}
960
961pub fn register_starlark_dialect(
963 env: &mut Environment,
964 type_values: &mut TypeValues,
965) -> Result<(), EnvironmentError> {
966 build_targets_module(env, type_values);
967
968 Ok(())
969}
970
971pub fn populate_environment(
976 env: &mut Environment,
977 type_values: &mut TypeValues,
978 context: EnvironmentContext,
979) -> Result<(), EnvironmentError> {
980 env.set(ENVIRONMENT_CONTEXT_SYMBOL, Value::new(context))?;
981
982 for f in &[
987 "register_target",
988 "resolve_target",
989 "resolve_targets",
990 "set_build_path",
991 ENVIRONMENT_CONTEXT_SYMBOL,
992 ] {
993 type_values.add_type_value(PlaceholderContext::TYPE, f, env.get(f)?);
994 }
995
996 Ok(())
997}
998
999pub fn build_target(
1001 _env: &mut Environment,
1002 type_values: &TypeValues,
1003 call_stack: &mut CallStack,
1004 target: &str,
1005) -> Result<ResolvedTarget> {
1006 let resolved_value = {
1007 let context_value = get_context_value(type_values)
1008 .map_err(|_| anyhow!("unable to resolve context value"))?;
1009 let context = context_value
1010 .downcast_ref::<EnvironmentContext>()
1011 .ok_or_else(|| anyhow!("context has incorrect type"))?;
1012
1013 let v = if let Some(t) = context.get_target(target) {
1014 if let Some(t) = &t.built_target {
1015 return Ok(t.clone());
1016 }
1017
1018 if let Some(v) = &t.resolved_value {
1019 v.clone()
1020 } else {
1021 return Err(anyhow!("target {} is not resolved", target));
1022 }
1023 } else {
1024 return Err(anyhow!("target {} is not resolved", target));
1025 };
1026
1027 v
1028 };
1029
1030 let build = type_values
1031 .get_type_value(&resolved_value, "build")
1032 .ok_or_else(|| anyhow!("{} does not implement build()", resolved_value.get_type()))?;
1033
1034 let resolved_target_value = build
1035 .call(
1036 call_stack,
1037 type_values,
1038 vec![resolved_value, Value::from(target)],
1039 LinkedHashMap::new(),
1040 None,
1041 None,
1042 )
1043 .map_err(|e| anyhow!("error calling build(): {:?}", e))?;
1044
1045 let resolved_target = resolved_target_value
1046 .downcast_ref::<ResolvedTargetValue>()
1047 .unwrap();
1048
1049 let context_value = get_context_value(type_values)
1050 .map_err(|e| anyhow!("unable to resolve context: {:?}", e))?;
1051 let mut context = context_value
1052 .downcast_mut::<EnvironmentContext>()
1053 .map_err(|_| anyhow!("unable to obtain mutable context"))?
1054 .ok_or_else(|| anyhow!("context has incorrect type"))?;
1055
1056 context.get_target_mut(target).unwrap().built_target = Some(resolved_target.inner.clone());
1057
1058 Ok(resolved_target.inner.clone())
1059}
1060
1061pub fn run_target(
1065 env: &mut Environment,
1066 type_values: &TypeValues,
1067 call_stack: &mut CallStack,
1068 target: Option<&str>,
1069) -> Result<()> {
1070 let target = {
1071 let context_value = get_context_value(type_values)
1073 .map_err(|e| anyhow!("unable to resolve context value: {:?}", e))?;
1074 let context = context_value.downcast_ref::<EnvironmentContext>().unwrap();
1075
1076 if let Some(t) = target {
1077 t.to_string()
1078 } else if let Some(t) = context.default_target() {
1079 t.to_string()
1080 } else {
1081 return Err(anyhow!("unable to determine target to run"));
1082 }
1083 };
1084
1085 let resolved_target = build_target(env, type_values, call_stack, &target)?;
1086
1087 resolved_target.run()
1088}
1089
1090#[cfg(test)]
1091mod test {
1092 use super::*;
1093 use crate::testutil::*;
1094
1095 #[test]
1096 fn test_register_target() -> Result<()> {
1097 let mut env = StarlarkEnvironment::new()?;
1098 env.eval("def foo(): pass")?;
1099 env.eval("register_target('default', foo)")?;
1100
1101 let context_value = get_context_value(&env.type_values).unwrap();
1102 let context = context_value
1103 .downcast_ref::<EnvironmentContext>()
1104 .ok_or(ValueError::IncorrectParameterType)
1105 .unwrap();
1106
1107 assert_eq!(context.targets().len(), 1);
1108 assert!(context.get_target("default").is_some());
1109 assert_eq!(
1110 context.get_target("default").unwrap().callable.to_string(),
1111 "foo()".to_string()
1112 );
1113 assert_eq!(context.targets_order(), &vec!["default".to_string()]);
1114 assert_eq!(context.default_target(), Some("default"));
1115
1116 Ok(())
1117 }
1118
1119 #[test]
1120 fn test_register_target_multiple() -> Result<()> {
1121 let mut env = StarlarkEnvironment::new()?;
1122 env.eval("def foo(): pass")?;
1123 env.eval("def bar(): pass")?;
1124 env.eval("register_target('foo', foo)")?;
1125 env.eval("register_target('bar', bar, depends=['foo'], default=True)")?;
1126
1127 let context_value = get_context_value(&env.type_values).unwrap();
1128 let context = context_value
1129 .downcast_ref::<EnvironmentContext>()
1130 .ok_or(ValueError::IncorrectParameterType)
1131 .unwrap();
1132
1133 assert_eq!(context.targets().len(), 2);
1134 assert_eq!(context.default_target(), Some("bar"));
1135 assert_eq!(
1136 &context.get_target("bar").unwrap().depends,
1137 &vec!["foo".to_string()],
1138 );
1139
1140 Ok(())
1141 }
1142}