1mod context;
2mod error;
3mod global_state;
4mod module_state;
5
6pub use self::error::CompileError;
7use self::{context::Context, global_state::GlobalState, module_state::ModuleState};
8use crate::{
9 ast,
10 ir::{Build, Config, DynamicBuild, DynamicConfig, HeaderDependency, Rule},
11 module_dependency::ModuleDependencyMap,
12};
13use alloc::sync::Arc;
14use once_cell::sync::Lazy;
15use regex::{Captures, Regex};
16use std::{
17 collections::HashMap,
18 path::{Path, PathBuf},
19};
20use train_map::TrainMap;
21
22const PHONY_RULE: &str = "phony";
23const BUILD_DIRECTORY_VARIABLE: &str = "builddir";
24const COMMAND_VARIABLE: &str = "command";
25const DESCRIPTION_VARIABLE: &str = "description";
26const DEPFILE_VARIABLE: &str = "depfile";
27const DEPS_VARIABLE: &str = "deps";
28const DYNAMIC_MODULE_VARIABLE: &str = "dyndep";
29const SOURCE_VARIABLE_NAME: &str = "srcdep";
30const MSVC_DEPS_PREFIX_VARIABLE: &str = "msvc_deps_prefix";
31const DEFAULT_MSVC_DEPS_PREFIX: &str = "Note: including file: ";
33
34static VARIABLE_PATTERN: Lazy<Regex> =
35 Lazy::new(|| Regex::new(r"\$(\$|[[:alpha:]_][[:alnum:]_]*)").unwrap());
36
37pub fn compile(
40 modules: &HashMap<PathBuf, ast::Module>,
41 dependencies: &ModuleDependencyMap,
42 root_module_path: &Path,
43) -> Result<Config, CompileError> {
44 let context = Context::new(modules, dependencies);
45
46 let mut global_state = GlobalState {
47 outputs: Default::default(),
48 default_outputs: Default::default(),
49 source_map: Default::default(),
50 };
51 let mut module_state = ModuleState {
52 rules: TrainMap::new(),
53 variables: TrainMap::new(),
54 };
55
56 compile_module(
57 &context,
58 &mut global_state,
59 &mut module_state,
60 root_module_path,
61 )?;
62
63 let default_outputs = if global_state.default_outputs.is_empty() {
64 global_state.outputs.keys().cloned().collect()
65 } else {
66 global_state.default_outputs
67 };
68
69 Ok(Config::new(
70 global_state.outputs,
71 default_outputs,
72 global_state.source_map,
73 module_state
74 .variables
75 .get(BUILD_DIRECTORY_VARIABLE)
76 .cloned(),
77 ))
78}
79
80fn compile_module<'a>(
81 context: &'a Context,
82 global_state: &mut GlobalState,
83 module_state: &mut ModuleState<'a, '_>,
84 path: &Path,
85) -> Result<(), CompileError> {
86 let module = &context
87 .modules()
88 .get(path)
89 .ok_or_else(|| CompileError::ModuleNotFound(path.into()))?;
90
91 for statement in module.statements() {
92 match statement {
93 ast::Statement::Build(build) => {
94 let mut variables = module_state.variables.fork();
95
96 if build.rule() != PHONY_RULE {
97 variables.extend(
98 module_state
99 .rules
100 .get(build.rule())
101 .ok_or_else(|| CompileError::RuleNotFound(build.rule().into()))?
102 .iter()
103 .cloned(),
104 );
105 }
106
107 variables.extend(
108 build
109 .variable_definitions()
110 .iter()
111 .map(|definition| (definition.name(), definition.value().into()))
112 .chain([
113 ("in", build.inputs().join(" ").into()),
114 ("out", build.outputs().join(" ").into()),
115 ]),
116 );
117
118 let ir = Arc::new(Build::new(
119 build
120 .outputs()
121 .iter()
122 .map(|string| string.as_str().into())
123 .collect(),
124 build
125 .implicit_outputs()
126 .iter()
127 .map(|string| string.as_str().into())
128 .collect(),
129 if build.rule() == PHONY_RULE {
130 None
131 } else {
132 Some(
133 Rule::new(
134 resolve_variable(COMMAND_VARIABLE, &variables).unwrap_or_default(),
135 resolve_variable(DESCRIPTION_VARIABLE, &variables),
136 )
137 .with_header_dependency(
138 compile_header_dependency(build.rule(), &variables)?,
139 ),
140 )
141 },
142 build
143 .inputs()
144 .iter()
145 .chain(build.implicit_inputs())
146 .map(|string| string.as_str().into())
147 .collect(),
148 build
149 .order_only_inputs()
150 .iter()
151 .map(|string| string.as_str().into())
152 .collect(),
153 resolve_variable(DYNAMIC_MODULE_VARIABLE, &variables).map(Into::into),
154 ));
155
156 let outputs = || build.outputs().iter().chain(build.implicit_outputs());
157
158 global_state
159 .outputs
160 .extend(outputs().map(|output| (output.as_str().into(), ir.clone())));
161
162 if let Some(source) = variables.get(SOURCE_VARIABLE_NAME) {
163 global_state
164 .source_map
165 .extend(outputs().map(|output| (output.as_str().into(), source.clone())));
166 }
167 }
168 ast::Statement::Default(default) => {
169 global_state.default_outputs.extend(
170 default
171 .outputs()
172 .iter()
173 .map(|string| string.as_str().into()),
174 );
175 }
176 ast::Statement::Include(include) => {
177 compile_module(
178 context,
179 global_state,
180 module_state,
181 resolve_dependency(context, path, include.path())?,
182 )?;
183 }
184 ast::Statement::Rule(rule) => {
185 module_state.rules.insert(
186 rule.name(),
187 rule.variable_definitions()
188 .iter()
189 .map(|definition| (definition.name(), definition.value().into()))
190 .collect(),
191 );
192 }
193 ast::Statement::Submodule(submodule) => {
194 compile_module(
195 context,
196 global_state,
197 &mut module_state.fork(),
198 resolve_dependency(context, path, submodule.path())?,
199 )?;
200 }
201 ast::Statement::VariableDefinition(definition) => {
202 module_state.variables.insert(
203 definition.name(),
204 interpolate_variables(definition.value(), &module_state.variables).into(),
205 );
206 }
207 }
208 }
209
210 Ok(())
211}
212
213fn compile_header_dependency(
214 rule: &str,
215 variables: &TrainMap<&str, Arc<str>>,
216) -> Result<Option<HeaderDependency>, CompileError> {
217 Ok(
218 match (
219 resolve_variable(DEPS_VARIABLE, variables).as_deref(),
220 resolve_variable(DEPFILE_VARIABLE, variables),
221 ) {
222 (None, None) => None,
223 (None, Some(path)) => Some(HeaderDependency::Make { path }),
224 (Some("gcc"), Some(path)) => Some(HeaderDependency::Gcc { path }),
225 (Some("gcc"), None) => return Err(CompileError::MissingDepfile(rule.into())),
226 (Some("msvc"), _) => Some(HeaderDependency::Msvc {
227 prefix: resolve_variable(MSVC_DEPS_PREFIX_VARIABLE, variables)
228 .unwrap_or_else(|| DEFAULT_MSVC_DEPS_PREFIX.into()),
229 }),
230 (Some(deps), _) => return Err(CompileError::InvalidDependencyStyle(deps.into())),
231 },
232 )
233}
234
235pub fn compile_dynamic(module: &ast::DynamicModule) -> Result<DynamicConfig, CompileError> {
236 Ok(DynamicConfig::new(
237 module
238 .builds()
239 .iter()
240 .map(|build| {
241 (
242 build.output().into(),
243 DynamicBuild::new(
244 build
245 .implicit_inputs()
246 .iter()
247 .map(|string| string.as_str().into())
248 .collect(),
249 ),
250 )
251 })
252 .collect(),
253 ))
254}
255
256fn resolve_dependency<'a>(
257 context: &'a Context,
258 module_path: &Path,
259 submodule_path: &str,
260) -> Result<&'a Path, CompileError> {
261 Ok(context
262 .dependencies()
263 .get(module_path)
264 .ok_or_else(|| CompileError::ModuleNotFound(module_path.into()))?
265 .get(submodule_path)
266 .ok_or_else(|| CompileError::ModuleNotFound(submodule_path.into()))?)
267}
268
269fn resolve_variable(name: &str, variables: &TrainMap<&str, Arc<str>>) -> Option<String> {
270 variables
271 .get(name)
272 .map(|value| interpolate_variables(value, variables))
273 .filter(|value| !value.is_empty())
274}
275
276fn interpolate_variables(template: &str, variables: &TrainMap<&str, Arc<str>>) -> String {
277 VARIABLE_PATTERN
278 .replace_all(template, |captures: &Captures| match &captures[1] {
279 "$" => "$",
280 name => variables
281 .get(name)
282 .map(|string| string.as_ref())
283 .unwrap_or_default(),
284 })
285 .into()
286}
287
288#[cfg(test)]
289mod tests {
290 use super::*;
291 use crate::ast;
292 use once_cell::sync::Lazy;
293 use pretty_assertions::assert_eq;
294 use std::collections::HashSet;
295
296 static ROOT_MODULE_PATH: Lazy<PathBuf> = Lazy::new(|| PathBuf::from("build.ninja"));
297 static DEFAULT_DEPENDENCIES: Lazy<ModuleDependencyMap> = Lazy::new(|| {
298 [(ROOT_MODULE_PATH.clone(), Default::default())]
299 .into_iter()
300 .collect()
301 });
302
303 fn ast_explicit_build(
304 outputs: Vec<String>,
305 rule: impl Into<String>,
306 inputs: Vec<String>,
307 variable_definitions: Vec<ast::VariableDefinition>,
308 ) -> ast::Build {
309 ast::Build::new(
310 outputs,
311 vec![],
312 rule,
313 inputs,
314 vec![],
315 vec![],
316 variable_definitions,
317 )
318 }
319
320 fn ast_rule(name: &str, variable_definitions: &[(&str, &str)]) -> ast::Rule {
321 ast::Rule::new(
322 name,
323 variable_definitions
324 .iter()
325 .map(|(name, value)| ast::VariableDefinition::new(*name, *value))
326 .collect(),
327 )
328 }
329
330 fn ir_explicit_build(outputs: Vec<Arc<str>>, rule: Rule, inputs: Vec<Arc<str>>) -> Build {
331 Build::new(outputs, vec![], rule.into(), inputs, vec![], None)
332 }
333
334 fn create_simple_config(
335 outputs: HashMap<Arc<str>, Arc<Build>>,
336 default_outputs: HashSet<Arc<str>>,
337 ) -> Config {
338 Config::new(outputs, default_outputs, Default::default(), None)
339 }
340
341 #[test]
342 fn compile_empty_module() {
343 assert_eq!(
344 compile(
345 &[(ROOT_MODULE_PATH.clone(), ast::Module::new(vec![]))]
346 .into_iter()
347 .collect(),
348 &DEFAULT_DEPENDENCIES,
349 &ROOT_MODULE_PATH
350 )
351 .unwrap(),
352 create_simple_config(Default::default(), Default::default())
353 );
354 }
355
356 #[test]
357 fn interpolate_variable_in_command() {
358 assert_eq!(
359 compile(
360 &[(
361 ROOT_MODULE_PATH.clone(),
362 ast::Module::new(vec![
363 ast::VariableDefinition::new("x", "42").into(),
364 ast_rule("foo", &[("command", "$x")]).into(),
365 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
366 ])
367 )]
368 .into_iter()
369 .collect(),
370 &DEFAULT_DEPENDENCIES,
371 &ROOT_MODULE_PATH
372 )
373 .unwrap(),
374 create_simple_config(
375 [(
376 "bar".into(),
377 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
378 )]
379 .into_iter()
380 .collect(),
381 ["bar".into()].into_iter().collect()
382 )
383 );
384 }
385
386 #[test]
387 fn interpolate_two_variables_in_command() {
388 assert_eq!(
389 compile(
390 &[(
391 ROOT_MODULE_PATH.clone(),
392 ast::Module::new(vec![
393 ast::VariableDefinition::new("x", "1").into(),
394 ast::VariableDefinition::new("y", "2").into(),
395 ast_rule("foo", &[("command", "$x $y")]).into(),
396 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
397 ])
398 )]
399 .into_iter()
400 .collect(),
401 &DEFAULT_DEPENDENCIES,
402 &ROOT_MODULE_PATH
403 )
404 .unwrap(),
405 create_simple_config(
406 [(
407 "bar".into(),
408 ir_explicit_build(vec!["bar".into()], Rule::new("1 2", None), vec![]).into()
409 )]
410 .into_iter()
411 .collect(),
412 ["bar".into()].into_iter().collect()
413 )
414 );
415 }
416
417 #[test]
418 fn interpolate_variable_with_underscore_in_command() {
419 assert_eq!(
420 compile(
421 &[(
422 ROOT_MODULE_PATH.clone(),
423 ast::Module::new(vec![
424 ast::VariableDefinition::new("x_y", "42").into(),
425 ast_rule("foo", &[("command", "$x_y")]).into(),
426 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
427 ])
428 )]
429 .into_iter()
430 .collect(),
431 &DEFAULT_DEPENDENCIES,
432 &ROOT_MODULE_PATH
433 )
434 .unwrap(),
435 create_simple_config(
436 [(
437 "bar".into(),
438 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
439 )]
440 .into_iter()
441 .collect(),
442 ["bar".into()].into_iter().collect()
443 )
444 );
445 }
446
447 #[test]
448 fn interpolate_dollar_sign_in_command() {
449 assert_eq!(
450 compile(
451 &[(
452 ROOT_MODULE_PATH.clone(),
453 ast::Module::new(vec![
454 ast_rule("foo", &[("command", "$$")]).into(),
455 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into()
456 ])
457 )]
458 .into_iter()
459 .collect(),
460 &DEFAULT_DEPENDENCIES,
461 &ROOT_MODULE_PATH
462 )
463 .unwrap(),
464 create_simple_config(
465 [(
466 "bar".into(),
467 ir_explicit_build(vec!["bar".into()], Rule::new("$", None), vec![]).into()
468 )]
469 .into_iter()
470 .collect(),
471 ["bar".into()].into_iter().collect()
472 )
473 );
474 }
475
476 #[test]
477 fn interpolate_escaped_variable_in_command() {
478 assert_eq!(
479 compile(
480 &[(
481 ROOT_MODULE_PATH.clone(),
482 ast::Module::new(vec![
483 ast::VariableDefinition::new("x", "42").into(),
484 ast_rule("foo", &[("command", "$$x")]).into(),
485 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
486 ])
487 )]
488 .into_iter()
489 .collect(),
490 &DEFAULT_DEPENDENCIES,
491 &ROOT_MODULE_PATH
492 )
493 .unwrap(),
494 create_simple_config(
495 [(
496 "bar".into(),
497 ir_explicit_build(vec!["bar".into()], Rule::new("$x", None), vec![]).into()
498 )]
499 .into_iter()
500 .collect(),
501 ["bar".into()].into_iter().collect()
502 )
503 );
504 }
505
506 #[test]
507 fn interpolate_nested_variable_in_command() {
508 assert_eq!(
509 compile(
510 &[(
511 ROOT_MODULE_PATH.clone(),
512 ast::Module::new(vec![
513 ast::VariableDefinition::new("x", "42").into(),
514 ast::VariableDefinition::new("y", "$x").into(),
515 ast_rule("foo", &[("command", "$y")]).into(),
516 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
517 ])
518 )]
519 .into_iter()
520 .collect(),
521 &DEFAULT_DEPENDENCIES,
522 &ROOT_MODULE_PATH
523 )
524 .unwrap(),
525 create_simple_config(
526 [(
527 "bar".into(),
528 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
529 )]
530 .into_iter()
531 .collect(),
532 ["bar".into()].into_iter().collect()
533 )
534 );
535 }
536
537 #[test]
538 fn expand_variable_on_definition() {
539 assert_eq!(
540 compile(
541 &[(
542 ROOT_MODULE_PATH.clone(),
543 ast::Module::new(vec![
544 ast::VariableDefinition::new("x", "1").into(),
545 ast::VariableDefinition::new("y", "$x").into(),
546 ast::VariableDefinition::new("x", "2").into(),
547 ast_rule("foo", &[("command", "$y")]).into(),
548 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
549 ])
550 )]
551 .into_iter()
552 .collect(),
553 &DEFAULT_DEPENDENCIES,
554 &ROOT_MODULE_PATH
555 )
556 .unwrap(),
557 create_simple_config(
558 [(
559 "bar".into(),
560 ir_explicit_build(vec!["bar".into()], Rule::new("1", None), vec![]).into()
561 )]
562 .into_iter()
563 .collect(),
564 ["bar".into()].into_iter().collect()
565 )
566 );
567 }
568
569 #[test]
570 fn append_value_to_variable() {
571 assert_eq!(
572 compile(
573 &[(
574 ROOT_MODULE_PATH.clone(),
575 ast::Module::new(vec![
576 ast::VariableDefinition::new("x", "1").into(),
577 ast::VariableDefinition::new("x", "$x 2").into(),
578 ast_rule("foo", &[("command", "$x")]).into(),
579 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
580 ])
581 )]
582 .into_iter()
583 .collect(),
584 &DEFAULT_DEPENDENCIES,
585 &ROOT_MODULE_PATH
586 )
587 .unwrap(),
588 create_simple_config(
589 [(
590 "bar".into(),
591 ir_explicit_build(vec!["bar".into()], Rule::new("1 2", None), vec![]).into()
592 )]
593 .into_iter()
594 .collect(),
595 ["bar".into()].into_iter().collect()
596 )
597 );
598 }
599
600 #[test]
601 fn expand_escaped_variable_on_definition() {
602 assert_eq!(
603 compile(
604 &[(
605 ROOT_MODULE_PATH.clone(),
606 ast::Module::new(vec![
607 ast::VariableDefinition::new("y", "42").into(),
608 ast::VariableDefinition::new("x", "$$y").into(),
609 ast_rule("foo", &[("command", "$x")]).into(),
610 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
611 ])
612 )]
613 .into_iter()
614 .collect(),
615 &DEFAULT_DEPENDENCIES,
616 &ROOT_MODULE_PATH
617 )
618 .unwrap(),
619 create_simple_config(
620 [(
621 "bar".into(),
622 ir_explicit_build(vec!["bar".into()], Rule::new("$y", None), vec![]).into()
623 )]
624 .into_iter()
625 .collect(),
626 ["bar".into()].into_iter().collect()
627 )
628 );
629 }
630
631 #[test]
632 fn unescape_dollar_signs_once() {
633 assert_eq!(
634 compile(
635 &[(
636 ROOT_MODULE_PATH.clone(),
637 ast::Module::new(vec![
638 ast::VariableDefinition::new("x", "$$$$").into(),
639 ast_rule("foo", &[("command", "$x")]).into(),
640 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
641 ])
642 )]
643 .into_iter()
644 .collect(),
645 &DEFAULT_DEPENDENCIES,
646 &ROOT_MODULE_PATH
647 )
648 .unwrap(),
649 create_simple_config(
650 [(
651 "bar".into(),
652 ir_explicit_build(vec!["bar".into()], Rule::new("$$", None), vec![]).into()
653 )]
654 .into_iter()
655 .collect(),
656 ["bar".into()].into_iter().collect()
657 )
658 );
659 }
660
661 #[test]
662 fn interpolate_in_variable_in_command() {
663 assert_eq!(
664 compile(
665 &[(
666 ROOT_MODULE_PATH.clone(),
667 ast::Module::new(vec![
668 ast_rule("foo", &[("command", "$in")]).into(),
669 ast_explicit_build(vec!["bar".into()], "foo", vec!["baz".into()], vec![])
670 .into(),
671 ])
672 )]
673 .into_iter()
674 .collect(),
675 &DEFAULT_DEPENDENCIES,
676 &ROOT_MODULE_PATH
677 )
678 .unwrap(),
679 create_simple_config(
680 [(
681 "bar".into(),
682 ir_explicit_build(
683 vec!["bar".into()],
684 Rule::new("baz", None),
685 vec!["baz".into()]
686 )
687 .into()
688 )]
689 .into_iter()
690 .collect(),
691 ["bar".into()].into_iter().collect()
692 )
693 );
694 }
695
696 #[test]
697 fn interpolate_in_variable_with_implicit_input() {
698 assert_eq!(
699 compile(
700 &[(
701 ROOT_MODULE_PATH.clone(),
702 ast::Module::new(vec![
703 ast_rule("foo", &[("command", "$in")]).into(),
704 ast::Build::new(
705 vec!["bar".into()],
706 vec![],
707 "foo",
708 vec!["baz".into()],
709 vec!["blah".into()],
710 vec![],
711 vec![]
712 )
713 .into(),
714 ])
715 )]
716 .into_iter()
717 .collect(),
718 &DEFAULT_DEPENDENCIES,
719 &ROOT_MODULE_PATH
720 )
721 .unwrap(),
722 create_simple_config(
723 [(
724 "bar".into(),
725 ir_explicit_build(
726 vec!["bar".into()],
727 Rule::new("baz", None),
728 vec!["baz".into(), "blah".into()]
729 )
730 .into()
731 )]
732 .into_iter()
733 .collect(),
734 ["bar".into()].into_iter().collect()
735 )
736 );
737 }
738
739 #[test]
740 fn interpolate_out_variable_in_command() {
741 assert_eq!(
742 compile(
743 &[(
744 ROOT_MODULE_PATH.clone(),
745 ast::Module::new(vec![
746 ast_rule("foo", &[("command", "$out")]).into(),
747 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
748 ])
749 )]
750 .into_iter()
751 .collect(),
752 &DEFAULT_DEPENDENCIES,
753 &ROOT_MODULE_PATH
754 )
755 .unwrap(),
756 create_simple_config(
757 [(
758 "bar".into(),
759 ir_explicit_build(vec!["bar".into()], Rule::new("bar", None), vec![]).into()
760 )]
761 .into_iter()
762 .collect(),
763 ["bar".into()].into_iter().collect()
764 )
765 );
766 }
767
768 #[test]
769 fn interpolate_out_variable_with_implicit_output() {
770 let build = Arc::new(Build::new(
771 vec!["bar".into()],
772 vec!["baz".into()],
773 Rule::new("bar", None).into(),
774 vec![],
775 vec![],
776 None,
777 ));
778
779 assert_eq!(
780 compile(
781 &[(
782 ROOT_MODULE_PATH.clone(),
783 ast::Module::new(vec![
784 ast_rule("foo", &[("command", "$out")]).into(),
785 ast::Build::new(
786 vec!["bar".into()],
787 vec!["baz".into()],
788 "foo",
789 vec![],
790 vec![],
791 vec![],
792 vec![]
793 )
794 .into(),
795 ])
796 )]
797 .into_iter()
798 .collect(),
799 &DEFAULT_DEPENDENCIES,
800 &ROOT_MODULE_PATH
801 )
802 .unwrap(),
803 create_simple_config(
804 [("baz".into(), build.clone()), ("bar".into(), build)]
805 .into_iter()
806 .collect(),
807 ["baz".into(), "bar".into()].into_iter().collect()
808 )
809 );
810 }
811
812 #[test]
813 fn compile_order_only_inputs() {
814 assert_eq!(
815 compile(
816 &[(
817 ROOT_MODULE_PATH.clone(),
818 ast::Module::new(vec![
819 ast_rule("foo", &[("command", "$in")]).into(),
820 ast::Build::new(
821 vec!["bar".into()],
822 vec![],
823 "foo",
824 vec![],
825 vec![],
826 vec!["baz".into()],
827 vec![]
828 )
829 .into(),
830 ])
831 )]
832 .into_iter()
833 .collect(),
834 &DEFAULT_DEPENDENCIES,
835 &ROOT_MODULE_PATH
836 )
837 .unwrap(),
838 create_simple_config(
839 [(
840 "bar".into(),
841 Build::new(
842 vec!["bar".into()],
843 vec![],
844 Some(Rule::new("", None)),
845 vec![],
846 vec!["baz".into()],
847 None
848 )
849 .into()
850 )]
851 .into_iter()
852 .collect(),
853 ["bar".into()].into_iter().collect()
854 )
855 );
856 }
857
858 #[test]
859 fn generate_build_ids() {
860 assert_eq!(
861 compile(
862 &[(
863 ROOT_MODULE_PATH.clone(),
864 ast::Module::new(vec![
865 ast_rule("foo", &[("command", "")]).into(),
866 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
867 ast_explicit_build(vec!["baz".into()], "foo", vec![], vec![]).into()
868 ])
869 )]
870 .into_iter()
871 .collect(),
872 &DEFAULT_DEPENDENCIES,
873 &ROOT_MODULE_PATH
874 )
875 .unwrap(),
876 create_simple_config(
877 [
878 (
879 "bar".into(),
880 ir_explicit_build(vec!["bar".into()], Rule::new("", None), vec![]).into()
881 ),
882 (
883 "baz".into(),
884 ir_explicit_build(vec!["baz".into()], Rule::new("", None), vec![]).into()
885 )
886 ]
887 .into_iter()
888 .collect(),
889 ["bar".into(), "baz".into()].into_iter().collect()
890 )
891 );
892 }
893
894 #[test]
895 fn interpolate_build_local_variable() {
896 assert_eq!(
897 compile(
898 &[(
899 ROOT_MODULE_PATH.clone(),
900 ast::Module::new(vec![
901 ast_rule("foo", &[("command", "$x")]).into(),
902 ast_explicit_build(
903 vec!["bar".into()],
904 "foo",
905 vec![],
906 vec![ast::VariableDefinition::new("x", "42")]
907 )
908 .into(),
909 ])
910 )]
911 .into_iter()
912 .collect(),
913 &DEFAULT_DEPENDENCIES,
914 &ROOT_MODULE_PATH
915 )
916 .unwrap(),
917 create_simple_config(
918 [(
919 "bar".into(),
920 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
921 )]
922 .into_iter()
923 .collect(),
924 ["bar".into()].into_iter().collect()
925 )
926 );
927 }
928
929 #[test]
930 fn interpolate_rule_variable_in_command() {
931 assert_eq!(
932 compile(
933 &[(
934 ROOT_MODULE_PATH.clone(),
935 ast::Module::new(vec![
936 ast_rule(
937 "foo",
938 &[("command", "$description"), ("description", "bar")]
939 )
940 .into(),
941 ast_explicit_build(vec!["baz".into()], "foo", vec![], vec![]).into(),
942 ])
943 )]
944 .into_iter()
945 .collect(),
946 &DEFAULT_DEPENDENCIES,
947 &ROOT_MODULE_PATH
948 )
949 .unwrap(),
950 create_simple_config(
951 [(
952 "baz".into(),
953 ir_explicit_build(
954 vec!["baz".into()],
955 Rule::new("bar", Some("bar".into())),
956 vec![]
957 )
958 .into()
959 )]
960 .into_iter()
961 .collect(),
962 ["baz".into()].into_iter().collect()
963 )
964 );
965 }
966
967 #[test]
968 fn compile_last_command_in_rule() {
969 assert_eq!(
970 compile(
971 &[(
972 ROOT_MODULE_PATH.clone(),
973 ast::Module::new(vec![
974 ast_rule("foo", &[("command", "first"), ("command", "second")]).into(),
975 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
976 ])
977 )]
978 .into_iter()
979 .collect(),
980 &DEFAULT_DEPENDENCIES,
981 &ROOT_MODULE_PATH
982 )
983 .unwrap(),
984 create_simple_config(
985 [(
986 "bar".into(),
987 ir_explicit_build(vec!["bar".into()], Rule::new("second", None), vec![]).into()
988 )]
989 .into_iter()
990 .collect(),
991 ["bar".into()].into_iter().collect()
992 )
993 );
994 }
995
996 #[test]
997 fn compile_build_level_command_shadowing_rule_command() {
998 assert_eq!(
999 compile(
1000 &[(
1001 ROOT_MODULE_PATH.clone(),
1002 ast::Module::new(vec![
1003 ast_rule("foo", &[("command", "rule")]).into(),
1004 ast_explicit_build(
1005 vec!["bar".into()],
1006 "foo",
1007 vec![],
1008 vec![ast::VariableDefinition::new("command", "build")]
1009 )
1010 .into(),
1011 ])
1012 )]
1013 .into_iter()
1014 .collect(),
1015 &DEFAULT_DEPENDENCIES,
1016 &ROOT_MODULE_PATH
1017 )
1018 .unwrap(),
1019 create_simple_config(
1020 [(
1021 "bar".into(),
1022 ir_explicit_build(vec!["bar".into()], Rule::new("build", None), vec![]).into()
1023 )]
1024 .into_iter()
1025 .collect(),
1026 ["bar".into()].into_iter().collect()
1027 )
1028 );
1029 }
1030
1031 #[test]
1032 fn compile_global_description() {
1033 assert_eq!(
1034 compile(
1035 &[(
1036 ROOT_MODULE_PATH.clone(),
1037 ast::Module::new(vec![
1038 ast::VariableDefinition::new("description", "global").into(),
1039 ast_rule("foo", &[("command", "")]).into(),
1040 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1041 ])
1042 )]
1043 .into_iter()
1044 .collect(),
1045 &DEFAULT_DEPENDENCIES,
1046 &ROOT_MODULE_PATH
1047 )
1048 .unwrap(),
1049 create_simple_config(
1050 [(
1051 "bar".into(),
1052 ir_explicit_build(
1053 vec!["bar".into()],
1054 Rule::new("", Some("global".into())),
1055 vec![]
1056 )
1057 .into()
1058 )]
1059 .into_iter()
1060 .collect(),
1061 ["bar".into()].into_iter().collect()
1062 )
1063 );
1064 }
1065
1066 #[test]
1067 fn compile_source_map() {
1068 assert_eq!(
1069 compile(
1070 &[(
1071 ROOT_MODULE_PATH.clone(),
1072 ast::Module::new(vec![
1073 ast_rule("foo", &[("command", "foo")]).into(),
1074 ast_explicit_build(
1075 vec!["bar".into()],
1076 "foo",
1077 vec![],
1078 vec![ast::VariableDefinition::new(
1079 SOURCE_VARIABLE_NAME,
1080 "oh-my-src"
1081 )]
1082 )
1083 .into(),
1084 ])
1085 )]
1086 .into_iter()
1087 .collect(),
1088 &DEFAULT_DEPENDENCIES,
1089 &ROOT_MODULE_PATH
1090 )
1091 .unwrap(),
1092 Config::new(
1093 [(
1094 "bar".into(),
1095 ir_explicit_build(vec!["bar".into()], Rule::new("foo", None), vec![]).into()
1096 )]
1097 .into_iter()
1098 .collect(),
1099 ["bar".into()].into_iter().collect(),
1100 [("bar".into(), "oh-my-src".into())].into_iter().collect(),
1101 None,
1102 )
1103 );
1104 }
1105
1106 #[test]
1107 fn compile_phony_rule() {
1108 assert_eq!(
1109 compile(
1110 &[(
1111 ROOT_MODULE_PATH.clone(),
1112 ast::Module::new(vec![
1113 ast_explicit_build(vec!["foo".into()], "phony", vec!["bar".into()], vec![])
1114 .into(),
1115 ])
1116 )]
1117 .into_iter()
1118 .collect(),
1119 &DEFAULT_DEPENDENCIES,
1120 &ROOT_MODULE_PATH
1121 )
1122 .unwrap(),
1123 create_simple_config(
1124 [(
1125 "foo".into(),
1126 Build::new(
1127 vec!["foo".into()],
1128 vec![],
1129 None,
1130 vec!["bar".into()],
1131 vec![],
1132 None
1133 )
1134 .into()
1135 )]
1136 .into_iter()
1137 .collect(),
1138 ["foo".into()].into_iter().collect()
1139 )
1140 );
1141 }
1142
1143 #[test]
1144 fn compile_build_directory() {
1145 assert_eq!(
1146 compile(
1147 &[(
1148 ROOT_MODULE_PATH.clone(),
1149 ast::Module::new(vec![ast::VariableDefinition::new("builddir", "foo").into()])
1150 )]
1151 .into_iter()
1152 .collect(),
1153 &DEFAULT_DEPENDENCIES,
1154 &ROOT_MODULE_PATH
1155 )
1156 .unwrap(),
1157 Config::new(
1158 Default::default(),
1159 Default::default(),
1160 Default::default(),
1161 Some("foo".into())
1162 )
1163 );
1164 }
1165
1166 #[test]
1167 fn compile_build_directory_with_variable() {
1168 assert_eq!(
1169 compile(
1170 &[(
1171 ROOT_MODULE_PATH.clone(),
1172 ast::Module::new(vec![
1173 ast::VariableDefinition::new("x", "foo").into(),
1174 ast::VariableDefinition::new("builddir", "$x/bar").into(),
1175 ])
1176 )]
1177 .into_iter()
1178 .collect(),
1179 &DEFAULT_DEPENDENCIES,
1180 &ROOT_MODULE_PATH
1181 )
1182 .unwrap(),
1183 Config::new(
1184 Default::default(),
1185 Default::default(),
1186 Default::default(),
1187 Some("foo/bar".into())
1188 )
1189 );
1190 }
1191
1192 #[test]
1193 fn compile_dynamic_module_variable() {
1194 assert_eq!(
1195 compile(
1196 &[(
1197 ROOT_MODULE_PATH.clone(),
1198 ast::Module::new(vec![
1199 ast_explicit_build(
1200 vec!["foo".into()],
1201 "phony",
1202 vec![],
1203 vec![ast::VariableDefinition::new("dyndep", "bar")]
1204 )
1205 .into()
1206 ])
1207 )]
1208 .into_iter()
1209 .collect(),
1210 &DEFAULT_DEPENDENCIES,
1211 &ROOT_MODULE_PATH
1212 )
1213 .unwrap(),
1214 create_simple_config(
1215 [(
1216 "foo".into(),
1217 Build::new(
1218 vec!["foo".into()],
1219 vec![],
1220 None,
1221 vec![],
1222 vec![],
1223 Some("bar".into())
1224 )
1225 .into()
1226 )]
1227 .into_iter()
1228 .collect(),
1229 ["foo".into()].into_iter().collect()
1230 )
1231 );
1232 }
1233
1234 #[test]
1235 fn compile_rule_level_dynamic_module_variable() {
1236 assert_eq!(
1237 compile(
1238 &[(
1239 ROOT_MODULE_PATH.clone(),
1240 ast::Module::new(vec![
1241 ast_rule("foo", &[("command", ""), ("dyndep", "$out.dd")]).into(),
1242 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1243 ])
1244 )]
1245 .into_iter()
1246 .collect(),
1247 &DEFAULT_DEPENDENCIES,
1248 &ROOT_MODULE_PATH
1249 )
1250 .unwrap(),
1251 create_simple_config(
1252 [(
1253 "bar".into(),
1254 Build::new(
1255 vec!["bar".into()],
1256 vec![],
1257 Some(Rule::new("", None)),
1258 vec![],
1259 vec![],
1260 Some("bar.dd".into())
1261 )
1262 .into()
1263 )]
1264 .into_iter()
1265 .collect(),
1266 ["bar".into()].into_iter().collect()
1267 )
1268 );
1269 }
1270
1271 #[test]
1272 fn compile_build_level_dynamic_module_variable_shadowing_rule_level_one() {
1273 assert_eq!(
1274 compile(
1275 &[(
1276 ROOT_MODULE_PATH.clone(),
1277 ast::Module::new(vec![
1278 ast_rule("foo", &[("command", ""), ("dyndep", "rule.dd")]).into(),
1279 ast_explicit_build(
1280 vec!["bar".into()],
1281 "foo",
1282 vec![],
1283 vec![ast::VariableDefinition::new("dyndep", "build.dd")]
1284 )
1285 .into(),
1286 ])
1287 )]
1288 .into_iter()
1289 .collect(),
1290 &DEFAULT_DEPENDENCIES,
1291 &ROOT_MODULE_PATH
1292 )
1293 .unwrap(),
1294 create_simple_config(
1295 [(
1296 "bar".into(),
1297 Build::new(
1298 vec!["bar".into()],
1299 vec![],
1300 Some(Rule::new("", None)),
1301 vec![],
1302 vec![],
1303 Some("build.dd".into())
1304 )
1305 .into()
1306 )]
1307 .into_iter()
1308 .collect(),
1309 ["bar".into()].into_iter().collect()
1310 )
1311 );
1312 }
1313
1314 #[test]
1315 fn compile_make_header_dependency() {
1316 assert_eq!(
1317 compile(
1318 &[(
1319 ROOT_MODULE_PATH.clone(),
1320 ast::Module::new(vec![
1321 ast_rule("foo", &[("command", "bar"), ("depfile", "foo.d")]).into(),
1322 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1323 ])
1324 )]
1325 .into_iter()
1326 .collect(),
1327 &DEFAULT_DEPENDENCIES,
1328 &ROOT_MODULE_PATH
1329 )
1330 .unwrap(),
1331 create_simple_config(
1332 [(
1333 "bar".into(),
1334 ir_explicit_build(
1335 vec!["bar".into()],
1336 Rule::new("bar", None).with_header_dependency(Some(
1337 HeaderDependency::Make {
1338 path: "foo.d".into()
1339 }
1340 )),
1341 vec![]
1342 )
1343 .into()
1344 )]
1345 .into_iter()
1346 .collect(),
1347 ["bar".into()].into_iter().collect()
1348 )
1349 );
1350 }
1351
1352 #[test]
1353 fn compile_gcc_header_dependency() {
1354 assert_eq!(
1355 compile(
1356 &[(
1357 ROOT_MODULE_PATH.clone(),
1358 ast::Module::new(vec![
1359 ast_rule(
1360 "foo",
1361 &[("command", "bar"), ("depfile", "foo.d"), ("deps", "gcc")]
1362 )
1363 .into(),
1364 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1365 ])
1366 )]
1367 .into_iter()
1368 .collect(),
1369 &DEFAULT_DEPENDENCIES,
1370 &ROOT_MODULE_PATH
1371 )
1372 .unwrap(),
1373 create_simple_config(
1374 [(
1375 "bar".into(),
1376 ir_explicit_build(
1377 vec!["bar".into()],
1378 Rule::new("bar", None).with_header_dependency(Some(
1379 HeaderDependency::Gcc {
1380 path: "foo.d".into()
1381 }
1382 )),
1383 vec![]
1384 )
1385 .into()
1386 )]
1387 .into_iter()
1388 .collect(),
1389 ["bar".into()].into_iter().collect()
1390 )
1391 );
1392 }
1393
1394 #[test]
1395 fn compile_make_header_dependency_with_build_level_depfile() {
1396 assert_eq!(
1397 compile(
1398 &[(
1399 ROOT_MODULE_PATH.clone(),
1400 ast::Module::new(vec![
1401 ast_rule("foo", &[("command", "bar")]).into(),
1402 ast_explicit_build(
1403 vec!["bar".into()],
1404 "foo",
1405 vec![],
1406 vec![ast::VariableDefinition::new("depfile", "foo.d")]
1407 )
1408 .into(),
1409 ])
1410 )]
1411 .into_iter()
1412 .collect(),
1413 &DEFAULT_DEPENDENCIES,
1414 &ROOT_MODULE_PATH
1415 )
1416 .unwrap(),
1417 create_simple_config(
1418 [(
1419 "bar".into(),
1420 ir_explicit_build(
1421 vec!["bar".into()],
1422 Rule::new("bar", None).with_header_dependency(Some(
1423 HeaderDependency::Make {
1424 path: "foo.d".into()
1425 }
1426 )),
1427 vec![]
1428 )
1429 .into()
1430 )]
1431 .into_iter()
1432 .collect(),
1433 ["bar".into()].into_iter().collect()
1434 )
1435 );
1436 }
1437
1438 #[test]
1439 fn compile_gcc_header_dependency_with_build_level_deps() {
1440 assert_eq!(
1441 compile(
1442 &[(
1443 ROOT_MODULE_PATH.clone(),
1444 ast::Module::new(vec![
1445 ast_rule("foo", &[("command", "bar"), ("depfile", "$out.d")]).into(),
1446 ast_explicit_build(
1447 vec!["bar".into()],
1448 "foo",
1449 vec![],
1450 vec![ast::VariableDefinition::new("deps", "gcc")]
1451 )
1452 .into(),
1453 ])
1454 )]
1455 .into_iter()
1456 .collect(),
1457 &DEFAULT_DEPENDENCIES,
1458 &ROOT_MODULE_PATH
1459 )
1460 .unwrap(),
1461 create_simple_config(
1462 [(
1463 "bar".into(),
1464 ir_explicit_build(
1465 vec!["bar".into()],
1466 Rule::new("bar", None).with_header_dependency(Some(
1467 HeaderDependency::Gcc {
1468 path: "bar.d".into()
1469 }
1470 )),
1471 vec![]
1472 )
1473 .into()
1474 )]
1475 .into_iter()
1476 .collect(),
1477 ["bar".into()].into_iter().collect()
1478 )
1479 );
1480 }
1481
1482 #[test]
1483 fn compile_make_header_dependency_with_build_level_empty_deps_shadowing_rule_deps() {
1484 assert_eq!(
1485 compile(
1486 &[(
1487 ROOT_MODULE_PATH.clone(),
1488 ast::Module::new(vec![
1489 ast_rule(
1490 "foo",
1491 &[("command", "bar"), ("depfile", "foo.d"), ("deps", "gcc")]
1492 )
1493 .into(),
1494 ast_explicit_build(
1495 vec!["bar".into()],
1496 "foo",
1497 vec![],
1498 vec![ast::VariableDefinition::new("deps", "")]
1499 )
1500 .into(),
1501 ])
1502 )]
1503 .into_iter()
1504 .collect(),
1505 &DEFAULT_DEPENDENCIES,
1506 &ROOT_MODULE_PATH
1507 )
1508 .unwrap(),
1509 create_simple_config(
1510 [(
1511 "bar".into(),
1512 ir_explicit_build(
1513 vec!["bar".into()],
1514 Rule::new("bar", None).with_header_dependency(Some(
1515 HeaderDependency::Make {
1516 path: "foo.d".into()
1517 }
1518 )),
1519 vec![]
1520 )
1521 .into()
1522 )]
1523 .into_iter()
1524 .collect(),
1525 ["bar".into()].into_iter().collect()
1526 )
1527 );
1528 }
1529
1530 #[test]
1531 fn fail_to_compile_gcc_header_dependency_without_depfile() {
1532 assert_eq!(
1533 compile(
1534 &[(
1535 ROOT_MODULE_PATH.clone(),
1536 ast::Module::new(vec![
1537 ast_rule("foo", &[("command", "bar"), ("deps", "gcc")]).into(),
1538 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1539 ])
1540 )]
1541 .into_iter()
1542 .collect(),
1543 &DEFAULT_DEPENDENCIES,
1544 &ROOT_MODULE_PATH
1545 ),
1546 Err(CompileError::MissingDepfile("foo".into()))
1547 );
1548 }
1549
1550 #[test]
1551 fn fail_to_compile_unknown_header_dependency() {
1552 assert_eq!(
1553 compile(
1554 &[(
1555 ROOT_MODULE_PATH.clone(),
1556 ast::Module::new(vec![
1557 ast_rule("foo", &[("command", "bar"), ("deps", "clang")]).into(),
1558 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1559 ])
1560 )]
1561 .into_iter()
1562 .collect(),
1563 &DEFAULT_DEPENDENCIES,
1564 &ROOT_MODULE_PATH
1565 ),
1566 Err(CompileError::InvalidDependencyStyle("clang".into()))
1567 );
1568 }
1569
1570 #[test]
1571 fn compile_msvc_header_dependency_with_default_prefix() {
1572 assert_eq!(
1573 compile(
1574 &[(
1575 ROOT_MODULE_PATH.clone(),
1576 ast::Module::new(vec![
1577 ast_rule("foo", &[("command", "bar"), ("deps", "msvc")]).into(),
1578 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1579 ])
1580 )]
1581 .into_iter()
1582 .collect(),
1583 &DEFAULT_DEPENDENCIES,
1584 &ROOT_MODULE_PATH
1585 )
1586 .unwrap(),
1587 create_simple_config(
1588 [(
1589 "bar".into(),
1590 ir_explicit_build(
1591 vec!["bar".into()],
1592 Rule::new("bar", None).with_header_dependency(Some(
1593 HeaderDependency::Msvc {
1594 prefix: "Note: including file: ".into()
1595 }
1596 )),
1597 vec![]
1598 )
1599 .into()
1600 )]
1601 .into_iter()
1602 .collect(),
1603 ["bar".into()].into_iter().collect()
1604 )
1605 );
1606 }
1607
1608 #[test]
1609 fn compile_msvc_header_dependency_ignoring_depfile() {
1610 assert_eq!(
1611 compile(
1612 &[(
1613 ROOT_MODULE_PATH.clone(),
1614 ast::Module::new(vec![
1615 ast_rule(
1616 "foo",
1617 &[("command", "bar"), ("depfile", "foo.d"), ("deps", "msvc")]
1618 )
1619 .into(),
1620 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1621 ])
1622 )]
1623 .into_iter()
1624 .collect(),
1625 &DEFAULT_DEPENDENCIES,
1626 &ROOT_MODULE_PATH
1627 )
1628 .unwrap(),
1629 create_simple_config(
1630 [(
1631 "bar".into(),
1632 ir_explicit_build(
1633 vec!["bar".into()],
1634 Rule::new("bar", None).with_header_dependency(Some(
1635 HeaderDependency::Msvc {
1636 prefix: "Note: including file: ".into()
1637 }
1638 )),
1639 vec![]
1640 )
1641 .into()
1642 )]
1643 .into_iter()
1644 .collect(),
1645 ["bar".into()].into_iter().collect()
1646 )
1647 );
1648 }
1649
1650 #[test]
1651 fn compile_msvc_header_dependency_with_empty_prefix() {
1652 assert_eq!(
1653 compile(
1654 &[(
1655 ROOT_MODULE_PATH.clone(),
1656 ast::Module::new(vec![
1657 ast_rule(
1658 "foo",
1659 &[
1660 ("command", "bar"),
1661 ("deps", "msvc"),
1662 ("msvc_deps_prefix", "")
1663 ]
1664 )
1665 .into(),
1666 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1667 ])
1668 )]
1669 .into_iter()
1670 .collect(),
1671 &DEFAULT_DEPENDENCIES,
1672 &ROOT_MODULE_PATH
1673 )
1674 .unwrap(),
1675 create_simple_config(
1676 [(
1677 "bar".into(),
1678 ir_explicit_build(
1679 vec!["bar".into()],
1680 Rule::new("bar", None).with_header_dependency(Some(
1681 HeaderDependency::Msvc {
1682 prefix: "Note: including file: ".into()
1683 }
1684 )),
1685 vec![]
1686 )
1687 .into()
1688 )]
1689 .into_iter()
1690 .collect(),
1691 ["bar".into()].into_iter().collect()
1692 )
1693 );
1694 }
1695
1696 #[test]
1697 fn compile_msvc_header_dependency_with_custom_prefix() {
1698 assert_eq!(
1699 compile(
1700 &[(
1701 ROOT_MODULE_PATH.clone(),
1702 ast::Module::new(vec![
1703 ast_rule(
1704 "foo",
1705 &[
1706 ("command", "bar"),
1707 ("deps", "msvc"),
1708 ("msvc_deps_prefix", "Hinweis: Einlesen der Datei ")
1709 ]
1710 )
1711 .into(),
1712 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1713 ])
1714 )]
1715 .into_iter()
1716 .collect(),
1717 &DEFAULT_DEPENDENCIES,
1718 &ROOT_MODULE_PATH
1719 )
1720 .unwrap(),
1721 create_simple_config(
1722 [(
1723 "bar".into(),
1724 ir_explicit_build(
1725 vec!["bar".into()],
1726 Rule::new("bar", None).with_header_dependency(Some(
1727 HeaderDependency::Msvc {
1728 prefix: "Hinweis: Einlesen der Datei ".into()
1729 }
1730 )),
1731 vec![]
1732 )
1733 .into()
1734 )]
1735 .into_iter()
1736 .collect(),
1737 ["bar".into()].into_iter().collect()
1738 )
1739 );
1740 }
1741
1742 #[test]
1743 fn compile_msvc_header_dependency_with_global_prefix() {
1744 assert_eq!(
1745 compile(
1746 &[(
1747 ROOT_MODULE_PATH.clone(),
1748 ast::Module::new(vec![
1749 ast::VariableDefinition::new(
1750 "msvc_deps_prefix",
1751 "Hinweis: Einlesen der Datei "
1752 )
1753 .into(),
1754 ast_rule("foo", &[("command", "bar"), ("deps", "msvc")]).into(),
1755 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1756 ])
1757 )]
1758 .into_iter()
1759 .collect(),
1760 &DEFAULT_DEPENDENCIES,
1761 &ROOT_MODULE_PATH
1762 )
1763 .unwrap(),
1764 create_simple_config(
1765 [(
1766 "bar".into(),
1767 ir_explicit_build(
1768 vec!["bar".into()],
1769 Rule::new("bar", None).with_header_dependency(Some(
1770 HeaderDependency::Msvc {
1771 prefix: "Hinweis: Einlesen der Datei ".into()
1772 }
1773 )),
1774 vec![]
1775 )
1776 .into()
1777 )]
1778 .into_iter()
1779 .collect(),
1780 ["bar".into()].into_iter().collect()
1781 )
1782 );
1783 }
1784
1785 #[test]
1786 fn compile_msvc_header_dependency_with_rule_prefix_shadowing_global_prefix() {
1787 assert_eq!(
1788 compile(
1789 &[(
1790 ROOT_MODULE_PATH.clone(),
1791 ast::Module::new(vec![
1792 ast::VariableDefinition::new("msvc_deps_prefix", "global prefix: ").into(),
1793 ast_rule(
1794 "foo",
1795 &[
1796 ("command", "bar"),
1797 ("deps", "msvc"),
1798 ("msvc_deps_prefix", "rule prefix: ")
1799 ]
1800 )
1801 .into(),
1802 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1803 ])
1804 )]
1805 .into_iter()
1806 .collect(),
1807 &DEFAULT_DEPENDENCIES,
1808 &ROOT_MODULE_PATH
1809 )
1810 .unwrap(),
1811 create_simple_config(
1812 [(
1813 "bar".into(),
1814 ir_explicit_build(
1815 vec!["bar".into()],
1816 Rule::new("bar", None).with_header_dependency(Some(
1817 HeaderDependency::Msvc {
1818 prefix: "rule prefix: ".into()
1819 }
1820 )),
1821 vec![]
1822 )
1823 .into()
1824 )]
1825 .into_iter()
1826 .collect(),
1827 ["bar".into()].into_iter().collect()
1828 )
1829 );
1830 }
1831
1832 #[test]
1833 fn compile_msvc_header_dependency_with_build_level_prefix_shadowing_rule_prefix() {
1834 assert_eq!(
1835 compile(
1836 &[(
1837 ROOT_MODULE_PATH.clone(),
1838 ast::Module::new(vec![
1839 ast_rule(
1840 "foo",
1841 &[
1842 ("command", "bar"),
1843 ("deps", "msvc"),
1844 ("msvc_deps_prefix", "rule prefix: ")
1845 ]
1846 )
1847 .into(),
1848 ast_explicit_build(
1849 vec!["bar".into()],
1850 "foo",
1851 vec![],
1852 vec![ast::VariableDefinition::new(
1853 "msvc_deps_prefix",
1854 "build prefix: "
1855 )]
1856 )
1857 .into(),
1858 ])
1859 )]
1860 .into_iter()
1861 .collect(),
1862 &DEFAULT_DEPENDENCIES,
1863 &ROOT_MODULE_PATH
1864 )
1865 .unwrap(),
1866 create_simple_config(
1867 [(
1868 "bar".into(),
1869 ir_explicit_build(
1870 vec!["bar".into()],
1871 Rule::new("bar", None).with_header_dependency(Some(
1872 HeaderDependency::Msvc {
1873 prefix: "build prefix: ".into()
1874 }
1875 )),
1876 vec![]
1877 )
1878 .into()
1879 )]
1880 .into_iter()
1881 .collect(),
1882 ["bar".into()].into_iter().collect()
1883 )
1884 );
1885 }
1886
1887 mod submodule {
1888 use super::*;
1889 use pretty_assertions::assert_eq;
1890
1891 #[test]
1892 fn reference_variable_in_parent_module() {
1893 const SUBMODULE_PATH: &str = "foo.ninja";
1894
1895 assert_eq!(
1896 compile(
1897 &[
1898 (
1899 ROOT_MODULE_PATH.clone(),
1900 ast::Module::new(vec![
1901 ast::VariableDefinition::new("x", "42").into(),
1902 ast::Submodule::new(SUBMODULE_PATH).into(),
1903 ])
1904 ),
1905 (
1906 SUBMODULE_PATH.into(),
1907 ast::Module::new(vec![
1908 ast_rule("foo", &[("command", "$x")]).into(),
1909 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![])
1910 .into()
1911 ])
1912 )
1913 ]
1914 .into_iter()
1915 .collect(),
1916 &[(
1917 ROOT_MODULE_PATH.clone(),
1918 [(SUBMODULE_PATH.into(), PathBuf::from(SUBMODULE_PATH))]
1919 .into_iter()
1920 .collect()
1921 )]
1922 .into_iter()
1923 .collect(),
1924 &ROOT_MODULE_PATH
1925 )
1926 .unwrap(),
1927 create_simple_config(
1928 [(
1929 "bar".into(),
1930 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
1931 )]
1932 .into_iter()
1933 .collect(),
1934 ["bar".into()].into_iter().collect()
1935 )
1936 );
1937 }
1938
1939 #[test]
1940 fn reference_rule_in_parent_module() {
1941 const SUBMODULE_PATH: &str = "foo.ninja";
1942
1943 assert_eq!(
1944 compile(
1945 &[
1946 (
1947 ROOT_MODULE_PATH.clone(),
1948 ast::Module::new(vec![
1949 ast::VariableDefinition::new("x", "42").into(),
1950 ast_rule("foo", &[("command", "$x")]).into(),
1951 ast::Submodule::new(SUBMODULE_PATH).into(),
1952 ])
1953 ),
1954 (
1955 SUBMODULE_PATH.into(),
1956 ast::Module::new(vec![
1957 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![])
1958 .into()
1959 ])
1960 )
1961 ]
1962 .into_iter()
1963 .collect(),
1964 &[(
1965 ROOT_MODULE_PATH.clone(),
1966 [(SUBMODULE_PATH.into(), PathBuf::from(SUBMODULE_PATH))]
1967 .into_iter()
1968 .collect()
1969 )]
1970 .into_iter()
1971 .collect(),
1972 &ROOT_MODULE_PATH
1973 )
1974 .unwrap(),
1975 create_simple_config(
1976 [(
1977 "bar".into(),
1978 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
1979 )]
1980 .into_iter()
1981 .collect(),
1982 ["bar".into()].into_iter().collect()
1983 )
1984 );
1985 }
1986
1987 #[test]
1988 fn do_not_overwrite_variable_in_parent_module() {
1989 const SUBMODULE_PATH: &str = "foo.ninja";
1990
1991 assert_eq!(
1992 compile(
1993 &[
1994 (
1995 ROOT_MODULE_PATH.clone(),
1996 ast::Module::new(vec![
1997 ast::VariableDefinition::new("x", "42").into(),
1998 ast_rule("foo", &[("command", "$x")]).into(),
1999 ast::Submodule::new(SUBMODULE_PATH).into(),
2000 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![])
2001 .into(),
2002 ])
2003 ),
2004 (
2005 SUBMODULE_PATH.into(),
2006 ast::Module::new(vec![ast::VariableDefinition::new("x", "13").into(),])
2007 )
2008 ]
2009 .into_iter()
2010 .collect(),
2011 &[(
2012 ROOT_MODULE_PATH.clone(),
2013 [(SUBMODULE_PATH.into(), PathBuf::from(SUBMODULE_PATH))]
2014 .into_iter()
2015 .collect()
2016 )]
2017 .into_iter()
2018 .collect(),
2019 &ROOT_MODULE_PATH
2020 )
2021 .unwrap(),
2022 create_simple_config(
2023 [(
2024 "bar".into(),
2025 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
2026 )]
2027 .into_iter()
2028 .collect(),
2029 ["bar".into()].into_iter().collect()
2030 )
2031 );
2032 }
2033 }
2034}