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, Pool, Rule},
11 module_dependency::ModuleDependencyMap,
12};
13use alloc::{borrow::Cow, sync::Arc};
14use core::num::NonZeroUsize;
15use once_cell::sync::Lazy;
16use regex::{Captures, Regex};
17use std::{
18 collections::{HashMap, hash_map::Entry},
19 path::{Path, PathBuf},
20};
21use train_map::TrainMap;
22
23const PHONY_RULE: &str = "phony";
24const CONSOLE_POOL: &str = "console";
25const BUILD_DIRECTORY_VARIABLE: &str = "builddir";
26const COMMAND_VARIABLE: &str = "command";
27const DESCRIPTION_VARIABLE: &str = "description";
28const DEPFILE_VARIABLE: &str = "depfile";
29const DEPS_VARIABLE: &str = "deps";
30const DYNAMIC_MODULE_VARIABLE: &str = "dyndep";
31const POOL_VARIABLE: &str = "pool";
32const SOURCE_VARIABLE_NAME: &str = "srcdep";
33const MSVC_DEPS_PREFIX_VARIABLE: &str = "msvc_deps_prefix";
34const DEFAULT_MSVC_DEPS_PREFIX: &str = "Note: including file: ";
36
37static VARIABLE_PATTERN: Lazy<Regex> =
38 Lazy::new(|| Regex::new(r"\$(\$|\{([[:alnum:]_.-]+)\}|[[:alpha:]_][[:alnum:]_]*)").unwrap());
39
40pub fn compile(
43 modules: &HashMap<PathBuf, ast::Module>,
44 dependencies: &ModuleDependencyMap,
45 root_module_path: &Path,
46) -> Result<Config, CompileError> {
47 let context = Context::new(modules, dependencies);
48
49 let mut global_state = GlobalState {
50 outputs: Default::default(),
51 default_outputs: Default::default(),
52 source_map: Default::default(),
53 pools: [(CONSOLE_POOL.into(), None)].into(),
54 };
55 let mut module_state = ModuleState {
56 rules: TrainMap::new(),
57 variables: TrainMap::new(),
58 };
59
60 compile_module(
61 &context,
62 &mut global_state,
63 &mut module_state,
64 root_module_path,
65 )?;
66
67 let default_outputs = if global_state.default_outputs.is_empty() {
68 global_state.outputs.keys().cloned().collect()
69 } else {
70 global_state.default_outputs
71 };
72
73 Ok(Config::new(
74 global_state.outputs,
75 default_outputs,
76 global_state.source_map,
77 global_state
78 .pools
79 .into_iter()
80 .filter_map(|(name, depth)| Some((name, depth?)))
81 .collect(),
82 module_state
83 .variables
84 .get(BUILD_DIRECTORY_VARIABLE)
85 .cloned(),
86 ))
87}
88
89fn compile_module<'a>(
90 context: &'a Context,
91 global_state: &mut GlobalState,
92 module_state: &mut ModuleState<'a, '_>,
93 path: &Path,
94) -> Result<(), CompileError> {
95 let module = &context
96 .modules()
97 .get(path)
98 .ok_or_else(|| CompileError::ModuleNotFound(path.into()))?;
99
100 for statement in module.statements() {
101 match statement {
102 ast::Statement::Build(build) => {
103 let mut variables = module_state.variables.fork();
104
105 if build.rule() != PHONY_RULE {
106 variables.extend(
107 module_state
108 .rules
109 .get(build.rule())
110 .ok_or_else(|| CompileError::RuleNotFound(build.rule().into()))?
111 .iter()
112 .cloned(),
113 );
114 }
115
116 variables.extend(
117 build
118 .variable_definitions()
119 .iter()
120 .map(|definition| (definition.name(), definition.value().into())),
121 );
122
123 let pool = compile_pool(&global_state.pools, &variables)?;
124
125 let outputs = interpolate_strings(build.outputs(), &variables);
126 let implicit_outputs = interpolate_strings(build.implicit_outputs(), &variables);
127 let inputs = interpolate_strings(build.inputs(), &variables);
128 let implicit_inputs = interpolate_strings(build.implicit_inputs(), &variables);
129 let order_only_inputs = interpolate_strings(build.order_only_inputs(), &variables);
130
131 variables.extend([
132 ("in", inputs.join(" ").into()),
133 ("in_newline", inputs.join("\n").into()),
134 ("out", outputs.join(" ").into()),
135 ]);
136
137 let ir = Arc::new(Build::new(
138 outputs,
139 implicit_outputs,
140 if build.rule() == PHONY_RULE {
141 None
142 } else {
143 Some(
144 Rule::new(
145 resolve_variable(COMMAND_VARIABLE, &variables).unwrap_or_default(),
146 resolve_variable(DESCRIPTION_VARIABLE, &variables),
147 )
148 .with_pool(pool)
149 .with_header_dependency(
150 compile_header_dependency(build.rule(), &variables)?,
151 ),
152 )
153 },
154 inputs.into_iter().chain(implicit_inputs).collect(),
155 order_only_inputs,
156 resolve_variable(DYNAMIC_MODULE_VARIABLE, &variables).map(Into::into),
157 ));
158
159 let outputs = || ir.outputs().iter().chain(ir.implicit_outputs());
160
161 global_state
162 .outputs
163 .extend(outputs().map(|output| (output.clone(), ir.clone())));
164
165 if let Some(source) = variables.get(SOURCE_VARIABLE_NAME) {
166 global_state
167 .source_map
168 .extend(outputs().map(|output| (output.clone(), source.clone())));
169 }
170 }
171 ast::Statement::Default(default) => {
172 global_state.default_outputs.extend(interpolate_strings(
173 default.outputs(),
174 &module_state.variables,
175 ));
176 }
177 ast::Statement::Include(include) => {
178 compile_module(
179 context,
180 global_state,
181 module_state,
182 resolve_dependency(context, path, include.path())?,
183 )?;
184 }
185 ast::Statement::Pool(pool) => {
186 let Entry::Vacant(entry) = global_state.pools.entry(pool.name().into()) else {
187 return Err(CompileError::DuplicatePool(pool.name().into()));
188 };
189 let depth = interpolate_variables(pool.depth(), &module_state.variables);
190
191 entry.insert(NonZeroUsize::new(depth.trim().parse().map_err(|_| {
192 CompileError::InvalidPoolDepth(pool.name().into(), depth.as_ref().into())
193 })?));
194 }
195 ast::Statement::Rule(rule) => {
196 module_state.rules.insert(
197 rule.name(),
198 rule.variable_definitions()
199 .iter()
200 .map(|definition| (definition.name(), definition.value().into()))
201 .collect(),
202 );
203 }
204 ast::Statement::Submodule(submodule) => {
205 compile_module(
206 context,
207 global_state,
208 &mut module_state.fork(),
209 resolve_dependency(context, path, submodule.path())?,
210 )?;
211 }
212 ast::Statement::VariableDefinition(definition) => {
213 module_state.variables.insert(
214 definition.name(),
215 interpolate_variables(definition.value(), &module_state.variables).into(),
216 );
217 }
218 }
219 }
220
221 Ok(())
222}
223
224fn compile_header_dependency(
225 rule: &str,
226 variables: &TrainMap<&str, Arc<str>>,
227) -> Result<Option<HeaderDependency>, CompileError> {
228 Ok(
229 match (
230 resolve_variable(DEPS_VARIABLE, variables).as_deref(),
231 resolve_variable(DEPFILE_VARIABLE, variables),
232 ) {
233 (None, None) => None,
234 (None, Some(path)) => Some(HeaderDependency::Make { path }),
235 (Some("gcc"), Some(path)) => Some(HeaderDependency::Gcc { path }),
236 (Some("gcc"), None) => return Err(CompileError::MissingDepfile(rule.into())),
237 (Some("msvc"), _) => Some(HeaderDependency::Msvc {
238 prefix: resolve_variable(MSVC_DEPS_PREFIX_VARIABLE, variables)
239 .unwrap_or_else(|| DEFAULT_MSVC_DEPS_PREFIX.into()),
240 }),
241 (Some(deps), _) => return Err(CompileError::InvalidDependencyStyle(deps.into())),
242 },
243 )
244}
245
246fn compile_pool(
247 pools: &HashMap<Arc<str>, Option<NonZeroUsize>>,
248 variables: &TrainMap<&str, Arc<str>>,
249) -> Result<Option<Pool>, CompileError> {
250 let Some(name) = resolve_variable(POOL_VARIABLE, variables) else {
251 return Ok(None);
252 };
253 let (name, depth) = pools
254 .get_key_value(name.as_str())
255 .ok_or(CompileError::PoolNotFound(name))?;
256
257 Ok(if name.as_ref() == CONSOLE_POOL {
258 Some(Pool::Console)
259 } else if depth.is_some() {
260 Some(Pool::Limited(name.clone()))
261 } else {
262 None
263 })
264}
265
266pub fn compile_dynamic(module: &ast::DynamicModule) -> Result<DynamicConfig, CompileError> {
267 Ok(DynamicConfig::new(
268 module
269 .builds()
270 .iter()
271 .map(|build| {
272 (
273 build.output().into(),
274 DynamicBuild::new(
275 build
276 .implicit_inputs()
277 .iter()
278 .map(|string| string.as_str().into())
279 .collect(),
280 ),
281 )
282 })
283 .collect(),
284 ))
285}
286
287fn resolve_dependency<'a>(
288 context: &'a Context,
289 module_path: &Path,
290 submodule_path: &str,
291) -> Result<&'a Path, CompileError> {
292 Ok(context
293 .dependencies()
294 .get(module_path)
295 .ok_or_else(|| CompileError::ModuleNotFound(module_path.into()))?
296 .get(submodule_path)
297 .ok_or_else(|| CompileError::ModuleNotFound(submodule_path.into()))?)
298}
299
300fn resolve_variable(name: &str, variables: &TrainMap<&str, Arc<str>>) -> Option<String> {
301 variables
302 .get(name)
303 .map(|value| interpolate_variables(value, variables).into_owned())
304 .filter(|value| !value.is_empty())
305}
306
307fn interpolate_strings(strings: &[String], variables: &TrainMap<&str, Arc<str>>) -> Vec<Arc<str>> {
308 strings
309 .iter()
310 .map(|string| interpolate_variables(string, variables).into())
311 .collect()
312}
313
314fn interpolate_variables<'a>(
315 template: &'a str,
316 variables: &TrainMap<&str, Arc<str>>,
317) -> Cow<'a, str> {
318 VARIABLE_PATTERN.replace_all(template, |captures: &Captures| match &captures[1] {
319 "$" => "$",
320 name => variables
321 .get(captures.get(2).map_or(name, |name| name.as_str()))
322 .map(|string| string.as_ref())
323 .unwrap_or_default(),
324 })
325}
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330 use crate::ast;
331 use once_cell::sync::Lazy;
332 use pretty_assertions::assert_eq;
333 use std::collections::HashSet;
334
335 static ROOT_MODULE_PATH: Lazy<PathBuf> = Lazy::new(|| PathBuf::from("build.ninja"));
336 static DEFAULT_DEPENDENCIES: Lazy<ModuleDependencyMap> = Lazy::new(|| {
337 [(ROOT_MODULE_PATH.clone(), Default::default())]
338 .into_iter()
339 .collect()
340 });
341
342 fn ast_explicit_build(
343 outputs: Vec<String>,
344 rule: impl Into<String>,
345 inputs: Vec<String>,
346 variable_definitions: Vec<ast::VariableDefinition>,
347 ) -> ast::Build {
348 ast::Build::new(
349 outputs,
350 vec![],
351 rule,
352 inputs,
353 vec![],
354 vec![],
355 variable_definitions,
356 )
357 }
358
359 fn ast_rule(name: &str, variable_definitions: &[(&str, &str)]) -> ast::Rule {
360 ast::Rule::new(
361 name,
362 variable_definitions
363 .iter()
364 .map(|(name, value)| ast::VariableDefinition::new(*name, *value))
365 .collect(),
366 )
367 }
368
369 fn ir_explicit_build(outputs: Vec<Arc<str>>, rule: Rule, inputs: Vec<Arc<str>>) -> Build {
370 Build::new(outputs, vec![], rule.into(), inputs, vec![], None)
371 }
372
373 fn create_simple_config(
374 outputs: HashMap<Arc<str>, Arc<Build>>,
375 default_outputs: HashSet<Arc<str>>,
376 ) -> Config {
377 Config::new(
378 outputs,
379 default_outputs,
380 Default::default(),
381 Default::default(),
382 None,
383 )
384 }
385
386 fn compile_root_module(statements: Vec<ast::Statement>) -> Result<Config, CompileError> {
387 compile(
388 &[(ROOT_MODULE_PATH.clone(), ast::Module::new(statements))]
389 .into_iter()
390 .collect(),
391 &DEFAULT_DEPENDENCIES,
392 &ROOT_MODULE_PATH,
393 )
394 }
395
396 fn create_pool_config(pool: Option<Pool>, pools: &[(&str, usize)]) -> Config {
397 Config::new(
398 [(
399 "bar".into(),
400 ir_explicit_build(
401 vec!["bar".into()],
402 Rule::new("baz", None).with_pool(pool),
403 vec![],
404 )
405 .into(),
406 )]
407 .into_iter()
408 .collect(),
409 ["bar".into()].into_iter().collect(),
410 Default::default(),
411 pools
412 .iter()
413 .map(|&(name, depth)| (name.into(), NonZeroUsize::new(depth).unwrap()))
414 .collect(),
415 None,
416 )
417 }
418
419 fn limited_pool(name: &str) -> Option<Pool> {
420 Some(Pool::Limited(name.into()))
421 }
422
423 #[test]
424 fn compile_empty_module() {
425 assert_eq!(
426 compile(
427 &[(ROOT_MODULE_PATH.clone(), ast::Module::new(vec![]))]
428 .into_iter()
429 .collect(),
430 &DEFAULT_DEPENDENCIES,
431 &ROOT_MODULE_PATH
432 )
433 .unwrap(),
434 create_simple_config(Default::default(), Default::default())
435 );
436 }
437
438 #[test]
439 fn interpolate_variable_in_command() {
440 assert_eq!(
441 compile(
442 &[(
443 ROOT_MODULE_PATH.clone(),
444 ast::Module::new(vec![
445 ast::VariableDefinition::new("x", "42").into(),
446 ast_rule("foo", &[("command", "$x")]).into(),
447 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
448 ])
449 )]
450 .into_iter()
451 .collect(),
452 &DEFAULT_DEPENDENCIES,
453 &ROOT_MODULE_PATH
454 )
455 .unwrap(),
456 create_simple_config(
457 [(
458 "bar".into(),
459 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
460 )]
461 .into_iter()
462 .collect(),
463 ["bar".into()].into_iter().collect()
464 )
465 );
466 }
467
468 #[test]
469 fn interpolate_two_variables_in_command() {
470 assert_eq!(
471 compile(
472 &[(
473 ROOT_MODULE_PATH.clone(),
474 ast::Module::new(vec![
475 ast::VariableDefinition::new("x", "1").into(),
476 ast::VariableDefinition::new("y", "2").into(),
477 ast_rule("foo", &[("command", "$x $y")]).into(),
478 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
479 ])
480 )]
481 .into_iter()
482 .collect(),
483 &DEFAULT_DEPENDENCIES,
484 &ROOT_MODULE_PATH
485 )
486 .unwrap(),
487 create_simple_config(
488 [(
489 "bar".into(),
490 ir_explicit_build(vec!["bar".into()], Rule::new("1 2", None), vec![]).into()
491 )]
492 .into_iter()
493 .collect(),
494 ["bar".into()].into_iter().collect()
495 )
496 );
497 }
498
499 #[test]
500 fn interpolate_variable_with_underscore_in_command() {
501 assert_eq!(
502 compile(
503 &[(
504 ROOT_MODULE_PATH.clone(),
505 ast::Module::new(vec![
506 ast::VariableDefinition::new("x_y", "42").into(),
507 ast_rule("foo", &[("command", "$x_y")]).into(),
508 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
509 ])
510 )]
511 .into_iter()
512 .collect(),
513 &DEFAULT_DEPENDENCIES,
514 &ROOT_MODULE_PATH
515 )
516 .unwrap(),
517 create_simple_config(
518 [(
519 "bar".into(),
520 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
521 )]
522 .into_iter()
523 .collect(),
524 ["bar".into()].into_iter().collect()
525 )
526 );
527 }
528
529 #[test]
530 fn interpolate_variable_with_braces_in_command() {
531 assert_eq!(
532 compile(
533 &[(
534 ROOT_MODULE_PATH.clone(),
535 ast::Module::new(vec![
536 ast::VariableDefinition::new("x.y", "42").into(),
537 ast_rule("foo", &[("command", "${x.y}")]).into(),
538 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
539 ])
540 )]
541 .into_iter()
542 .collect(),
543 &DEFAULT_DEPENDENCIES,
544 &ROOT_MODULE_PATH
545 )
546 .unwrap(),
547 create_simple_config(
548 [(
549 "bar".into(),
550 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
551 )]
552 .into_iter()
553 .collect(),
554 ["bar".into()].into_iter().collect()
555 )
556 );
557 }
558
559 #[test]
560 fn interpolate_dollar_sign_in_command() {
561 assert_eq!(
562 compile(
563 &[(
564 ROOT_MODULE_PATH.clone(),
565 ast::Module::new(vec![
566 ast_rule("foo", &[("command", "$$")]).into(),
567 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into()
568 ])
569 )]
570 .into_iter()
571 .collect(),
572 &DEFAULT_DEPENDENCIES,
573 &ROOT_MODULE_PATH
574 )
575 .unwrap(),
576 create_simple_config(
577 [(
578 "bar".into(),
579 ir_explicit_build(vec!["bar".into()], Rule::new("$", None), vec![]).into()
580 )]
581 .into_iter()
582 .collect(),
583 ["bar".into()].into_iter().collect()
584 )
585 );
586 }
587
588 #[test]
589 fn interpolate_escaped_variable_in_command() {
590 assert_eq!(
591 compile(
592 &[(
593 ROOT_MODULE_PATH.clone(),
594 ast::Module::new(vec![
595 ast::VariableDefinition::new("x", "42").into(),
596 ast_rule("foo", &[("command", "$$x")]).into(),
597 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
598 ])
599 )]
600 .into_iter()
601 .collect(),
602 &DEFAULT_DEPENDENCIES,
603 &ROOT_MODULE_PATH
604 )
605 .unwrap(),
606 create_simple_config(
607 [(
608 "bar".into(),
609 ir_explicit_build(vec!["bar".into()], Rule::new("$x", None), vec![]).into()
610 )]
611 .into_iter()
612 .collect(),
613 ["bar".into()].into_iter().collect()
614 )
615 );
616 }
617
618 #[test]
619 fn interpolate_nested_variable_in_command() {
620 assert_eq!(
621 compile(
622 &[(
623 ROOT_MODULE_PATH.clone(),
624 ast::Module::new(vec![
625 ast::VariableDefinition::new("x", "42").into(),
626 ast::VariableDefinition::new("y", "$x").into(),
627 ast_rule("foo", &[("command", "$y")]).into(),
628 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
629 ])
630 )]
631 .into_iter()
632 .collect(),
633 &DEFAULT_DEPENDENCIES,
634 &ROOT_MODULE_PATH
635 )
636 .unwrap(),
637 create_simple_config(
638 [(
639 "bar".into(),
640 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
641 )]
642 .into_iter()
643 .collect(),
644 ["bar".into()].into_iter().collect()
645 )
646 );
647 }
648
649 #[test]
650 fn expand_variable_on_definition() {
651 assert_eq!(
652 compile(
653 &[(
654 ROOT_MODULE_PATH.clone(),
655 ast::Module::new(vec![
656 ast::VariableDefinition::new("x", "1").into(),
657 ast::VariableDefinition::new("y", "$x").into(),
658 ast::VariableDefinition::new("x", "2").into(),
659 ast_rule("foo", &[("command", "$y")]).into(),
660 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
661 ])
662 )]
663 .into_iter()
664 .collect(),
665 &DEFAULT_DEPENDENCIES,
666 &ROOT_MODULE_PATH
667 )
668 .unwrap(),
669 create_simple_config(
670 [(
671 "bar".into(),
672 ir_explicit_build(vec!["bar".into()], Rule::new("1", None), vec![]).into()
673 )]
674 .into_iter()
675 .collect(),
676 ["bar".into()].into_iter().collect()
677 )
678 );
679 }
680
681 #[test]
682 fn append_value_to_variable() {
683 assert_eq!(
684 compile(
685 &[(
686 ROOT_MODULE_PATH.clone(),
687 ast::Module::new(vec![
688 ast::VariableDefinition::new("x", "1").into(),
689 ast::VariableDefinition::new("x", "$x 2").into(),
690 ast_rule("foo", &[("command", "$x")]).into(),
691 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
692 ])
693 )]
694 .into_iter()
695 .collect(),
696 &DEFAULT_DEPENDENCIES,
697 &ROOT_MODULE_PATH
698 )
699 .unwrap(),
700 create_simple_config(
701 [(
702 "bar".into(),
703 ir_explicit_build(vec!["bar".into()], Rule::new("1 2", None), vec![]).into()
704 )]
705 .into_iter()
706 .collect(),
707 ["bar".into()].into_iter().collect()
708 )
709 );
710 }
711
712 #[test]
713 fn expand_escaped_variable_on_definition() {
714 assert_eq!(
715 compile(
716 &[(
717 ROOT_MODULE_PATH.clone(),
718 ast::Module::new(vec![
719 ast::VariableDefinition::new("y", "42").into(),
720 ast::VariableDefinition::new("x", "$$y").into(),
721 ast_rule("foo", &[("command", "$x")]).into(),
722 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
723 ])
724 )]
725 .into_iter()
726 .collect(),
727 &DEFAULT_DEPENDENCIES,
728 &ROOT_MODULE_PATH
729 )
730 .unwrap(),
731 create_simple_config(
732 [(
733 "bar".into(),
734 ir_explicit_build(vec!["bar".into()], Rule::new("$y", None), vec![]).into()
735 )]
736 .into_iter()
737 .collect(),
738 ["bar".into()].into_iter().collect()
739 )
740 );
741 }
742
743 #[test]
744 fn unescape_dollar_signs_once() {
745 assert_eq!(
746 compile(
747 &[(
748 ROOT_MODULE_PATH.clone(),
749 ast::Module::new(vec![
750 ast::VariableDefinition::new("x", "$$$$").into(),
751 ast_rule("foo", &[("command", "$x")]).into(),
752 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
753 ])
754 )]
755 .into_iter()
756 .collect(),
757 &DEFAULT_DEPENDENCIES,
758 &ROOT_MODULE_PATH
759 )
760 .unwrap(),
761 create_simple_config(
762 [(
763 "bar".into(),
764 ir_explicit_build(vec!["bar".into()], Rule::new("$$", None), vec![]).into()
765 )]
766 .into_iter()
767 .collect(),
768 ["bar".into()].into_iter().collect()
769 )
770 );
771 }
772
773 #[test]
774 fn interpolate_in_variable_in_command() {
775 assert_eq!(
776 compile(
777 &[(
778 ROOT_MODULE_PATH.clone(),
779 ast::Module::new(vec![
780 ast_rule("foo", &[("command", "$in")]).into(),
781 ast_explicit_build(vec!["bar".into()], "foo", vec!["baz".into()], vec![])
782 .into(),
783 ])
784 )]
785 .into_iter()
786 .collect(),
787 &DEFAULT_DEPENDENCIES,
788 &ROOT_MODULE_PATH
789 )
790 .unwrap(),
791 create_simple_config(
792 [(
793 "bar".into(),
794 ir_explicit_build(
795 vec!["bar".into()],
796 Rule::new("baz", None),
797 vec!["baz".into()]
798 )
799 .into()
800 )]
801 .into_iter()
802 .collect(),
803 ["bar".into()].into_iter().collect()
804 )
805 );
806 }
807
808 #[test]
809 fn interpolate_in_variable_with_implicit_input() {
810 assert_eq!(
811 compile(
812 &[(
813 ROOT_MODULE_PATH.clone(),
814 ast::Module::new(vec![
815 ast_rule("foo", &[("command", "$in")]).into(),
816 ast::Build::new(
817 vec!["bar".into()],
818 vec![],
819 "foo",
820 vec!["baz".into()],
821 vec!["blah".into()],
822 vec![],
823 vec![]
824 )
825 .into(),
826 ])
827 )]
828 .into_iter()
829 .collect(),
830 &DEFAULT_DEPENDENCIES,
831 &ROOT_MODULE_PATH
832 )
833 .unwrap(),
834 create_simple_config(
835 [(
836 "bar".into(),
837 ir_explicit_build(
838 vec!["bar".into()],
839 Rule::new("baz", None),
840 vec!["baz".into(), "blah".into()]
841 )
842 .into()
843 )]
844 .into_iter()
845 .collect(),
846 ["bar".into()].into_iter().collect()
847 )
848 );
849 }
850
851 #[test]
852 fn interpolate_in_newline_variable_in_command() {
853 assert_eq!(
854 compile(
855 &[(
856 ROOT_MODULE_PATH.clone(),
857 ast::Module::new(vec![
858 ast_rule("foo", &[("command", "$in_newline")]).into(),
859 ast_explicit_build(
860 vec!["bar".into()],
861 "foo",
862 vec!["baz".into(), "qux".into()],
863 vec![]
864 )
865 .into(),
866 ])
867 )]
868 .into_iter()
869 .collect(),
870 &DEFAULT_DEPENDENCIES,
871 &ROOT_MODULE_PATH
872 )
873 .unwrap(),
874 create_simple_config(
875 [(
876 "bar".into(),
877 ir_explicit_build(
878 vec!["bar".into()],
879 Rule::new("baz\nqux", None),
880 vec!["baz".into(), "qux".into()]
881 )
882 .into()
883 )]
884 .into_iter()
885 .collect(),
886 ["bar".into()].into_iter().collect()
887 )
888 );
889 }
890
891 #[test]
892 fn interpolate_in_newline_variable_with_implicit_and_order_only_inputs() {
893 assert_eq!(
894 compile(
895 &[(
896 ROOT_MODULE_PATH.clone(),
897 ast::Module::new(vec![
898 ast_rule("foo", &[("command", "$in_newline")]).into(),
899 ast::Build::new(
900 vec!["bar".into()],
901 vec![],
902 "foo",
903 vec!["baz".into(), "qux".into()],
904 vec!["blah".into()],
905 vec!["corge".into()],
906 vec![]
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 Build::new(
921 vec!["bar".into()],
922 vec![],
923 Some(Rule::new("baz\nqux", None)),
924 vec!["baz".into(), "qux".into(), "blah".into()],
925 vec!["corge".into()],
926 None
927 )
928 .into()
929 )]
930 .into_iter()
931 .collect(),
932 ["bar".into()].into_iter().collect()
933 )
934 );
935 }
936
937 #[test]
938 fn interpolate_out_variable_in_command() {
939 assert_eq!(
940 compile(
941 &[(
942 ROOT_MODULE_PATH.clone(),
943 ast::Module::new(vec![
944 ast_rule("foo", &[("command", "$out")]).into(),
945 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
946 ])
947 )]
948 .into_iter()
949 .collect(),
950 &DEFAULT_DEPENDENCIES,
951 &ROOT_MODULE_PATH
952 )
953 .unwrap(),
954 create_simple_config(
955 [(
956 "bar".into(),
957 ir_explicit_build(vec!["bar".into()], Rule::new("bar", None), vec![]).into()
958 )]
959 .into_iter()
960 .collect(),
961 ["bar".into()].into_iter().collect()
962 )
963 );
964 }
965
966 #[test]
967 fn interpolate_out_variable_with_implicit_output() {
968 let build = Arc::new(Build::new(
969 vec!["bar".into()],
970 vec!["baz".into()],
971 Rule::new("bar", None).into(),
972 vec![],
973 vec![],
974 None,
975 ));
976
977 assert_eq!(
978 compile(
979 &[(
980 ROOT_MODULE_PATH.clone(),
981 ast::Module::new(vec![
982 ast_rule("foo", &[("command", "$out")]).into(),
983 ast::Build::new(
984 vec!["bar".into()],
985 vec!["baz".into()],
986 "foo",
987 vec![],
988 vec![],
989 vec![],
990 vec![]
991 )
992 .into(),
993 ])
994 )]
995 .into_iter()
996 .collect(),
997 &DEFAULT_DEPENDENCIES,
998 &ROOT_MODULE_PATH
999 )
1000 .unwrap(),
1001 create_simple_config(
1002 [("baz".into(), build.clone()), ("bar".into(), build)]
1003 .into_iter()
1004 .collect(),
1005 ["baz".into(), "bar".into()].into_iter().collect()
1006 )
1007 );
1008 }
1009
1010 #[test]
1011 fn compile_order_only_inputs() {
1012 assert_eq!(
1013 compile(
1014 &[(
1015 ROOT_MODULE_PATH.clone(),
1016 ast::Module::new(vec![
1017 ast_rule("foo", &[("command", "$in")]).into(),
1018 ast::Build::new(
1019 vec!["bar".into()],
1020 vec![],
1021 "foo",
1022 vec![],
1023 vec![],
1024 vec!["baz".into()],
1025 vec![]
1026 )
1027 .into(),
1028 ])
1029 )]
1030 .into_iter()
1031 .collect(),
1032 &DEFAULT_DEPENDENCIES,
1033 &ROOT_MODULE_PATH
1034 )
1035 .unwrap(),
1036 create_simple_config(
1037 [(
1038 "bar".into(),
1039 Build::new(
1040 vec!["bar".into()],
1041 vec![],
1042 Some(Rule::new("", None)),
1043 vec![],
1044 vec!["baz".into()],
1045 None
1046 )
1047 .into()
1048 )]
1049 .into_iter()
1050 .collect(),
1051 ["bar".into()].into_iter().collect()
1052 )
1053 );
1054 }
1055
1056 #[test]
1057 fn interpolate_variables_in_paths() {
1058 let build = Arc::new(Build::new(
1059 vec!["foo/output".into()],
1060 vec!["foo/implicit_output".into()],
1061 Rule::new("foo/input foo/output", None).into(),
1062 vec!["foo/input".into(), "foo/implicit_input".into()],
1063 vec!["foo/order_only_input".into()],
1064 None,
1065 ));
1066
1067 assert_eq!(
1068 compile(
1069 &[(
1070 ROOT_MODULE_PATH.clone(),
1071 ast::Module::new(vec![
1072 ast::VariableDefinition::new("x", "foo").into(),
1073 ast_rule("bar", &[("command", "$in $out")]).into(),
1074 ast::Build::new(
1075 vec!["$x/output".into()],
1076 vec!["${x}/implicit_output".into()],
1077 "bar",
1078 vec!["$x/input".into()],
1079 vec!["${x}/implicit_input".into()],
1080 vec!["$x/order_only_input".into()],
1081 vec![]
1082 )
1083 .into(),
1084 ])
1085 )]
1086 .into_iter()
1087 .collect(),
1088 &DEFAULT_DEPENDENCIES,
1089 &ROOT_MODULE_PATH
1090 )
1091 .unwrap(),
1092 create_simple_config(
1093 [
1094 ("foo/output".into(), build.clone()),
1095 ("foo/implicit_output".into(), build)
1096 ]
1097 .into_iter()
1098 .collect(),
1099 ["foo/output".into(), "foo/implicit_output".into()]
1100 .into_iter()
1101 .collect()
1102 )
1103 );
1104 }
1105
1106 #[test]
1107 fn interpolate_build_local_variable_in_path() {
1108 assert_eq!(
1109 compile(
1110 &[(
1111 ROOT_MODULE_PATH.clone(),
1112 ast::Module::new(vec![
1113 ast_rule("foo", &[("command", "$out")]).into(),
1114 ast_explicit_build(
1115 vec!["$x/bar".into()],
1116 "foo",
1117 vec![],
1118 vec![ast::VariableDefinition::new("x", "baz")]
1119 )
1120 .into(),
1121 ])
1122 )]
1123 .into_iter()
1124 .collect(),
1125 &DEFAULT_DEPENDENCIES,
1126 &ROOT_MODULE_PATH
1127 )
1128 .unwrap(),
1129 create_simple_config(
1130 [(
1131 "baz/bar".into(),
1132 ir_explicit_build(vec!["baz/bar".into()], Rule::new("baz/bar", None), vec![])
1133 .into()
1134 )]
1135 .into_iter()
1136 .collect(),
1137 ["baz/bar".into()].into_iter().collect()
1138 )
1139 );
1140 }
1141
1142 #[test]
1143 fn unescape_dollar_sign_in_path() {
1144 assert_eq!(
1145 compile(
1146 &[(
1147 ROOT_MODULE_PATH.clone(),
1148 ast::Module::new(vec![
1149 ast_rule("foo", &[("command", "")]).into(),
1150 ast_explicit_build(vec!["bar$$baz".into()], "foo", vec![], vec![]).into(),
1151 ])
1152 )]
1153 .into_iter()
1154 .collect(),
1155 &DEFAULT_DEPENDENCIES,
1156 &ROOT_MODULE_PATH
1157 )
1158 .unwrap(),
1159 create_simple_config(
1160 [(
1161 "bar$baz".into(),
1162 ir_explicit_build(vec!["bar$baz".into()], Rule::new("", None), vec![]).into()
1163 )]
1164 .into_iter()
1165 .collect(),
1166 ["bar$baz".into()].into_iter().collect()
1167 )
1168 );
1169 }
1170
1171 #[test]
1172 fn do_not_interpolate_in_and_out_variables_in_paths() {
1173 assert_eq!(
1174 compile(
1175 &[(
1176 ROOT_MODULE_PATH.clone(),
1177 ast::Module::new(vec![
1178 ast_rule("foo", &[("command", "$in $out")]).into(),
1179 ast_explicit_build(
1180 vec!["bar$out".into()],
1181 "foo",
1182 vec!["baz$in".into()],
1183 vec![]
1184 )
1185 .into(),
1186 ])
1187 )]
1188 .into_iter()
1189 .collect(),
1190 &DEFAULT_DEPENDENCIES,
1191 &ROOT_MODULE_PATH
1192 )
1193 .unwrap(),
1194 create_simple_config(
1195 [(
1196 "bar".into(),
1197 ir_explicit_build(
1198 vec!["bar".into()],
1199 Rule::new("baz bar", None),
1200 vec!["baz".into()]
1201 )
1202 .into()
1203 )]
1204 .into_iter()
1205 .collect(),
1206 ["bar".into()].into_iter().collect()
1207 )
1208 );
1209 }
1210
1211 #[test]
1212 fn interpolate_variable_in_default_output() {
1213 assert_eq!(
1214 compile(
1215 &[(
1216 ROOT_MODULE_PATH.clone(),
1217 ast::Module::new(vec![
1218 ast::VariableDefinition::new("x", "foo").into(),
1219 ast_rule("bar", &[("command", "")]).into(),
1220 ast_explicit_build(vec!["foo/baz".into()], "bar", vec![], vec![]).into(),
1221 ast_explicit_build(vec!["qux".into()], "bar", vec![], vec![]).into(),
1222 ast::DefaultOutput::new(vec!["$x/baz".into()]).into(),
1223 ])
1224 )]
1225 .into_iter()
1226 .collect(),
1227 &DEFAULT_DEPENDENCIES,
1228 &ROOT_MODULE_PATH
1229 )
1230 .unwrap(),
1231 create_simple_config(
1232 [
1233 (
1234 "foo/baz".into(),
1235 ir_explicit_build(vec!["foo/baz".into()], Rule::new("", None), vec![])
1236 .into()
1237 ),
1238 (
1239 "qux".into(),
1240 ir_explicit_build(vec!["qux".into()], Rule::new("", None), vec![]).into()
1241 )
1242 ]
1243 .into_iter()
1244 .collect(),
1245 ["foo/baz".into()].into_iter().collect()
1246 )
1247 );
1248 }
1249
1250 #[test]
1251 fn generate_build_ids() {
1252 assert_eq!(
1253 compile(
1254 &[(
1255 ROOT_MODULE_PATH.clone(),
1256 ast::Module::new(vec![
1257 ast_rule("foo", &[("command", "")]).into(),
1258 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1259 ast_explicit_build(vec!["baz".into()], "foo", vec![], vec![]).into()
1260 ])
1261 )]
1262 .into_iter()
1263 .collect(),
1264 &DEFAULT_DEPENDENCIES,
1265 &ROOT_MODULE_PATH
1266 )
1267 .unwrap(),
1268 create_simple_config(
1269 [
1270 (
1271 "bar".into(),
1272 ir_explicit_build(vec!["bar".into()], Rule::new("", None), vec![]).into()
1273 ),
1274 (
1275 "baz".into(),
1276 ir_explicit_build(vec!["baz".into()], Rule::new("", None), vec![]).into()
1277 )
1278 ]
1279 .into_iter()
1280 .collect(),
1281 ["bar".into(), "baz".into()].into_iter().collect()
1282 )
1283 );
1284 }
1285
1286 #[test]
1287 fn interpolate_build_local_variable() {
1288 assert_eq!(
1289 compile(
1290 &[(
1291 ROOT_MODULE_PATH.clone(),
1292 ast::Module::new(vec![
1293 ast_rule("foo", &[("command", "$x")]).into(),
1294 ast_explicit_build(
1295 vec!["bar".into()],
1296 "foo",
1297 vec![],
1298 vec![ast::VariableDefinition::new("x", "42")]
1299 )
1300 .into(),
1301 ])
1302 )]
1303 .into_iter()
1304 .collect(),
1305 &DEFAULT_DEPENDENCIES,
1306 &ROOT_MODULE_PATH
1307 )
1308 .unwrap(),
1309 create_simple_config(
1310 [(
1311 "bar".into(),
1312 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
1313 )]
1314 .into_iter()
1315 .collect(),
1316 ["bar".into()].into_iter().collect()
1317 )
1318 );
1319 }
1320
1321 #[test]
1322 fn interpolate_rule_variable_in_command() {
1323 assert_eq!(
1324 compile(
1325 &[(
1326 ROOT_MODULE_PATH.clone(),
1327 ast::Module::new(vec![
1328 ast_rule(
1329 "foo",
1330 &[("command", "$description"), ("description", "bar")]
1331 )
1332 .into(),
1333 ast_explicit_build(vec!["baz".into()], "foo", vec![], vec![]).into(),
1334 ])
1335 )]
1336 .into_iter()
1337 .collect(),
1338 &DEFAULT_DEPENDENCIES,
1339 &ROOT_MODULE_PATH
1340 )
1341 .unwrap(),
1342 create_simple_config(
1343 [(
1344 "baz".into(),
1345 ir_explicit_build(
1346 vec!["baz".into()],
1347 Rule::new("bar", Some("bar".into())),
1348 vec![]
1349 )
1350 .into()
1351 )]
1352 .into_iter()
1353 .collect(),
1354 ["baz".into()].into_iter().collect()
1355 )
1356 );
1357 }
1358
1359 #[test]
1360 fn compile_last_command_in_rule() {
1361 assert_eq!(
1362 compile(
1363 &[(
1364 ROOT_MODULE_PATH.clone(),
1365 ast::Module::new(vec![
1366 ast_rule("foo", &[("command", "first"), ("command", "second")]).into(),
1367 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1368 ])
1369 )]
1370 .into_iter()
1371 .collect(),
1372 &DEFAULT_DEPENDENCIES,
1373 &ROOT_MODULE_PATH
1374 )
1375 .unwrap(),
1376 create_simple_config(
1377 [(
1378 "bar".into(),
1379 ir_explicit_build(vec!["bar".into()], Rule::new("second", None), vec![]).into()
1380 )]
1381 .into_iter()
1382 .collect(),
1383 ["bar".into()].into_iter().collect()
1384 )
1385 );
1386 }
1387
1388 #[test]
1389 fn compile_build_level_command_shadowing_rule_command() {
1390 assert_eq!(
1391 compile(
1392 &[(
1393 ROOT_MODULE_PATH.clone(),
1394 ast::Module::new(vec![
1395 ast_rule("foo", &[("command", "rule")]).into(),
1396 ast_explicit_build(
1397 vec!["bar".into()],
1398 "foo",
1399 vec![],
1400 vec![ast::VariableDefinition::new("command", "build")]
1401 )
1402 .into(),
1403 ])
1404 )]
1405 .into_iter()
1406 .collect(),
1407 &DEFAULT_DEPENDENCIES,
1408 &ROOT_MODULE_PATH
1409 )
1410 .unwrap(),
1411 create_simple_config(
1412 [(
1413 "bar".into(),
1414 ir_explicit_build(vec!["bar".into()], Rule::new("build", None), vec![]).into()
1415 )]
1416 .into_iter()
1417 .collect(),
1418 ["bar".into()].into_iter().collect()
1419 )
1420 );
1421 }
1422
1423 #[test]
1424 fn compile_global_description() {
1425 assert_eq!(
1426 compile(
1427 &[(
1428 ROOT_MODULE_PATH.clone(),
1429 ast::Module::new(vec![
1430 ast::VariableDefinition::new("description", "global").into(),
1431 ast_rule("foo", &[("command", "")]).into(),
1432 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1433 ])
1434 )]
1435 .into_iter()
1436 .collect(),
1437 &DEFAULT_DEPENDENCIES,
1438 &ROOT_MODULE_PATH
1439 )
1440 .unwrap(),
1441 create_simple_config(
1442 [(
1443 "bar".into(),
1444 ir_explicit_build(
1445 vec!["bar".into()],
1446 Rule::new("", Some("global".into())),
1447 vec![]
1448 )
1449 .into()
1450 )]
1451 .into_iter()
1452 .collect(),
1453 ["bar".into()].into_iter().collect()
1454 )
1455 );
1456 }
1457
1458 #[test]
1459 fn compile_source_map() {
1460 assert_eq!(
1461 compile(
1462 &[(
1463 ROOT_MODULE_PATH.clone(),
1464 ast::Module::new(vec![
1465 ast_rule("foo", &[("command", "foo")]).into(),
1466 ast_explicit_build(
1467 vec!["bar".into()],
1468 "foo",
1469 vec![],
1470 vec![ast::VariableDefinition::new(
1471 SOURCE_VARIABLE_NAME,
1472 "oh-my-src"
1473 )]
1474 )
1475 .into(),
1476 ])
1477 )]
1478 .into_iter()
1479 .collect(),
1480 &DEFAULT_DEPENDENCIES,
1481 &ROOT_MODULE_PATH
1482 )
1483 .unwrap(),
1484 Config::new(
1485 [(
1486 "bar".into(),
1487 ir_explicit_build(vec!["bar".into()], Rule::new("foo", None), vec![]).into()
1488 )]
1489 .into_iter()
1490 .collect(),
1491 ["bar".into()].into_iter().collect(),
1492 [("bar".into(), "oh-my-src".into())].into_iter().collect(),
1493 Default::default(),
1494 None,
1495 )
1496 );
1497 }
1498
1499 #[test]
1500 fn compile_phony_rule() {
1501 assert_eq!(
1502 compile(
1503 &[(
1504 ROOT_MODULE_PATH.clone(),
1505 ast::Module::new(vec![
1506 ast_explicit_build(vec!["foo".into()], "phony", vec!["bar".into()], vec![])
1507 .into(),
1508 ])
1509 )]
1510 .into_iter()
1511 .collect(),
1512 &DEFAULT_DEPENDENCIES,
1513 &ROOT_MODULE_PATH
1514 )
1515 .unwrap(),
1516 create_simple_config(
1517 [(
1518 "foo".into(),
1519 Build::new(
1520 vec!["foo".into()],
1521 vec![],
1522 None,
1523 vec!["bar".into()],
1524 vec![],
1525 None
1526 )
1527 .into()
1528 )]
1529 .into_iter()
1530 .collect(),
1531 ["foo".into()].into_iter().collect()
1532 )
1533 );
1534 }
1535
1536 #[test]
1537 fn compile_build_directory() {
1538 assert_eq!(
1539 compile(
1540 &[(
1541 ROOT_MODULE_PATH.clone(),
1542 ast::Module::new(vec![ast::VariableDefinition::new("builddir", "foo").into()])
1543 )]
1544 .into_iter()
1545 .collect(),
1546 &DEFAULT_DEPENDENCIES,
1547 &ROOT_MODULE_PATH
1548 )
1549 .unwrap(),
1550 Config::new(
1551 Default::default(),
1552 Default::default(),
1553 Default::default(),
1554 Default::default(),
1555 Some("foo".into())
1556 )
1557 );
1558 }
1559
1560 #[test]
1561 fn compile_build_directory_with_variable() {
1562 assert_eq!(
1563 compile(
1564 &[(
1565 ROOT_MODULE_PATH.clone(),
1566 ast::Module::new(vec![
1567 ast::VariableDefinition::new("x", "foo").into(),
1568 ast::VariableDefinition::new("builddir", "$x/bar").into(),
1569 ])
1570 )]
1571 .into_iter()
1572 .collect(),
1573 &DEFAULT_DEPENDENCIES,
1574 &ROOT_MODULE_PATH
1575 )
1576 .unwrap(),
1577 Config::new(
1578 Default::default(),
1579 Default::default(),
1580 Default::default(),
1581 Default::default(),
1582 Some("foo/bar".into())
1583 )
1584 );
1585 }
1586
1587 #[test]
1588 fn compile_dynamic_module_variable() {
1589 assert_eq!(
1590 compile(
1591 &[(
1592 ROOT_MODULE_PATH.clone(),
1593 ast::Module::new(vec![
1594 ast_explicit_build(
1595 vec!["foo".into()],
1596 "phony",
1597 vec![],
1598 vec![ast::VariableDefinition::new("dyndep", "bar")]
1599 )
1600 .into()
1601 ])
1602 )]
1603 .into_iter()
1604 .collect(),
1605 &DEFAULT_DEPENDENCIES,
1606 &ROOT_MODULE_PATH
1607 )
1608 .unwrap(),
1609 create_simple_config(
1610 [(
1611 "foo".into(),
1612 Build::new(
1613 vec!["foo".into()],
1614 vec![],
1615 None,
1616 vec![],
1617 vec![],
1618 Some("bar".into())
1619 )
1620 .into()
1621 )]
1622 .into_iter()
1623 .collect(),
1624 ["foo".into()].into_iter().collect()
1625 )
1626 );
1627 }
1628
1629 #[test]
1630 fn compile_rule_level_dynamic_module_variable() {
1631 assert_eq!(
1632 compile(
1633 &[(
1634 ROOT_MODULE_PATH.clone(),
1635 ast::Module::new(vec![
1636 ast_rule("foo", &[("command", ""), ("dyndep", "$out.dd")]).into(),
1637 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1638 ])
1639 )]
1640 .into_iter()
1641 .collect(),
1642 &DEFAULT_DEPENDENCIES,
1643 &ROOT_MODULE_PATH
1644 )
1645 .unwrap(),
1646 create_simple_config(
1647 [(
1648 "bar".into(),
1649 Build::new(
1650 vec!["bar".into()],
1651 vec![],
1652 Some(Rule::new("", None)),
1653 vec![],
1654 vec![],
1655 Some("bar.dd".into())
1656 )
1657 .into()
1658 )]
1659 .into_iter()
1660 .collect(),
1661 ["bar".into()].into_iter().collect()
1662 )
1663 );
1664 }
1665
1666 #[test]
1667 fn compile_build_level_dynamic_module_variable_shadowing_rule_level_one() {
1668 assert_eq!(
1669 compile(
1670 &[(
1671 ROOT_MODULE_PATH.clone(),
1672 ast::Module::new(vec![
1673 ast_rule("foo", &[("command", ""), ("dyndep", "rule.dd")]).into(),
1674 ast_explicit_build(
1675 vec!["bar".into()],
1676 "foo",
1677 vec![],
1678 vec![ast::VariableDefinition::new("dyndep", "build.dd")]
1679 )
1680 .into(),
1681 ])
1682 )]
1683 .into_iter()
1684 .collect(),
1685 &DEFAULT_DEPENDENCIES,
1686 &ROOT_MODULE_PATH
1687 )
1688 .unwrap(),
1689 create_simple_config(
1690 [(
1691 "bar".into(),
1692 Build::new(
1693 vec!["bar".into()],
1694 vec![],
1695 Some(Rule::new("", None)),
1696 vec![],
1697 vec![],
1698 Some("build.dd".into())
1699 )
1700 .into()
1701 )]
1702 .into_iter()
1703 .collect(),
1704 ["bar".into()].into_iter().collect()
1705 )
1706 );
1707 }
1708
1709 #[test]
1710 fn compile_make_header_dependency() {
1711 assert_eq!(
1712 compile(
1713 &[(
1714 ROOT_MODULE_PATH.clone(),
1715 ast::Module::new(vec![
1716 ast_rule("foo", &[("command", "bar"), ("depfile", "foo.d")]).into(),
1717 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1718 ])
1719 )]
1720 .into_iter()
1721 .collect(),
1722 &DEFAULT_DEPENDENCIES,
1723 &ROOT_MODULE_PATH
1724 )
1725 .unwrap(),
1726 create_simple_config(
1727 [(
1728 "bar".into(),
1729 ir_explicit_build(
1730 vec!["bar".into()],
1731 Rule::new("bar", None).with_header_dependency(Some(
1732 HeaderDependency::Make {
1733 path: "foo.d".into()
1734 }
1735 )),
1736 vec![]
1737 )
1738 .into()
1739 )]
1740 .into_iter()
1741 .collect(),
1742 ["bar".into()].into_iter().collect()
1743 )
1744 );
1745 }
1746
1747 #[test]
1748 fn compile_gcc_header_dependency() {
1749 assert_eq!(
1750 compile(
1751 &[(
1752 ROOT_MODULE_PATH.clone(),
1753 ast::Module::new(vec![
1754 ast_rule(
1755 "foo",
1756 &[("command", "bar"), ("depfile", "foo.d"), ("deps", "gcc")]
1757 )
1758 .into(),
1759 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1760 ])
1761 )]
1762 .into_iter()
1763 .collect(),
1764 &DEFAULT_DEPENDENCIES,
1765 &ROOT_MODULE_PATH
1766 )
1767 .unwrap(),
1768 create_simple_config(
1769 [(
1770 "bar".into(),
1771 ir_explicit_build(
1772 vec!["bar".into()],
1773 Rule::new("bar", None).with_header_dependency(Some(
1774 HeaderDependency::Gcc {
1775 path: "foo.d".into()
1776 }
1777 )),
1778 vec![]
1779 )
1780 .into()
1781 )]
1782 .into_iter()
1783 .collect(),
1784 ["bar".into()].into_iter().collect()
1785 )
1786 );
1787 }
1788
1789 #[test]
1790 fn compile_make_header_dependency_with_build_level_depfile() {
1791 assert_eq!(
1792 compile(
1793 &[(
1794 ROOT_MODULE_PATH.clone(),
1795 ast::Module::new(vec![
1796 ast_rule("foo", &[("command", "bar")]).into(),
1797 ast_explicit_build(
1798 vec!["bar".into()],
1799 "foo",
1800 vec![],
1801 vec![ast::VariableDefinition::new("depfile", "foo.d")]
1802 )
1803 .into(),
1804 ])
1805 )]
1806 .into_iter()
1807 .collect(),
1808 &DEFAULT_DEPENDENCIES,
1809 &ROOT_MODULE_PATH
1810 )
1811 .unwrap(),
1812 create_simple_config(
1813 [(
1814 "bar".into(),
1815 ir_explicit_build(
1816 vec!["bar".into()],
1817 Rule::new("bar", None).with_header_dependency(Some(
1818 HeaderDependency::Make {
1819 path: "foo.d".into()
1820 }
1821 )),
1822 vec![]
1823 )
1824 .into()
1825 )]
1826 .into_iter()
1827 .collect(),
1828 ["bar".into()].into_iter().collect()
1829 )
1830 );
1831 }
1832
1833 #[test]
1834 fn compile_gcc_header_dependency_with_build_level_deps() {
1835 assert_eq!(
1836 compile(
1837 &[(
1838 ROOT_MODULE_PATH.clone(),
1839 ast::Module::new(vec![
1840 ast_rule("foo", &[("command", "bar"), ("depfile", "$out.d")]).into(),
1841 ast_explicit_build(
1842 vec!["bar".into()],
1843 "foo",
1844 vec![],
1845 vec![ast::VariableDefinition::new("deps", "gcc")]
1846 )
1847 .into(),
1848 ])
1849 )]
1850 .into_iter()
1851 .collect(),
1852 &DEFAULT_DEPENDENCIES,
1853 &ROOT_MODULE_PATH
1854 )
1855 .unwrap(),
1856 create_simple_config(
1857 [(
1858 "bar".into(),
1859 ir_explicit_build(
1860 vec!["bar".into()],
1861 Rule::new("bar", None).with_header_dependency(Some(
1862 HeaderDependency::Gcc {
1863 path: "bar.d".into()
1864 }
1865 )),
1866 vec![]
1867 )
1868 .into()
1869 )]
1870 .into_iter()
1871 .collect(),
1872 ["bar".into()].into_iter().collect()
1873 )
1874 );
1875 }
1876
1877 #[test]
1878 fn compile_make_header_dependency_with_build_level_empty_deps_shadowing_rule_deps() {
1879 assert_eq!(
1880 compile(
1881 &[(
1882 ROOT_MODULE_PATH.clone(),
1883 ast::Module::new(vec![
1884 ast_rule(
1885 "foo",
1886 &[("command", "bar"), ("depfile", "foo.d"), ("deps", "gcc")]
1887 )
1888 .into(),
1889 ast_explicit_build(
1890 vec!["bar".into()],
1891 "foo",
1892 vec![],
1893 vec![ast::VariableDefinition::new("deps", "")]
1894 )
1895 .into(),
1896 ])
1897 )]
1898 .into_iter()
1899 .collect(),
1900 &DEFAULT_DEPENDENCIES,
1901 &ROOT_MODULE_PATH
1902 )
1903 .unwrap(),
1904 create_simple_config(
1905 [(
1906 "bar".into(),
1907 ir_explicit_build(
1908 vec!["bar".into()],
1909 Rule::new("bar", None).with_header_dependency(Some(
1910 HeaderDependency::Make {
1911 path: "foo.d".into()
1912 }
1913 )),
1914 vec![]
1915 )
1916 .into()
1917 )]
1918 .into_iter()
1919 .collect(),
1920 ["bar".into()].into_iter().collect()
1921 )
1922 );
1923 }
1924
1925 #[test]
1926 fn fail_to_compile_gcc_header_dependency_without_depfile() {
1927 assert_eq!(
1928 compile(
1929 &[(
1930 ROOT_MODULE_PATH.clone(),
1931 ast::Module::new(vec![
1932 ast_rule("foo", &[("command", "bar"), ("deps", "gcc")]).into(),
1933 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1934 ])
1935 )]
1936 .into_iter()
1937 .collect(),
1938 &DEFAULT_DEPENDENCIES,
1939 &ROOT_MODULE_PATH
1940 ),
1941 Err(CompileError::MissingDepfile("foo".into()))
1942 );
1943 }
1944
1945 #[test]
1946 fn fail_to_compile_unknown_header_dependency() {
1947 assert_eq!(
1948 compile(
1949 &[(
1950 ROOT_MODULE_PATH.clone(),
1951 ast::Module::new(vec![
1952 ast_rule("foo", &[("command", "bar"), ("deps", "clang")]).into(),
1953 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1954 ])
1955 )]
1956 .into_iter()
1957 .collect(),
1958 &DEFAULT_DEPENDENCIES,
1959 &ROOT_MODULE_PATH
1960 ),
1961 Err(CompileError::InvalidDependencyStyle("clang".into()))
1962 );
1963 }
1964
1965 #[test]
1966 fn compile_msvc_header_dependency_with_default_prefix() {
1967 assert_eq!(
1968 compile(
1969 &[(
1970 ROOT_MODULE_PATH.clone(),
1971 ast::Module::new(vec![
1972 ast_rule("foo", &[("command", "bar"), ("deps", "msvc")]).into(),
1973 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
1974 ])
1975 )]
1976 .into_iter()
1977 .collect(),
1978 &DEFAULT_DEPENDENCIES,
1979 &ROOT_MODULE_PATH
1980 )
1981 .unwrap(),
1982 create_simple_config(
1983 [(
1984 "bar".into(),
1985 ir_explicit_build(
1986 vec!["bar".into()],
1987 Rule::new("bar", None).with_header_dependency(Some(
1988 HeaderDependency::Msvc {
1989 prefix: "Note: including file: ".into()
1990 }
1991 )),
1992 vec![]
1993 )
1994 .into()
1995 )]
1996 .into_iter()
1997 .collect(),
1998 ["bar".into()].into_iter().collect()
1999 )
2000 );
2001 }
2002
2003 #[test]
2004 fn compile_msvc_header_dependency_ignoring_depfile() {
2005 assert_eq!(
2006 compile(
2007 &[(
2008 ROOT_MODULE_PATH.clone(),
2009 ast::Module::new(vec![
2010 ast_rule(
2011 "foo",
2012 &[("command", "bar"), ("depfile", "foo.d"), ("deps", "msvc")]
2013 )
2014 .into(),
2015 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
2016 ])
2017 )]
2018 .into_iter()
2019 .collect(),
2020 &DEFAULT_DEPENDENCIES,
2021 &ROOT_MODULE_PATH
2022 )
2023 .unwrap(),
2024 create_simple_config(
2025 [(
2026 "bar".into(),
2027 ir_explicit_build(
2028 vec!["bar".into()],
2029 Rule::new("bar", None).with_header_dependency(Some(
2030 HeaderDependency::Msvc {
2031 prefix: "Note: including file: ".into()
2032 }
2033 )),
2034 vec![]
2035 )
2036 .into()
2037 )]
2038 .into_iter()
2039 .collect(),
2040 ["bar".into()].into_iter().collect()
2041 )
2042 );
2043 }
2044
2045 #[test]
2046 fn compile_msvc_header_dependency_with_empty_prefix() {
2047 assert_eq!(
2048 compile(
2049 &[(
2050 ROOT_MODULE_PATH.clone(),
2051 ast::Module::new(vec![
2052 ast_rule(
2053 "foo",
2054 &[
2055 ("command", "bar"),
2056 ("deps", "msvc"),
2057 ("msvc_deps_prefix", "")
2058 ]
2059 )
2060 .into(),
2061 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
2062 ])
2063 )]
2064 .into_iter()
2065 .collect(),
2066 &DEFAULT_DEPENDENCIES,
2067 &ROOT_MODULE_PATH
2068 )
2069 .unwrap(),
2070 create_simple_config(
2071 [(
2072 "bar".into(),
2073 ir_explicit_build(
2074 vec!["bar".into()],
2075 Rule::new("bar", None).with_header_dependency(Some(
2076 HeaderDependency::Msvc {
2077 prefix: "Note: including file: ".into()
2078 }
2079 )),
2080 vec![]
2081 )
2082 .into()
2083 )]
2084 .into_iter()
2085 .collect(),
2086 ["bar".into()].into_iter().collect()
2087 )
2088 );
2089 }
2090
2091 #[test]
2092 fn compile_msvc_header_dependency_with_custom_prefix() {
2093 assert_eq!(
2094 compile(
2095 &[(
2096 ROOT_MODULE_PATH.clone(),
2097 ast::Module::new(vec![
2098 ast_rule(
2099 "foo",
2100 &[
2101 ("command", "bar"),
2102 ("deps", "msvc"),
2103 ("msvc_deps_prefix", "Hinweis: Einlesen der Datei ")
2104 ]
2105 )
2106 .into(),
2107 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
2108 ])
2109 )]
2110 .into_iter()
2111 .collect(),
2112 &DEFAULT_DEPENDENCIES,
2113 &ROOT_MODULE_PATH
2114 )
2115 .unwrap(),
2116 create_simple_config(
2117 [(
2118 "bar".into(),
2119 ir_explicit_build(
2120 vec!["bar".into()],
2121 Rule::new("bar", None).with_header_dependency(Some(
2122 HeaderDependency::Msvc {
2123 prefix: "Hinweis: Einlesen der Datei ".into()
2124 }
2125 )),
2126 vec![]
2127 )
2128 .into()
2129 )]
2130 .into_iter()
2131 .collect(),
2132 ["bar".into()].into_iter().collect()
2133 )
2134 );
2135 }
2136
2137 #[test]
2138 fn compile_msvc_header_dependency_with_global_prefix() {
2139 assert_eq!(
2140 compile(
2141 &[(
2142 ROOT_MODULE_PATH.clone(),
2143 ast::Module::new(vec![
2144 ast::VariableDefinition::new(
2145 "msvc_deps_prefix",
2146 "Hinweis: Einlesen der Datei "
2147 )
2148 .into(),
2149 ast_rule("foo", &[("command", "bar"), ("deps", "msvc")]).into(),
2150 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
2151 ])
2152 )]
2153 .into_iter()
2154 .collect(),
2155 &DEFAULT_DEPENDENCIES,
2156 &ROOT_MODULE_PATH
2157 )
2158 .unwrap(),
2159 create_simple_config(
2160 [(
2161 "bar".into(),
2162 ir_explicit_build(
2163 vec!["bar".into()],
2164 Rule::new("bar", None).with_header_dependency(Some(
2165 HeaderDependency::Msvc {
2166 prefix: "Hinweis: Einlesen der Datei ".into()
2167 }
2168 )),
2169 vec![]
2170 )
2171 .into()
2172 )]
2173 .into_iter()
2174 .collect(),
2175 ["bar".into()].into_iter().collect()
2176 )
2177 );
2178 }
2179
2180 #[test]
2181 fn compile_msvc_header_dependency_with_rule_prefix_shadowing_global_prefix() {
2182 assert_eq!(
2183 compile(
2184 &[(
2185 ROOT_MODULE_PATH.clone(),
2186 ast::Module::new(vec![
2187 ast::VariableDefinition::new("msvc_deps_prefix", "global prefix: ").into(),
2188 ast_rule(
2189 "foo",
2190 &[
2191 ("command", "bar"),
2192 ("deps", "msvc"),
2193 ("msvc_deps_prefix", "rule prefix: ")
2194 ]
2195 )
2196 .into(),
2197 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![]).into(),
2198 ])
2199 )]
2200 .into_iter()
2201 .collect(),
2202 &DEFAULT_DEPENDENCIES,
2203 &ROOT_MODULE_PATH
2204 )
2205 .unwrap(),
2206 create_simple_config(
2207 [(
2208 "bar".into(),
2209 ir_explicit_build(
2210 vec!["bar".into()],
2211 Rule::new("bar", None).with_header_dependency(Some(
2212 HeaderDependency::Msvc {
2213 prefix: "rule prefix: ".into()
2214 }
2215 )),
2216 vec![]
2217 )
2218 .into()
2219 )]
2220 .into_iter()
2221 .collect(),
2222 ["bar".into()].into_iter().collect()
2223 )
2224 );
2225 }
2226
2227 #[test]
2228 fn compile_msvc_header_dependency_with_build_level_prefix_shadowing_rule_prefix() {
2229 assert_eq!(
2230 compile(
2231 &[(
2232 ROOT_MODULE_PATH.clone(),
2233 ast::Module::new(vec![
2234 ast_rule(
2235 "foo",
2236 &[
2237 ("command", "bar"),
2238 ("deps", "msvc"),
2239 ("msvc_deps_prefix", "rule prefix: ")
2240 ]
2241 )
2242 .into(),
2243 ast_explicit_build(
2244 vec!["bar".into()],
2245 "foo",
2246 vec![],
2247 vec![ast::VariableDefinition::new(
2248 "msvc_deps_prefix",
2249 "build prefix: "
2250 )]
2251 )
2252 .into(),
2253 ])
2254 )]
2255 .into_iter()
2256 .collect(),
2257 &DEFAULT_DEPENDENCIES,
2258 &ROOT_MODULE_PATH
2259 )
2260 .unwrap(),
2261 create_simple_config(
2262 [(
2263 "bar".into(),
2264 ir_explicit_build(
2265 vec!["bar".into()],
2266 Rule::new("bar", None).with_header_dependency(Some(
2267 HeaderDependency::Msvc {
2268 prefix: "build prefix: ".into()
2269 }
2270 )),
2271 vec![]
2272 )
2273 .into()
2274 )]
2275 .into_iter()
2276 .collect(),
2277 ["bar".into()].into_iter().collect()
2278 )
2279 );
2280 }
2281
2282 #[test]
2283 fn compile_rule_level_pool() {
2284 assert_eq!(
2285 compile_root_module(vec![
2286 ast::Pool::new("foo", "2").into(),
2287 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2288 ast_explicit_build(vec!["bar".into()], "qux", vec![], vec![]).into(),
2289 ]),
2290 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2291 );
2292 }
2293
2294 #[test]
2295 fn compile_build_level_pool() {
2296 assert_eq!(
2297 compile_root_module(vec![
2298 ast::Pool::new("foo", "2").into(),
2299 ast_rule("qux", &[("command", "baz")]).into(),
2300 ast_explicit_build(
2301 vec!["bar".into()],
2302 "qux",
2303 vec![],
2304 vec![ast::VariableDefinition::new("pool", "foo")]
2305 )
2306 .into(),
2307 ]),
2308 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2309 );
2310 }
2311
2312 #[test]
2313 fn compile_build_level_pool_shadowing_rule_level_pool() {
2314 assert_eq!(
2315 compile_root_module(vec![
2316 ast::Pool::new("foo", "2").into(),
2317 ast_rule("qux", &[("command", "baz"), ("pool", "console")]).into(),
2318 ast_explicit_build(
2319 vec!["bar".into()],
2320 "qux",
2321 vec![],
2322 vec![ast::VariableDefinition::new("pool", "foo")]
2323 )
2324 .into(),
2325 ]),
2326 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2327 );
2328 }
2329
2330 #[test]
2331 fn compile_build_level_empty_pool_shadowing_rule_level_pool() {
2332 assert_eq!(
2333 compile_root_module(vec![
2334 ast_rule("qux", &[("command", "baz"), ("pool", "console")]).into(),
2335 ast_explicit_build(
2336 vec!["bar".into()],
2337 "qux",
2338 vec![],
2339 vec![ast::VariableDefinition::new("pool", "")]
2340 )
2341 .into(),
2342 ]),
2343 Ok(create_pool_config(None, &[]))
2344 );
2345 }
2346
2347 #[test]
2348 fn compile_console_pool() {
2349 assert_eq!(
2350 compile_root_module(vec![
2351 ast_rule("qux", &[("command", "baz"), ("pool", "console")]).into(),
2352 ast_explicit_build(vec!["bar".into()], "qux", vec![], vec![]).into(),
2353 ]),
2354 Ok(create_pool_config(Some(Pool::Console), &[]))
2355 );
2356 }
2357
2358 #[test]
2359 fn compile_console_pool_in_phony_build() {
2360 assert_eq!(
2361 compile_root_module(vec![
2362 ast_explicit_build(
2363 vec!["foo".into()],
2364 "phony",
2365 vec![],
2366 vec![ast::VariableDefinition::new("pool", "console")]
2367 )
2368 .into(),
2369 ]),
2370 Ok(create_simple_config(
2371 [(
2372 "foo".into(),
2373 Build::new(vec!["foo".into()], vec![], None, vec![], vec![], None).into()
2374 )]
2375 .into_iter()
2376 .collect(),
2377 ["foo".into()].into_iter().collect()
2378 ))
2379 );
2380 }
2381
2382 #[test]
2383 fn compile_pool_with_zero_depth() {
2384 assert_eq!(
2385 compile_root_module(vec![
2386 ast::Pool::new("foo", "0").into(),
2387 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2388 ast_explicit_build(vec!["bar".into()], "qux", vec![], vec![]).into(),
2389 ]),
2390 Ok(create_pool_config(None, &[]))
2391 );
2392 }
2393
2394 #[test]
2395 fn compile_pool_depth_with_variable() {
2396 assert_eq!(
2397 compile_root_module(vec![
2398 ast::VariableDefinition::new("x", "2").into(),
2399 ast::Pool::new("foo", "$x").into(),
2400 ast::VariableDefinition::new("x", "3").into(),
2401 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2402 ast_explicit_build(vec!["bar".into()], "qux", vec![], vec![]).into(),
2403 ]),
2404 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2405 );
2406 }
2407
2408 #[test]
2409 fn compile_pool_depth_with_empty_variable() {
2410 assert_eq!(
2411 compile_root_module(vec![
2412 ast::VariableDefinition::new("x", "").into(),
2413 ast::Pool::new("foo", "$x 2").into(),
2414 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2415 ast_explicit_build(vec!["bar".into()], "qux", vec![], vec![]).into(),
2416 ]),
2417 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2418 );
2419 }
2420
2421 #[test]
2422 fn compile_pool_name_with_variable() {
2423 assert_eq!(
2424 compile_root_module(vec![
2425 ast::Pool::new("foo", "2").into(),
2426 ast_rule("qux", &[("command", "baz"), ("pool", "$x")]).into(),
2427 ast_explicit_build(
2428 vec!["bar".into()],
2429 "qux",
2430 vec![],
2431 vec![ast::VariableDefinition::new("x", "foo")]
2432 )
2433 .into(),
2434 ]),
2435 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2436 );
2437 }
2438
2439 #[test]
2440 fn do_not_interpolate_out_variable_in_pool() {
2441 assert_eq!(
2442 compile_root_module(vec![
2443 ast::Pool::new("bar", "2").into(),
2444 ast_rule("qux", &[("command", "baz"), ("pool", "$out")]).into(),
2445 ast_explicit_build(vec!["bar".into()], "qux", vec![], vec![]).into(),
2446 ]),
2447 Ok(create_pool_config(None, &[("bar", 2)]))
2448 );
2449 }
2450
2451 #[test]
2452 fn fail_to_compile_unknown_pool() {
2453 assert_eq!(
2454 compile_root_module(vec![
2455 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2456 ast_explicit_build(vec!["bar".into()], "qux", vec![], vec![]).into(),
2457 ]),
2458 Err(CompileError::PoolNotFound("foo".into()))
2459 );
2460 }
2461
2462 #[test]
2463 fn fail_to_compile_unknown_pool_in_phony_build() {
2464 assert_eq!(
2465 compile_root_module(vec![
2466 ast_explicit_build(
2467 vec!["bar".into()],
2468 "phony",
2469 vec![],
2470 vec![ast::VariableDefinition::new("pool", "foo")]
2471 )
2472 .into(),
2473 ]),
2474 Err(CompileError::PoolNotFound("foo".into()))
2475 );
2476 }
2477
2478 #[test]
2479 fn fail_to_compile_pool_declared_after_build() {
2480 assert_eq!(
2481 compile_root_module(vec![
2482 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2483 ast_explicit_build(vec!["bar".into()], "qux", vec![], vec![]).into(),
2484 ast::Pool::new("foo", "2").into(),
2485 ]),
2486 Err(CompileError::PoolNotFound("foo".into()))
2487 );
2488 }
2489
2490 #[test]
2491 fn fail_to_compile_duplicate_pool() {
2492 assert_eq!(
2493 compile_root_module(vec![
2494 ast::Pool::new("foo", "1").into(),
2495 ast::Pool::new("foo", "2").into(),
2496 ]),
2497 Err(CompileError::DuplicatePool("foo".into()))
2498 );
2499 }
2500
2501 #[test]
2502 fn fail_to_compile_duplicate_console_pool() {
2503 assert_eq!(
2504 compile_root_module(vec![ast::Pool::new("console", "foo").into()]),
2505 Err(CompileError::DuplicatePool("console".into()))
2506 );
2507 }
2508
2509 #[test]
2510 fn fail_to_compile_invalid_pool_depth() {
2511 for depth in ["", "foo", "-1", "1x", "99999999999999999999"] {
2512 assert_eq!(
2513 compile_root_module(vec![ast::Pool::new("foo", depth).into()]),
2514 Err(CompileError::InvalidPoolDepth("foo".into(), depth.into()))
2515 );
2516 }
2517 }
2518
2519 mod submodule {
2520 use super::*;
2521 use pretty_assertions::assert_eq;
2522
2523 const SUBMODULE_PATH: &str = "foo.ninja";
2524
2525 fn compile_with_submodule(
2526 statements: Vec<ast::Statement>,
2527 submodule_statements: Vec<ast::Statement>,
2528 ) -> Result<Config, CompileError> {
2529 compile(
2530 &[
2531 (ROOT_MODULE_PATH.clone(), ast::Module::new(statements)),
2532 (
2533 SUBMODULE_PATH.into(),
2534 ast::Module::new(submodule_statements),
2535 ),
2536 ]
2537 .into_iter()
2538 .collect(),
2539 &[(
2540 ROOT_MODULE_PATH.clone(),
2541 [(SUBMODULE_PATH.into(), PathBuf::from(SUBMODULE_PATH))]
2542 .into_iter()
2543 .collect(),
2544 )]
2545 .into_iter()
2546 .collect(),
2547 &ROOT_MODULE_PATH,
2548 )
2549 }
2550
2551 #[test]
2552 fn reference_variable_in_parent_module() {
2553 assert_eq!(
2554 compile(
2555 &[
2556 (
2557 ROOT_MODULE_PATH.clone(),
2558 ast::Module::new(vec![
2559 ast::VariableDefinition::new("x", "42").into(),
2560 ast::Submodule::new(SUBMODULE_PATH).into(),
2561 ])
2562 ),
2563 (
2564 SUBMODULE_PATH.into(),
2565 ast::Module::new(vec![
2566 ast_rule("foo", &[("command", "$x")]).into(),
2567 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![])
2568 .into()
2569 ])
2570 )
2571 ]
2572 .into_iter()
2573 .collect(),
2574 &[(
2575 ROOT_MODULE_PATH.clone(),
2576 [(SUBMODULE_PATH.into(), PathBuf::from(SUBMODULE_PATH))]
2577 .into_iter()
2578 .collect()
2579 )]
2580 .into_iter()
2581 .collect(),
2582 &ROOT_MODULE_PATH
2583 )
2584 .unwrap(),
2585 create_simple_config(
2586 [(
2587 "bar".into(),
2588 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
2589 )]
2590 .into_iter()
2591 .collect(),
2592 ["bar".into()].into_iter().collect()
2593 )
2594 );
2595 }
2596
2597 #[test]
2598 fn reference_rule_in_parent_module() {
2599 assert_eq!(
2600 compile(
2601 &[
2602 (
2603 ROOT_MODULE_PATH.clone(),
2604 ast::Module::new(vec![
2605 ast::VariableDefinition::new("x", "42").into(),
2606 ast_rule("foo", &[("command", "$x")]).into(),
2607 ast::Submodule::new(SUBMODULE_PATH).into(),
2608 ])
2609 ),
2610 (
2611 SUBMODULE_PATH.into(),
2612 ast::Module::new(vec![
2613 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![])
2614 .into()
2615 ])
2616 )
2617 ]
2618 .into_iter()
2619 .collect(),
2620 &[(
2621 ROOT_MODULE_PATH.clone(),
2622 [(SUBMODULE_PATH.into(), PathBuf::from(SUBMODULE_PATH))]
2623 .into_iter()
2624 .collect()
2625 )]
2626 .into_iter()
2627 .collect(),
2628 &ROOT_MODULE_PATH
2629 )
2630 .unwrap(),
2631 create_simple_config(
2632 [(
2633 "bar".into(),
2634 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
2635 )]
2636 .into_iter()
2637 .collect(),
2638 ["bar".into()].into_iter().collect()
2639 )
2640 );
2641 }
2642
2643 #[test]
2644 fn do_not_overwrite_variable_in_parent_module() {
2645 assert_eq!(
2646 compile(
2647 &[
2648 (
2649 ROOT_MODULE_PATH.clone(),
2650 ast::Module::new(vec![
2651 ast::VariableDefinition::new("x", "42").into(),
2652 ast_rule("foo", &[("command", "$x")]).into(),
2653 ast::Submodule::new(SUBMODULE_PATH).into(),
2654 ast_explicit_build(vec!["bar".into()], "foo", vec![], vec![])
2655 .into(),
2656 ])
2657 ),
2658 (
2659 SUBMODULE_PATH.into(),
2660 ast::Module::new(vec![ast::VariableDefinition::new("x", "13").into(),])
2661 )
2662 ]
2663 .into_iter()
2664 .collect(),
2665 &[(
2666 ROOT_MODULE_PATH.clone(),
2667 [(SUBMODULE_PATH.into(), PathBuf::from(SUBMODULE_PATH))]
2668 .into_iter()
2669 .collect()
2670 )]
2671 .into_iter()
2672 .collect(),
2673 &ROOT_MODULE_PATH
2674 )
2675 .unwrap(),
2676 create_simple_config(
2677 [(
2678 "bar".into(),
2679 ir_explicit_build(vec!["bar".into()], Rule::new("42", None), vec![]).into()
2680 )]
2681 .into_iter()
2682 .collect(),
2683 ["bar".into()].into_iter().collect()
2684 )
2685 );
2686 }
2687
2688 #[test]
2689 fn reference_pool_in_parent_module() {
2690 assert_eq!(
2691 compile_with_submodule(
2692 vec![
2693 ast::Pool::new("foo", "2").into(),
2694 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2695 ast::Submodule::new(SUBMODULE_PATH).into(),
2696 ],
2697 vec![ast_explicit_build(vec!["bar".into()], "qux", vec![], vec![]).into()],
2698 ),
2699 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2700 );
2701 }
2702
2703 #[test]
2704 fn reference_pool_in_child_module() {
2705 assert_eq!(
2706 compile_with_submodule(
2707 vec![
2708 ast::Submodule::new(SUBMODULE_PATH).into(),
2709 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2710 ast_explicit_build(vec!["bar".into()], "qux", vec![], vec![]).into(),
2711 ],
2712 vec![ast::Pool::new("foo", "2").into()],
2713 ),
2714 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2715 );
2716 }
2717
2718 #[test]
2719 fn fail_to_reference_pool_declared_after_submodule() {
2720 assert_eq!(
2721 compile_with_submodule(
2722 vec![
2723 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2724 ast::Submodule::new(SUBMODULE_PATH).into(),
2725 ast::Pool::new("foo", "2").into(),
2726 ],
2727 vec![ast_explicit_build(vec!["bar".into()], "qux", vec![], vec![]).into()],
2728 ),
2729 Err(CompileError::PoolNotFound("foo".into()))
2730 );
2731 }
2732
2733 #[test]
2734 fn fail_to_declare_pool_in_submodule_referenced_twice() {
2735 assert_eq!(
2736 compile_with_submodule(
2737 vec![
2738 ast::Submodule::new(SUBMODULE_PATH).into(),
2739 ast::Submodule::new(SUBMODULE_PATH).into(),
2740 ],
2741 vec![ast::Pool::new("foo", "2").into()],
2742 ),
2743 Err(CompileError::DuplicatePool("foo".into()))
2744 );
2745 }
2746 }
2747}