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