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: 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.into(),
362 variable_definitions
363 .iter()
364 .map(|&(name, value)| ast::VariableDefinition::new(name.into(), value.into()))
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".into(), 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".into(), "42".into()).into(),
446 ast_rule("foo", &[("command", "$x")]).into(),
447 ast_explicit_build(vec!["bar".into()], "foo".into(), 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".into(), None), vec![])
460 .into()
461 )]
462 .into_iter()
463 .collect(),
464 ["bar".into()].into_iter().collect()
465 )
466 );
467 }
468
469 #[test]
470 fn interpolate_two_variables_in_command() {
471 assert_eq!(
472 compile(
473 &[(
474 ROOT_MODULE_PATH.clone(),
475 ast::Module::new(vec![
476 ast::VariableDefinition::new("x".into(), "1".into()).into(),
477 ast::VariableDefinition::new("y".into(), "2".into()).into(),
478 ast_rule("foo", &[("command", "$x $y")]).into(),
479 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
480 ])
481 )]
482 .into_iter()
483 .collect(),
484 &DEFAULT_DEPENDENCIES,
485 &ROOT_MODULE_PATH
486 )
487 .unwrap(),
488 create_simple_config(
489 [(
490 "bar".into(),
491 ir_explicit_build(vec!["bar".into()], Rule::new("1 2".into(), None), vec![])
492 .into()
493 )]
494 .into_iter()
495 .collect(),
496 ["bar".into()].into_iter().collect()
497 )
498 );
499 }
500
501 #[test]
502 fn interpolate_variable_with_underscore_in_command() {
503 assert_eq!(
504 compile(
505 &[(
506 ROOT_MODULE_PATH.clone(),
507 ast::Module::new(vec![
508 ast::VariableDefinition::new("x_y".into(), "42".into()).into(),
509 ast_rule("foo", &[("command", "$x_y")]).into(),
510 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
511 ])
512 )]
513 .into_iter()
514 .collect(),
515 &DEFAULT_DEPENDENCIES,
516 &ROOT_MODULE_PATH
517 )
518 .unwrap(),
519 create_simple_config(
520 [(
521 "bar".into(),
522 ir_explicit_build(vec!["bar".into()], Rule::new("42".into(), None), vec![])
523 .into()
524 )]
525 .into_iter()
526 .collect(),
527 ["bar".into()].into_iter().collect()
528 )
529 );
530 }
531
532 #[test]
533 fn interpolate_variable_with_braces_in_command() {
534 assert_eq!(
535 compile(
536 &[(
537 ROOT_MODULE_PATH.clone(),
538 ast::Module::new(vec![
539 ast::VariableDefinition::new("x.y".into(), "42".into()).into(),
540 ast_rule("foo", &[("command", "${x.y}")]).into(),
541 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
542 ])
543 )]
544 .into_iter()
545 .collect(),
546 &DEFAULT_DEPENDENCIES,
547 &ROOT_MODULE_PATH
548 )
549 .unwrap(),
550 create_simple_config(
551 [(
552 "bar".into(),
553 ir_explicit_build(vec!["bar".into()], Rule::new("42".into(), None), vec![])
554 .into()
555 )]
556 .into_iter()
557 .collect(),
558 ["bar".into()].into_iter().collect()
559 )
560 );
561 }
562
563 #[test]
564 fn interpolate_dollar_sign_in_command() {
565 assert_eq!(
566 compile(
567 &[(
568 ROOT_MODULE_PATH.clone(),
569 ast::Module::new(vec![
570 ast_rule("foo", &[("command", "$$")]).into(),
571 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into()
572 ])
573 )]
574 .into_iter()
575 .collect(),
576 &DEFAULT_DEPENDENCIES,
577 &ROOT_MODULE_PATH
578 )
579 .unwrap(),
580 create_simple_config(
581 [(
582 "bar".into(),
583 ir_explicit_build(vec!["bar".into()], Rule::new("$".into(), None), vec![])
584 .into()
585 )]
586 .into_iter()
587 .collect(),
588 ["bar".into()].into_iter().collect()
589 )
590 );
591 }
592
593 #[test]
594 fn interpolate_escaped_variable_in_command() {
595 assert_eq!(
596 compile(
597 &[(
598 ROOT_MODULE_PATH.clone(),
599 ast::Module::new(vec![
600 ast::VariableDefinition::new("x".into(), "42".into()).into(),
601 ast_rule("foo", &[("command", "$$x")]).into(),
602 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
603 ])
604 )]
605 .into_iter()
606 .collect(),
607 &DEFAULT_DEPENDENCIES,
608 &ROOT_MODULE_PATH
609 )
610 .unwrap(),
611 create_simple_config(
612 [(
613 "bar".into(),
614 ir_explicit_build(vec!["bar".into()], Rule::new("$x".into(), None), vec![])
615 .into()
616 )]
617 .into_iter()
618 .collect(),
619 ["bar".into()].into_iter().collect()
620 )
621 );
622 }
623
624 #[test]
625 fn interpolate_nested_variable_in_command() {
626 assert_eq!(
627 compile(
628 &[(
629 ROOT_MODULE_PATH.clone(),
630 ast::Module::new(vec![
631 ast::VariableDefinition::new("x".into(), "42".into()).into(),
632 ast::VariableDefinition::new("y".into(), "$x".into()).into(),
633 ast_rule("foo", &[("command", "$y")]).into(),
634 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
635 ])
636 )]
637 .into_iter()
638 .collect(),
639 &DEFAULT_DEPENDENCIES,
640 &ROOT_MODULE_PATH
641 )
642 .unwrap(),
643 create_simple_config(
644 [(
645 "bar".into(),
646 ir_explicit_build(vec!["bar".into()], Rule::new("42".into(), None), vec![])
647 .into()
648 )]
649 .into_iter()
650 .collect(),
651 ["bar".into()].into_iter().collect()
652 )
653 );
654 }
655
656 #[test]
657 fn expand_variable_on_definition() {
658 assert_eq!(
659 compile(
660 &[(
661 ROOT_MODULE_PATH.clone(),
662 ast::Module::new(vec![
663 ast::VariableDefinition::new("x".into(), "1".into()).into(),
664 ast::VariableDefinition::new("y".into(), "$x".into()).into(),
665 ast::VariableDefinition::new("x".into(), "2".into()).into(),
666 ast_rule("foo", &[("command", "$y")]).into(),
667 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
668 ])
669 )]
670 .into_iter()
671 .collect(),
672 &DEFAULT_DEPENDENCIES,
673 &ROOT_MODULE_PATH
674 )
675 .unwrap(),
676 create_simple_config(
677 [(
678 "bar".into(),
679 ir_explicit_build(vec!["bar".into()], Rule::new("1".into(), None), vec![])
680 .into()
681 )]
682 .into_iter()
683 .collect(),
684 ["bar".into()].into_iter().collect()
685 )
686 );
687 }
688
689 #[test]
690 fn append_value_to_variable() {
691 assert_eq!(
692 compile(
693 &[(
694 ROOT_MODULE_PATH.clone(),
695 ast::Module::new(vec![
696 ast::VariableDefinition::new("x".into(), "1".into()).into(),
697 ast::VariableDefinition::new("x".into(), "$x 2".into()).into(),
698 ast_rule("foo", &[("command", "$x")]).into(),
699 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
700 ])
701 )]
702 .into_iter()
703 .collect(),
704 &DEFAULT_DEPENDENCIES,
705 &ROOT_MODULE_PATH
706 )
707 .unwrap(),
708 create_simple_config(
709 [(
710 "bar".into(),
711 ir_explicit_build(vec!["bar".into()], Rule::new("1 2".into(), None), vec![])
712 .into()
713 )]
714 .into_iter()
715 .collect(),
716 ["bar".into()].into_iter().collect()
717 )
718 );
719 }
720
721 #[test]
722 fn expand_escaped_variable_on_definition() {
723 assert_eq!(
724 compile(
725 &[(
726 ROOT_MODULE_PATH.clone(),
727 ast::Module::new(vec![
728 ast::VariableDefinition::new("y".into(), "42".into()).into(),
729 ast::VariableDefinition::new("x".into(), "$$y".into()).into(),
730 ast_rule("foo", &[("command", "$x")]).into(),
731 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
732 ])
733 )]
734 .into_iter()
735 .collect(),
736 &DEFAULT_DEPENDENCIES,
737 &ROOT_MODULE_PATH
738 )
739 .unwrap(),
740 create_simple_config(
741 [(
742 "bar".into(),
743 ir_explicit_build(vec!["bar".into()], Rule::new("$y".into(), None), vec![])
744 .into()
745 )]
746 .into_iter()
747 .collect(),
748 ["bar".into()].into_iter().collect()
749 )
750 );
751 }
752
753 #[test]
754 fn unescape_dollar_signs_once() {
755 assert_eq!(
756 compile(
757 &[(
758 ROOT_MODULE_PATH.clone(),
759 ast::Module::new(vec![
760 ast::VariableDefinition::new("x".into(), "$$$$".into()).into(),
761 ast_rule("foo", &[("command", "$x")]).into(),
762 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
763 ])
764 )]
765 .into_iter()
766 .collect(),
767 &DEFAULT_DEPENDENCIES,
768 &ROOT_MODULE_PATH
769 )
770 .unwrap(),
771 create_simple_config(
772 [(
773 "bar".into(),
774 ir_explicit_build(vec!["bar".into()], Rule::new("$$".into(), None), vec![])
775 .into()
776 )]
777 .into_iter()
778 .collect(),
779 ["bar".into()].into_iter().collect()
780 )
781 );
782 }
783
784 #[test]
785 fn interpolate_in_variable_in_command() {
786 assert_eq!(
787 compile(
788 &[(
789 ROOT_MODULE_PATH.clone(),
790 ast::Module::new(vec![
791 ast_rule("foo", &[("command", "$in")]).into(),
792 ast_explicit_build(
793 vec!["bar".into()],
794 "foo".into(),
795 vec!["baz".into()],
796 vec![]
797 )
798 .into(),
799 ])
800 )]
801 .into_iter()
802 .collect(),
803 &DEFAULT_DEPENDENCIES,
804 &ROOT_MODULE_PATH
805 )
806 .unwrap(),
807 create_simple_config(
808 [(
809 "bar".into(),
810 ir_explicit_build(
811 vec!["bar".into()],
812 Rule::new("baz".into(), None),
813 vec!["baz".into()]
814 )
815 .into()
816 )]
817 .into_iter()
818 .collect(),
819 ["bar".into()].into_iter().collect()
820 )
821 );
822 }
823
824 #[test]
825 fn interpolate_in_variable_with_implicit_input() {
826 assert_eq!(
827 compile(
828 &[(
829 ROOT_MODULE_PATH.clone(),
830 ast::Module::new(vec![
831 ast_rule("foo", &[("command", "$in")]).into(),
832 ast::Build::new(
833 vec!["bar".into()],
834 vec![],
835 "foo".into(),
836 vec!["baz".into()],
837 vec!["blah".into()],
838 vec![],
839 vec![]
840 )
841 .into(),
842 ])
843 )]
844 .into_iter()
845 .collect(),
846 &DEFAULT_DEPENDENCIES,
847 &ROOT_MODULE_PATH
848 )
849 .unwrap(),
850 create_simple_config(
851 [(
852 "bar".into(),
853 ir_explicit_build(
854 vec!["bar".into()],
855 Rule::new("baz".into(), None),
856 vec!["baz".into(), "blah".into()]
857 )
858 .into()
859 )]
860 .into_iter()
861 .collect(),
862 ["bar".into()].into_iter().collect()
863 )
864 );
865 }
866
867 #[test]
868 fn interpolate_in_newline_variable_in_command() {
869 assert_eq!(
870 compile(
871 &[(
872 ROOT_MODULE_PATH.clone(),
873 ast::Module::new(vec![
874 ast_rule("foo", &[("command", "$in_newline")]).into(),
875 ast_explicit_build(
876 vec!["bar".into()],
877 "foo".into(),
878 vec!["baz".into(), "qux".into()],
879 vec![]
880 )
881 .into(),
882 ])
883 )]
884 .into_iter()
885 .collect(),
886 &DEFAULT_DEPENDENCIES,
887 &ROOT_MODULE_PATH
888 )
889 .unwrap(),
890 create_simple_config(
891 [(
892 "bar".into(),
893 ir_explicit_build(
894 vec!["bar".into()],
895 Rule::new("baz\nqux".into(), None),
896 vec!["baz".into(), "qux".into()]
897 )
898 .into()
899 )]
900 .into_iter()
901 .collect(),
902 ["bar".into()].into_iter().collect()
903 )
904 );
905 }
906
907 #[test]
908 fn interpolate_in_newline_variable_with_implicit_and_order_only_inputs() {
909 assert_eq!(
910 compile(
911 &[(
912 ROOT_MODULE_PATH.clone(),
913 ast::Module::new(vec![
914 ast_rule("foo", &[("command", "$in_newline")]).into(),
915 ast::Build::new(
916 vec!["bar".into()],
917 vec![],
918 "foo".into(),
919 vec!["baz".into(), "qux".into()],
920 vec!["blah".into()],
921 vec!["corge".into()],
922 vec![]
923 )
924 .into(),
925 ])
926 )]
927 .into_iter()
928 .collect(),
929 &DEFAULT_DEPENDENCIES,
930 &ROOT_MODULE_PATH
931 )
932 .unwrap(),
933 create_simple_config(
934 [(
935 "bar".into(),
936 Build::new(
937 vec!["bar".into()],
938 vec![],
939 Some(Rule::new("baz\nqux".into(), None)),
940 vec!["baz".into(), "qux".into(), "blah".into()],
941 vec!["corge".into()],
942 None
943 )
944 .into()
945 )]
946 .into_iter()
947 .collect(),
948 ["bar".into()].into_iter().collect()
949 )
950 );
951 }
952
953 #[test]
954 fn interpolate_out_variable_in_command() {
955 assert_eq!(
956 compile(
957 &[(
958 ROOT_MODULE_PATH.clone(),
959 ast::Module::new(vec![
960 ast_rule("foo", &[("command", "$out")]).into(),
961 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
962 ])
963 )]
964 .into_iter()
965 .collect(),
966 &DEFAULT_DEPENDENCIES,
967 &ROOT_MODULE_PATH
968 )
969 .unwrap(),
970 create_simple_config(
971 [(
972 "bar".into(),
973 ir_explicit_build(vec!["bar".into()], Rule::new("bar".into(), None), vec![])
974 .into()
975 )]
976 .into_iter()
977 .collect(),
978 ["bar".into()].into_iter().collect()
979 )
980 );
981 }
982
983 #[test]
984 fn interpolate_out_variable_with_implicit_output() {
985 let build = Arc::new(Build::new(
986 vec!["bar".into()],
987 vec!["baz".into()],
988 Rule::new("bar".into(), None).into(),
989 vec![],
990 vec![],
991 None,
992 ));
993
994 assert_eq!(
995 compile(
996 &[(
997 ROOT_MODULE_PATH.clone(),
998 ast::Module::new(vec![
999 ast_rule("foo", &[("command", "$out")]).into(),
1000 ast::Build::new(
1001 vec!["bar".into()],
1002 vec!["baz".into()],
1003 "foo".into(),
1004 vec![],
1005 vec![],
1006 vec![],
1007 vec![]
1008 )
1009 .into(),
1010 ])
1011 )]
1012 .into_iter()
1013 .collect(),
1014 &DEFAULT_DEPENDENCIES,
1015 &ROOT_MODULE_PATH
1016 )
1017 .unwrap(),
1018 create_simple_config(
1019 [("baz".into(), build.clone()), ("bar".into(), build)]
1020 .into_iter()
1021 .collect(),
1022 ["baz".into(), "bar".into()].into_iter().collect()
1023 )
1024 );
1025 }
1026
1027 #[test]
1028 fn compile_order_only_inputs() {
1029 assert_eq!(
1030 compile(
1031 &[(
1032 ROOT_MODULE_PATH.clone(),
1033 ast::Module::new(vec![
1034 ast_rule("foo", &[("command", "$in")]).into(),
1035 ast::Build::new(
1036 vec!["bar".into()],
1037 vec![],
1038 "foo".into(),
1039 vec![],
1040 vec![],
1041 vec!["baz".into()],
1042 vec![]
1043 )
1044 .into(),
1045 ])
1046 )]
1047 .into_iter()
1048 .collect(),
1049 &DEFAULT_DEPENDENCIES,
1050 &ROOT_MODULE_PATH
1051 )
1052 .unwrap(),
1053 create_simple_config(
1054 [(
1055 "bar".into(),
1056 Build::new(
1057 vec!["bar".into()],
1058 vec![],
1059 Some(Rule::new("".into(), None)),
1060 vec![],
1061 vec!["baz".into()],
1062 None
1063 )
1064 .into()
1065 )]
1066 .into_iter()
1067 .collect(),
1068 ["bar".into()].into_iter().collect()
1069 )
1070 );
1071 }
1072
1073 #[test]
1074 fn interpolate_variables_in_paths() {
1075 let build = Arc::new(Build::new(
1076 vec!["foo/output".into()],
1077 vec!["foo/implicit_output".into()],
1078 Rule::new("foo/input foo/output".into(), None).into(),
1079 vec!["foo/input".into(), "foo/implicit_input".into()],
1080 vec!["foo/order_only_input".into()],
1081 None,
1082 ));
1083
1084 assert_eq!(
1085 compile(
1086 &[(
1087 ROOT_MODULE_PATH.clone(),
1088 ast::Module::new(vec![
1089 ast::VariableDefinition::new("x".into(), "foo".into()).into(),
1090 ast_rule("bar", &[("command", "$in $out")]).into(),
1091 ast::Build::new(
1092 vec!["$x/output".into()],
1093 vec!["${x}/implicit_output".into()],
1094 "bar".into(),
1095 vec!["$x/input".into()],
1096 vec!["${x}/implicit_input".into()],
1097 vec!["$x/order_only_input".into()],
1098 vec![]
1099 )
1100 .into(),
1101 ])
1102 )]
1103 .into_iter()
1104 .collect(),
1105 &DEFAULT_DEPENDENCIES,
1106 &ROOT_MODULE_PATH
1107 )
1108 .unwrap(),
1109 create_simple_config(
1110 [
1111 ("foo/output".into(), build.clone()),
1112 ("foo/implicit_output".into(), build)
1113 ]
1114 .into_iter()
1115 .collect(),
1116 ["foo/output".into(), "foo/implicit_output".into()]
1117 .into_iter()
1118 .collect()
1119 )
1120 );
1121 }
1122
1123 #[test]
1124 fn interpolate_build_local_variable_in_path() {
1125 assert_eq!(
1126 compile(
1127 &[(
1128 ROOT_MODULE_PATH.clone(),
1129 ast::Module::new(vec![
1130 ast_rule("foo", &[("command", "$out")]).into(),
1131 ast_explicit_build(
1132 vec!["$x/bar".into()],
1133 "foo".into(),
1134 vec![],
1135 vec![ast::VariableDefinition::new("x".into(), "baz".into())]
1136 )
1137 .into(),
1138 ])
1139 )]
1140 .into_iter()
1141 .collect(),
1142 &DEFAULT_DEPENDENCIES,
1143 &ROOT_MODULE_PATH
1144 )
1145 .unwrap(),
1146 create_simple_config(
1147 [(
1148 "baz/bar".into(),
1149 ir_explicit_build(
1150 vec!["baz/bar".into()],
1151 Rule::new("baz/bar".into(), None),
1152 vec![]
1153 )
1154 .into()
1155 )]
1156 .into_iter()
1157 .collect(),
1158 ["baz/bar".into()].into_iter().collect()
1159 )
1160 );
1161 }
1162
1163 #[test]
1164 fn unescape_dollar_sign_in_path() {
1165 assert_eq!(
1166 compile(
1167 &[(
1168 ROOT_MODULE_PATH.clone(),
1169 ast::Module::new(vec![
1170 ast_rule("foo", &[("command", "")]).into(),
1171 ast_explicit_build(vec!["bar$$baz".into()], "foo".into(), vec![], vec![])
1172 .into(),
1173 ])
1174 )]
1175 .into_iter()
1176 .collect(),
1177 &DEFAULT_DEPENDENCIES,
1178 &ROOT_MODULE_PATH
1179 )
1180 .unwrap(),
1181 create_simple_config(
1182 [(
1183 "bar$baz".into(),
1184 ir_explicit_build(vec!["bar$baz".into()], Rule::new("".into(), None), vec![])
1185 .into()
1186 )]
1187 .into_iter()
1188 .collect(),
1189 ["bar$baz".into()].into_iter().collect()
1190 )
1191 );
1192 }
1193
1194 #[test]
1195 fn do_not_interpolate_in_and_out_variables_in_paths() {
1196 assert_eq!(
1197 compile(
1198 &[(
1199 ROOT_MODULE_PATH.clone(),
1200 ast::Module::new(vec![
1201 ast_rule("foo", &[("command", "$in $out")]).into(),
1202 ast_explicit_build(
1203 vec!["bar$out".into()],
1204 "foo".into(),
1205 vec!["baz$in".into()],
1206 vec![]
1207 )
1208 .into(),
1209 ])
1210 )]
1211 .into_iter()
1212 .collect(),
1213 &DEFAULT_DEPENDENCIES,
1214 &ROOT_MODULE_PATH
1215 )
1216 .unwrap(),
1217 create_simple_config(
1218 [(
1219 "bar".into(),
1220 ir_explicit_build(
1221 vec!["bar".into()],
1222 Rule::new("baz bar".into(), None),
1223 vec!["baz".into()]
1224 )
1225 .into()
1226 )]
1227 .into_iter()
1228 .collect(),
1229 ["bar".into()].into_iter().collect()
1230 )
1231 );
1232 }
1233
1234 #[test]
1235 fn interpolate_variable_in_default_output() {
1236 assert_eq!(
1237 compile(
1238 &[(
1239 ROOT_MODULE_PATH.clone(),
1240 ast::Module::new(vec![
1241 ast::VariableDefinition::new("x".into(), "foo".into()).into(),
1242 ast_rule("bar", &[("command", "")]).into(),
1243 ast_explicit_build(vec!["foo/baz".into()], "bar".into(), vec![], vec![])
1244 .into(),
1245 ast_explicit_build(vec!["qux".into()], "bar".into(), vec![], vec![]).into(),
1246 ast::DefaultOutput::new(vec!["$x/baz".into()]).into(),
1247 ])
1248 )]
1249 .into_iter()
1250 .collect(),
1251 &DEFAULT_DEPENDENCIES,
1252 &ROOT_MODULE_PATH
1253 )
1254 .unwrap(),
1255 create_simple_config(
1256 [
1257 (
1258 "foo/baz".into(),
1259 ir_explicit_build(
1260 vec!["foo/baz".into()],
1261 Rule::new("".into(), None),
1262 vec![]
1263 )
1264 .into()
1265 ),
1266 (
1267 "qux".into(),
1268 ir_explicit_build(vec!["qux".into()], Rule::new("".into(), None), vec![])
1269 .into()
1270 )
1271 ]
1272 .into_iter()
1273 .collect(),
1274 ["foo/baz".into()].into_iter().collect()
1275 )
1276 );
1277 }
1278
1279 #[test]
1280 fn generate_build_ids() {
1281 assert_eq!(
1282 compile(
1283 &[(
1284 ROOT_MODULE_PATH.clone(),
1285 ast::Module::new(vec![
1286 ast_rule("foo", &[("command", "")]).into(),
1287 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
1288 ast_explicit_build(vec!["baz".into()], "foo".into(), vec![], vec![]).into()
1289 ])
1290 )]
1291 .into_iter()
1292 .collect(),
1293 &DEFAULT_DEPENDENCIES,
1294 &ROOT_MODULE_PATH
1295 )
1296 .unwrap(),
1297 create_simple_config(
1298 [
1299 (
1300 "bar".into(),
1301 ir_explicit_build(vec!["bar".into()], Rule::new("".into(), None), vec![])
1302 .into()
1303 ),
1304 (
1305 "baz".into(),
1306 ir_explicit_build(vec!["baz".into()], Rule::new("".into(), None), vec![])
1307 .into()
1308 )
1309 ]
1310 .into_iter()
1311 .collect(),
1312 ["bar".into(), "baz".into()].into_iter().collect()
1313 )
1314 );
1315 }
1316
1317 #[test]
1318 fn interpolate_build_local_variable() {
1319 assert_eq!(
1320 compile(
1321 &[(
1322 ROOT_MODULE_PATH.clone(),
1323 ast::Module::new(vec![
1324 ast_rule("foo", &[("command", "$x")]).into(),
1325 ast_explicit_build(
1326 vec!["bar".into()],
1327 "foo".into(),
1328 vec![],
1329 vec![ast::VariableDefinition::new("x".into(), "42".into())]
1330 )
1331 .into(),
1332 ])
1333 )]
1334 .into_iter()
1335 .collect(),
1336 &DEFAULT_DEPENDENCIES,
1337 &ROOT_MODULE_PATH
1338 )
1339 .unwrap(),
1340 create_simple_config(
1341 [(
1342 "bar".into(),
1343 ir_explicit_build(vec!["bar".into()], Rule::new("42".into(), None), vec![])
1344 .into()
1345 )]
1346 .into_iter()
1347 .collect(),
1348 ["bar".into()].into_iter().collect()
1349 )
1350 );
1351 }
1352
1353 #[test]
1354 fn interpolate_rule_variable_in_command() {
1355 assert_eq!(
1356 compile(
1357 &[(
1358 ROOT_MODULE_PATH.clone(),
1359 ast::Module::new(vec![
1360 ast_rule(
1361 "foo",
1362 &[("command", "$description"), ("description", "bar")]
1363 )
1364 .into(),
1365 ast_explicit_build(vec!["baz".into()], "foo".into(), vec![], vec![]).into(),
1366 ])
1367 )]
1368 .into_iter()
1369 .collect(),
1370 &DEFAULT_DEPENDENCIES,
1371 &ROOT_MODULE_PATH
1372 )
1373 .unwrap(),
1374 create_simple_config(
1375 [(
1376 "baz".into(),
1377 ir_explicit_build(
1378 vec!["baz".into()],
1379 Rule::new("bar".into(), Some("bar".into())),
1380 vec![]
1381 )
1382 .into()
1383 )]
1384 .into_iter()
1385 .collect(),
1386 ["baz".into()].into_iter().collect()
1387 )
1388 );
1389 }
1390
1391 #[test]
1392 fn compile_last_command_in_rule() {
1393 assert_eq!(
1394 compile(
1395 &[(
1396 ROOT_MODULE_PATH.clone(),
1397 ast::Module::new(vec![
1398 ast_rule("foo", &[("command", "first"), ("command", "second")]).into(),
1399 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
1400 ])
1401 )]
1402 .into_iter()
1403 .collect(),
1404 &DEFAULT_DEPENDENCIES,
1405 &ROOT_MODULE_PATH
1406 )
1407 .unwrap(),
1408 create_simple_config(
1409 [(
1410 "bar".into(),
1411 ir_explicit_build(vec!["bar".into()], Rule::new("second".into(), None), vec![])
1412 .into()
1413 )]
1414 .into_iter()
1415 .collect(),
1416 ["bar".into()].into_iter().collect()
1417 )
1418 );
1419 }
1420
1421 #[test]
1422 fn compile_build_level_command_shadowing_rule_command() {
1423 assert_eq!(
1424 compile(
1425 &[(
1426 ROOT_MODULE_PATH.clone(),
1427 ast::Module::new(vec![
1428 ast_rule("foo", &[("command", "rule")]).into(),
1429 ast_explicit_build(
1430 vec!["bar".into()],
1431 "foo".into(),
1432 vec![],
1433 vec![ast::VariableDefinition::new(
1434 "command".into(),
1435 "build".into()
1436 )]
1437 )
1438 .into(),
1439 ])
1440 )]
1441 .into_iter()
1442 .collect(),
1443 &DEFAULT_DEPENDENCIES,
1444 &ROOT_MODULE_PATH
1445 )
1446 .unwrap(),
1447 create_simple_config(
1448 [(
1449 "bar".into(),
1450 ir_explicit_build(vec!["bar".into()], Rule::new("build".into(), None), vec![])
1451 .into()
1452 )]
1453 .into_iter()
1454 .collect(),
1455 ["bar".into()].into_iter().collect()
1456 )
1457 );
1458 }
1459
1460 #[test]
1461 fn compile_global_description() {
1462 assert_eq!(
1463 compile(
1464 &[(
1465 ROOT_MODULE_PATH.clone(),
1466 ast::Module::new(vec![
1467 ast::VariableDefinition::new("description".into(), "global".into()).into(),
1468 ast_rule("foo", &[("command", "")]).into(),
1469 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
1470 ])
1471 )]
1472 .into_iter()
1473 .collect(),
1474 &DEFAULT_DEPENDENCIES,
1475 &ROOT_MODULE_PATH
1476 )
1477 .unwrap(),
1478 create_simple_config(
1479 [(
1480 "bar".into(),
1481 ir_explicit_build(
1482 vec!["bar".into()],
1483 Rule::new("".into(), Some("global".into())),
1484 vec![]
1485 )
1486 .into()
1487 )]
1488 .into_iter()
1489 .collect(),
1490 ["bar".into()].into_iter().collect()
1491 )
1492 );
1493 }
1494
1495 #[test]
1496 fn compile_source_map() {
1497 assert_eq!(
1498 compile(
1499 &[(
1500 ROOT_MODULE_PATH.clone(),
1501 ast::Module::new(vec![
1502 ast_rule("foo", &[("command", "foo")]).into(),
1503 ast_explicit_build(
1504 vec!["bar".into()],
1505 "foo".into(),
1506 vec![],
1507 vec![ast::VariableDefinition::new(
1508 SOURCE_VARIABLE_NAME.into(),
1509 "oh-my-src".into()
1510 )]
1511 )
1512 .into(),
1513 ])
1514 )]
1515 .into_iter()
1516 .collect(),
1517 &DEFAULT_DEPENDENCIES,
1518 &ROOT_MODULE_PATH
1519 )
1520 .unwrap(),
1521 Config::new(
1522 [(
1523 "bar".into(),
1524 ir_explicit_build(vec!["bar".into()], Rule::new("foo".into(), None), vec![])
1525 .into()
1526 )]
1527 .into_iter()
1528 .collect(),
1529 ["bar".into()].into_iter().collect(),
1530 [("bar".into(), "oh-my-src".into())].into_iter().collect(),
1531 Default::default(),
1532 None,
1533 )
1534 );
1535 }
1536
1537 #[test]
1538 fn compile_phony_rule() {
1539 assert_eq!(
1540 compile(
1541 &[(
1542 ROOT_MODULE_PATH.clone(),
1543 ast::Module::new(vec![
1544 ast_explicit_build(
1545 vec!["foo".into()],
1546 "phony".into(),
1547 vec!["bar".into()],
1548 vec![]
1549 )
1550 .into(),
1551 ])
1552 )]
1553 .into_iter()
1554 .collect(),
1555 &DEFAULT_DEPENDENCIES,
1556 &ROOT_MODULE_PATH
1557 )
1558 .unwrap(),
1559 create_simple_config(
1560 [(
1561 "foo".into(),
1562 Build::new(
1563 vec!["foo".into()],
1564 vec![],
1565 None,
1566 vec!["bar".into()],
1567 vec![],
1568 None
1569 )
1570 .into()
1571 )]
1572 .into_iter()
1573 .collect(),
1574 ["foo".into()].into_iter().collect()
1575 )
1576 );
1577 }
1578
1579 #[test]
1580 fn compile_build_directory() {
1581 assert_eq!(
1582 compile(
1583 &[(
1584 ROOT_MODULE_PATH.clone(),
1585 ast::Module::new(vec![
1586 ast::VariableDefinition::new("builddir".into(), "foo".into()).into()
1587 ])
1588 )]
1589 .into_iter()
1590 .collect(),
1591 &DEFAULT_DEPENDENCIES,
1592 &ROOT_MODULE_PATH
1593 )
1594 .unwrap(),
1595 Config::new(
1596 Default::default(),
1597 Default::default(),
1598 Default::default(),
1599 Default::default(),
1600 Some("foo".into())
1601 )
1602 );
1603 }
1604
1605 #[test]
1606 fn compile_build_directory_with_variable() {
1607 assert_eq!(
1608 compile(
1609 &[(
1610 ROOT_MODULE_PATH.clone(),
1611 ast::Module::new(vec![
1612 ast::VariableDefinition::new("x".into(), "foo".into()).into(),
1613 ast::VariableDefinition::new("builddir".into(), "$x/bar".into()).into(),
1614 ])
1615 )]
1616 .into_iter()
1617 .collect(),
1618 &DEFAULT_DEPENDENCIES,
1619 &ROOT_MODULE_PATH
1620 )
1621 .unwrap(),
1622 Config::new(
1623 Default::default(),
1624 Default::default(),
1625 Default::default(),
1626 Default::default(),
1627 Some("foo/bar".into())
1628 )
1629 );
1630 }
1631
1632 #[test]
1633 fn compile_dynamic_module_variable() {
1634 assert_eq!(
1635 compile(
1636 &[(
1637 ROOT_MODULE_PATH.clone(),
1638 ast::Module::new(vec![
1639 ast_explicit_build(
1640 vec!["foo".into()],
1641 "phony".into(),
1642 vec![],
1643 vec![ast::VariableDefinition::new("dyndep".into(), "bar".into())]
1644 )
1645 .into()
1646 ])
1647 )]
1648 .into_iter()
1649 .collect(),
1650 &DEFAULT_DEPENDENCIES,
1651 &ROOT_MODULE_PATH
1652 )
1653 .unwrap(),
1654 create_simple_config(
1655 [(
1656 "foo".into(),
1657 Build::new(
1658 vec!["foo".into()],
1659 vec![],
1660 None,
1661 vec![],
1662 vec![],
1663 Some("bar".into())
1664 )
1665 .into()
1666 )]
1667 .into_iter()
1668 .collect(),
1669 ["foo".into()].into_iter().collect()
1670 )
1671 );
1672 }
1673
1674 #[test]
1675 fn compile_rule_level_dynamic_module_variable() {
1676 assert_eq!(
1677 compile(
1678 &[(
1679 ROOT_MODULE_PATH.clone(),
1680 ast::Module::new(vec![
1681 ast_rule("foo", &[("command", ""), ("dyndep", "$out.dd")]).into(),
1682 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
1683 ])
1684 )]
1685 .into_iter()
1686 .collect(),
1687 &DEFAULT_DEPENDENCIES,
1688 &ROOT_MODULE_PATH
1689 )
1690 .unwrap(),
1691 create_simple_config(
1692 [(
1693 "bar".into(),
1694 Build::new(
1695 vec!["bar".into()],
1696 vec![],
1697 Some(Rule::new("".into(), None)),
1698 vec![],
1699 vec![],
1700 Some("bar.dd".into())
1701 )
1702 .into()
1703 )]
1704 .into_iter()
1705 .collect(),
1706 ["bar".into()].into_iter().collect()
1707 )
1708 );
1709 }
1710
1711 #[test]
1712 fn compile_build_level_dynamic_module_variable_shadowing_rule_level_one() {
1713 assert_eq!(
1714 compile(
1715 &[(
1716 ROOT_MODULE_PATH.clone(),
1717 ast::Module::new(vec![
1718 ast_rule("foo", &[("command", ""), ("dyndep", "rule.dd")]).into(),
1719 ast_explicit_build(
1720 vec!["bar".into()],
1721 "foo".into(),
1722 vec![],
1723 vec![ast::VariableDefinition::new(
1724 "dyndep".into(),
1725 "build.dd".into()
1726 )]
1727 )
1728 .into(),
1729 ])
1730 )]
1731 .into_iter()
1732 .collect(),
1733 &DEFAULT_DEPENDENCIES,
1734 &ROOT_MODULE_PATH
1735 )
1736 .unwrap(),
1737 create_simple_config(
1738 [(
1739 "bar".into(),
1740 Build::new(
1741 vec!["bar".into()],
1742 vec![],
1743 Some(Rule::new("".into(), None)),
1744 vec![],
1745 vec![],
1746 Some("build.dd".into())
1747 )
1748 .into()
1749 )]
1750 .into_iter()
1751 .collect(),
1752 ["bar".into()].into_iter().collect()
1753 )
1754 );
1755 }
1756
1757 #[test]
1758 fn compile_make_header_dependency() {
1759 assert_eq!(
1760 compile(
1761 &[(
1762 ROOT_MODULE_PATH.clone(),
1763 ast::Module::new(vec![
1764 ast_rule("foo", &[("command", "bar"), ("depfile", "foo.d")]).into(),
1765 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
1766 ])
1767 )]
1768 .into_iter()
1769 .collect(),
1770 &DEFAULT_DEPENDENCIES,
1771 &ROOT_MODULE_PATH
1772 )
1773 .unwrap(),
1774 create_simple_config(
1775 [(
1776 "bar".into(),
1777 ir_explicit_build(
1778 vec!["bar".into()],
1779 Rule::new("bar".into(), None).with_header_dependency(Some(
1780 HeaderDependency::Make {
1781 path: "foo.d".into()
1782 }
1783 )),
1784 vec![]
1785 )
1786 .into()
1787 )]
1788 .into_iter()
1789 .collect(),
1790 ["bar".into()].into_iter().collect()
1791 )
1792 );
1793 }
1794
1795 #[test]
1796 fn compile_gcc_header_dependency() {
1797 assert_eq!(
1798 compile(
1799 &[(
1800 ROOT_MODULE_PATH.clone(),
1801 ast::Module::new(vec![
1802 ast_rule(
1803 "foo",
1804 &[("command", "bar"), ("depfile", "foo.d"), ("deps", "gcc")]
1805 )
1806 .into(),
1807 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
1808 ])
1809 )]
1810 .into_iter()
1811 .collect(),
1812 &DEFAULT_DEPENDENCIES,
1813 &ROOT_MODULE_PATH
1814 )
1815 .unwrap(),
1816 create_simple_config(
1817 [(
1818 "bar".into(),
1819 ir_explicit_build(
1820 vec!["bar".into()],
1821 Rule::new("bar".into(), None).with_header_dependency(Some(
1822 HeaderDependency::Gcc {
1823 path: "foo.d".into()
1824 }
1825 )),
1826 vec![]
1827 )
1828 .into()
1829 )]
1830 .into_iter()
1831 .collect(),
1832 ["bar".into()].into_iter().collect()
1833 )
1834 );
1835 }
1836
1837 #[test]
1838 fn compile_make_header_dependency_with_build_level_depfile() {
1839 assert_eq!(
1840 compile(
1841 &[(
1842 ROOT_MODULE_PATH.clone(),
1843 ast::Module::new(vec![
1844 ast_rule("foo", &[("command", "bar")]).into(),
1845 ast_explicit_build(
1846 vec!["bar".into()],
1847 "foo".into(),
1848 vec![],
1849 vec![ast::VariableDefinition::new(
1850 "depfile".into(),
1851 "foo.d".into()
1852 )]
1853 )
1854 .into(),
1855 ])
1856 )]
1857 .into_iter()
1858 .collect(),
1859 &DEFAULT_DEPENDENCIES,
1860 &ROOT_MODULE_PATH
1861 )
1862 .unwrap(),
1863 create_simple_config(
1864 [(
1865 "bar".into(),
1866 ir_explicit_build(
1867 vec!["bar".into()],
1868 Rule::new("bar".into(), None).with_header_dependency(Some(
1869 HeaderDependency::Make {
1870 path: "foo.d".into()
1871 }
1872 )),
1873 vec![]
1874 )
1875 .into()
1876 )]
1877 .into_iter()
1878 .collect(),
1879 ["bar".into()].into_iter().collect()
1880 )
1881 );
1882 }
1883
1884 #[test]
1885 fn compile_gcc_header_dependency_with_build_level_deps() {
1886 assert_eq!(
1887 compile(
1888 &[(
1889 ROOT_MODULE_PATH.clone(),
1890 ast::Module::new(vec![
1891 ast_rule("foo", &[("command", "bar"), ("depfile", "$out.d")]).into(),
1892 ast_explicit_build(
1893 vec!["bar".into()],
1894 "foo".into(),
1895 vec![],
1896 vec![ast::VariableDefinition::new("deps".into(), "gcc".into())]
1897 )
1898 .into(),
1899 ])
1900 )]
1901 .into_iter()
1902 .collect(),
1903 &DEFAULT_DEPENDENCIES,
1904 &ROOT_MODULE_PATH
1905 )
1906 .unwrap(),
1907 create_simple_config(
1908 [(
1909 "bar".into(),
1910 ir_explicit_build(
1911 vec!["bar".into()],
1912 Rule::new("bar".into(), None).with_header_dependency(Some(
1913 HeaderDependency::Gcc {
1914 path: "bar.d".into()
1915 }
1916 )),
1917 vec![]
1918 )
1919 .into()
1920 )]
1921 .into_iter()
1922 .collect(),
1923 ["bar".into()].into_iter().collect()
1924 )
1925 );
1926 }
1927
1928 #[test]
1929 fn compile_make_header_dependency_with_build_level_empty_deps_shadowing_rule_deps() {
1930 assert_eq!(
1931 compile(
1932 &[(
1933 ROOT_MODULE_PATH.clone(),
1934 ast::Module::new(vec![
1935 ast_rule(
1936 "foo",
1937 &[("command", "bar"), ("depfile", "foo.d"), ("deps", "gcc")]
1938 )
1939 .into(),
1940 ast_explicit_build(
1941 vec!["bar".into()],
1942 "foo".into(),
1943 vec![],
1944 vec![ast::VariableDefinition::new("deps".into(), "".into())]
1945 )
1946 .into(),
1947 ])
1948 )]
1949 .into_iter()
1950 .collect(),
1951 &DEFAULT_DEPENDENCIES,
1952 &ROOT_MODULE_PATH
1953 )
1954 .unwrap(),
1955 create_simple_config(
1956 [(
1957 "bar".into(),
1958 ir_explicit_build(
1959 vec!["bar".into()],
1960 Rule::new("bar".into(), None).with_header_dependency(Some(
1961 HeaderDependency::Make {
1962 path: "foo.d".into()
1963 }
1964 )),
1965 vec![]
1966 )
1967 .into()
1968 )]
1969 .into_iter()
1970 .collect(),
1971 ["bar".into()].into_iter().collect()
1972 )
1973 );
1974 }
1975
1976 #[test]
1977 fn fail_to_compile_gcc_header_dependency_without_depfile() {
1978 assert_eq!(
1979 compile(
1980 &[(
1981 ROOT_MODULE_PATH.clone(),
1982 ast::Module::new(vec![
1983 ast_rule("foo", &[("command", "bar"), ("deps", "gcc")]).into(),
1984 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
1985 ])
1986 )]
1987 .into_iter()
1988 .collect(),
1989 &DEFAULT_DEPENDENCIES,
1990 &ROOT_MODULE_PATH
1991 ),
1992 Err(CompileError::MissingDepfile("foo".into()))
1993 );
1994 }
1995
1996 #[test]
1997 fn fail_to_compile_unknown_header_dependency() {
1998 assert_eq!(
1999 compile(
2000 &[(
2001 ROOT_MODULE_PATH.clone(),
2002 ast::Module::new(vec![
2003 ast_rule("foo", &[("command", "bar"), ("deps", "clang")]).into(),
2004 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
2005 ])
2006 )]
2007 .into_iter()
2008 .collect(),
2009 &DEFAULT_DEPENDENCIES,
2010 &ROOT_MODULE_PATH
2011 ),
2012 Err(CompileError::InvalidDependencyStyle("clang".into()))
2013 );
2014 }
2015
2016 #[test]
2017 fn compile_msvc_header_dependency_with_default_prefix() {
2018 assert_eq!(
2019 compile(
2020 &[(
2021 ROOT_MODULE_PATH.clone(),
2022 ast::Module::new(vec![
2023 ast_rule("foo", &[("command", "bar"), ("deps", "msvc")]).into(),
2024 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
2025 ])
2026 )]
2027 .into_iter()
2028 .collect(),
2029 &DEFAULT_DEPENDENCIES,
2030 &ROOT_MODULE_PATH
2031 )
2032 .unwrap(),
2033 create_simple_config(
2034 [(
2035 "bar".into(),
2036 ir_explicit_build(
2037 vec!["bar".into()],
2038 Rule::new("bar".into(), None).with_header_dependency(Some(
2039 HeaderDependency::Msvc {
2040 prefix: "Note: including file: ".into()
2041 }
2042 )),
2043 vec![]
2044 )
2045 .into()
2046 )]
2047 .into_iter()
2048 .collect(),
2049 ["bar".into()].into_iter().collect()
2050 )
2051 );
2052 }
2053
2054 #[test]
2055 fn compile_msvc_header_dependency_ignoring_depfile() {
2056 assert_eq!(
2057 compile(
2058 &[(
2059 ROOT_MODULE_PATH.clone(),
2060 ast::Module::new(vec![
2061 ast_rule(
2062 "foo",
2063 &[("command", "bar"), ("depfile", "foo.d"), ("deps", "msvc")]
2064 )
2065 .into(),
2066 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
2067 ])
2068 )]
2069 .into_iter()
2070 .collect(),
2071 &DEFAULT_DEPENDENCIES,
2072 &ROOT_MODULE_PATH
2073 )
2074 .unwrap(),
2075 create_simple_config(
2076 [(
2077 "bar".into(),
2078 ir_explicit_build(
2079 vec!["bar".into()],
2080 Rule::new("bar".into(), None).with_header_dependency(Some(
2081 HeaderDependency::Msvc {
2082 prefix: "Note: including file: ".into()
2083 }
2084 )),
2085 vec![]
2086 )
2087 .into()
2088 )]
2089 .into_iter()
2090 .collect(),
2091 ["bar".into()].into_iter().collect()
2092 )
2093 );
2094 }
2095
2096 #[test]
2097 fn compile_msvc_header_dependency_with_empty_prefix() {
2098 assert_eq!(
2099 compile(
2100 &[(
2101 ROOT_MODULE_PATH.clone(),
2102 ast::Module::new(vec![
2103 ast_rule(
2104 "foo",
2105 &[
2106 ("command", "bar"),
2107 ("deps", "msvc"),
2108 ("msvc_deps_prefix", "")
2109 ]
2110 )
2111 .into(),
2112 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
2113 ])
2114 )]
2115 .into_iter()
2116 .collect(),
2117 &DEFAULT_DEPENDENCIES,
2118 &ROOT_MODULE_PATH
2119 )
2120 .unwrap(),
2121 create_simple_config(
2122 [(
2123 "bar".into(),
2124 ir_explicit_build(
2125 vec!["bar".into()],
2126 Rule::new("bar".into(), None).with_header_dependency(Some(
2127 HeaderDependency::Msvc {
2128 prefix: "Note: including file: ".into()
2129 }
2130 )),
2131 vec![]
2132 )
2133 .into()
2134 )]
2135 .into_iter()
2136 .collect(),
2137 ["bar".into()].into_iter().collect()
2138 )
2139 );
2140 }
2141
2142 #[test]
2143 fn compile_msvc_header_dependency_with_custom_prefix() {
2144 assert_eq!(
2145 compile(
2146 &[(
2147 ROOT_MODULE_PATH.clone(),
2148 ast::Module::new(vec![
2149 ast_rule(
2150 "foo",
2151 &[
2152 ("command", "bar"),
2153 ("deps", "msvc"),
2154 ("msvc_deps_prefix", "Hinweis: Einlesen der Datei ")
2155 ]
2156 )
2157 .into(),
2158 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
2159 ])
2160 )]
2161 .into_iter()
2162 .collect(),
2163 &DEFAULT_DEPENDENCIES,
2164 &ROOT_MODULE_PATH
2165 )
2166 .unwrap(),
2167 create_simple_config(
2168 [(
2169 "bar".into(),
2170 ir_explicit_build(
2171 vec!["bar".into()],
2172 Rule::new("bar".into(), None).with_header_dependency(Some(
2173 HeaderDependency::Msvc {
2174 prefix: "Hinweis: Einlesen der Datei ".into()
2175 }
2176 )),
2177 vec![]
2178 )
2179 .into()
2180 )]
2181 .into_iter()
2182 .collect(),
2183 ["bar".into()].into_iter().collect()
2184 )
2185 );
2186 }
2187
2188 #[test]
2189 fn compile_msvc_header_dependency_with_global_prefix() {
2190 assert_eq!(
2191 compile(
2192 &[(
2193 ROOT_MODULE_PATH.clone(),
2194 ast::Module::new(vec![
2195 ast::VariableDefinition::new(
2196 "msvc_deps_prefix".into(),
2197 "Hinweis: Einlesen der Datei ".into()
2198 )
2199 .into(),
2200 ast_rule("foo", &[("command", "bar"), ("deps", "msvc")]).into(),
2201 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).into(),
2202 ])
2203 )]
2204 .into_iter()
2205 .collect(),
2206 &DEFAULT_DEPENDENCIES,
2207 &ROOT_MODULE_PATH
2208 )
2209 .unwrap(),
2210 create_simple_config(
2211 [(
2212 "bar".into(),
2213 ir_explicit_build(
2214 vec!["bar".into()],
2215 Rule::new("bar".into(), None).with_header_dependency(Some(
2216 HeaderDependency::Msvc {
2217 prefix: "Hinweis: Einlesen der Datei ".into()
2218 }
2219 )),
2220 vec![]
2221 )
2222 .into()
2223 )]
2224 .into_iter()
2225 .collect(),
2226 ["bar".into()].into_iter().collect()
2227 )
2228 );
2229 }
2230
2231 #[test]
2232 fn compile_msvc_header_dependency_with_rule_prefix_shadowing_global_prefix() {
2233 assert_eq!(
2234 compile(
2235 &[(
2236 ROOT_MODULE_PATH.clone(),
2237 ast::Module::new(vec![
2238 ast::VariableDefinition::new(
2239 "msvc_deps_prefix".into(),
2240 "global prefix: ".into()
2241 )
2242 .into(),
2243 ast_rule(
2244 "foo",
2245 &[
2246 ("command", "bar"),
2247 ("deps", "msvc"),
2248 ("msvc_deps_prefix", "rule prefix: ")
2249 ]
2250 )
2251 .into(),
2252 ast_explicit_build(vec!["bar".into()], "foo".into(), vec![], vec![]).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".into(), None).with_header_dependency(Some(
2267 HeaderDependency::Msvc {
2268 prefix: "rule 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_msvc_header_dependency_with_build_level_prefix_shadowing_rule_prefix() {
2284 assert_eq!(
2285 compile(
2286 &[(
2287 ROOT_MODULE_PATH.clone(),
2288 ast::Module::new(vec![
2289 ast_rule(
2290 "foo",
2291 &[
2292 ("command", "bar"),
2293 ("deps", "msvc"),
2294 ("msvc_deps_prefix", "rule prefix: ")
2295 ]
2296 )
2297 .into(),
2298 ast_explicit_build(
2299 vec!["bar".into()],
2300 "foo".into(),
2301 vec![],
2302 vec![ast::VariableDefinition::new(
2303 "msvc_deps_prefix".into(),
2304 "build prefix: ".into()
2305 )]
2306 )
2307 .into(),
2308 ])
2309 )]
2310 .into_iter()
2311 .collect(),
2312 &DEFAULT_DEPENDENCIES,
2313 &ROOT_MODULE_PATH
2314 )
2315 .unwrap(),
2316 create_simple_config(
2317 [(
2318 "bar".into(),
2319 ir_explicit_build(
2320 vec!["bar".into()],
2321 Rule::new("bar".into(), None).with_header_dependency(Some(
2322 HeaderDependency::Msvc {
2323 prefix: "build prefix: ".into()
2324 }
2325 )),
2326 vec![]
2327 )
2328 .into()
2329 )]
2330 .into_iter()
2331 .collect(),
2332 ["bar".into()].into_iter().collect()
2333 )
2334 );
2335 }
2336
2337 #[test]
2338 fn compile_rule_level_pool() {
2339 assert_eq!(
2340 compile_root_module(vec![
2341 ast::Pool::new("foo".into(), "2".into()).into(),
2342 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2343 ast_explicit_build(vec!["bar".into()], "qux".into(), vec![], vec![]).into(),
2344 ]),
2345 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2346 );
2347 }
2348
2349 #[test]
2350 fn compile_build_level_pool() {
2351 assert_eq!(
2352 compile_root_module(vec![
2353 ast::Pool::new("foo".into(), "2".into()).into(),
2354 ast_rule("qux", &[("command", "baz")]).into(),
2355 ast_explicit_build(
2356 vec!["bar".into()],
2357 "qux".into(),
2358 vec![],
2359 vec![ast::VariableDefinition::new("pool".into(), "foo".into())]
2360 )
2361 .into(),
2362 ]),
2363 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2364 );
2365 }
2366
2367 #[test]
2368 fn compile_build_level_pool_shadowing_rule_level_pool() {
2369 assert_eq!(
2370 compile_root_module(vec![
2371 ast::Pool::new("foo".into(), "2".into()).into(),
2372 ast_rule("qux", &[("command", "baz"), ("pool", "console")]).into(),
2373 ast_explicit_build(
2374 vec!["bar".into()],
2375 "qux".into(),
2376 vec![],
2377 vec![ast::VariableDefinition::new("pool".into(), "foo".into())]
2378 )
2379 .into(),
2380 ]),
2381 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2382 );
2383 }
2384
2385 #[test]
2386 fn compile_build_level_empty_pool_shadowing_rule_level_pool() {
2387 assert_eq!(
2388 compile_root_module(vec![
2389 ast_rule("qux", &[("command", "baz"), ("pool", "console")]).into(),
2390 ast_explicit_build(
2391 vec!["bar".into()],
2392 "qux".into(),
2393 vec![],
2394 vec![ast::VariableDefinition::new("pool".into(), "".into())]
2395 )
2396 .into(),
2397 ]),
2398 Ok(create_pool_config(None, &[]))
2399 );
2400 }
2401
2402 #[test]
2403 fn compile_console_pool() {
2404 assert_eq!(
2405 compile_root_module(vec![
2406 ast_rule("qux", &[("command", "baz"), ("pool", "console")]).into(),
2407 ast_explicit_build(vec!["bar".into()], "qux".into(), vec![], vec![]).into(),
2408 ]),
2409 Ok(create_pool_config(Some(Pool::Console), &[]))
2410 );
2411 }
2412
2413 #[test]
2414 fn compile_console_pool_in_phony_build() {
2415 assert_eq!(
2416 compile_root_module(vec![
2417 ast_explicit_build(
2418 vec!["foo".into()],
2419 "phony".into(),
2420 vec![],
2421 vec![ast::VariableDefinition::new(
2422 "pool".into(),
2423 "console".into()
2424 )]
2425 )
2426 .into(),
2427 ]),
2428 Ok(create_simple_config(
2429 [(
2430 "foo".into(),
2431 Build::new(vec!["foo".into()], vec![], None, vec![], vec![], None).into()
2432 )]
2433 .into_iter()
2434 .collect(),
2435 ["foo".into()].into_iter().collect()
2436 ))
2437 );
2438 }
2439
2440 #[test]
2441 fn compile_pool_with_zero_depth() {
2442 assert_eq!(
2443 compile_root_module(vec![
2444 ast::Pool::new("foo".into(), "0".into()).into(),
2445 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2446 ast_explicit_build(vec!["bar".into()], "qux".into(), vec![], vec![]).into(),
2447 ]),
2448 Ok(create_pool_config(None, &[]))
2449 );
2450 }
2451
2452 #[test]
2453 fn compile_pool_depth_with_variable() {
2454 assert_eq!(
2455 compile_root_module(vec![
2456 ast::VariableDefinition::new("x".into(), "2".into()).into(),
2457 ast::Pool::new("foo".into(), "$x".into()).into(),
2458 ast::VariableDefinition::new("x".into(), "3".into()).into(),
2459 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2460 ast_explicit_build(vec!["bar".into()], "qux".into(), vec![], vec![]).into(),
2461 ]),
2462 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2463 );
2464 }
2465
2466 #[test]
2467 fn compile_pool_depth_with_empty_variable() {
2468 assert_eq!(
2469 compile_root_module(vec![
2470 ast::VariableDefinition::new("x".into(), "".into()).into(),
2471 ast::Pool::new("foo".into(), "$x 2".into()).into(),
2472 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2473 ast_explicit_build(vec!["bar".into()], "qux".into(), vec![], vec![]).into(),
2474 ]),
2475 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2476 );
2477 }
2478
2479 #[test]
2480 fn compile_pool_name_with_variable() {
2481 assert_eq!(
2482 compile_root_module(vec![
2483 ast::Pool::new("foo".into(), "2".into()).into(),
2484 ast_rule("qux", &[("command", "baz"), ("pool", "$x")]).into(),
2485 ast_explicit_build(
2486 vec!["bar".into()],
2487 "qux".into(),
2488 vec![],
2489 vec![ast::VariableDefinition::new("x".into(), "foo".into())]
2490 )
2491 .into(),
2492 ]),
2493 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2494 );
2495 }
2496
2497 #[test]
2498 fn do_not_interpolate_out_variable_in_pool() {
2499 assert_eq!(
2500 compile_root_module(vec![
2501 ast::Pool::new("bar".into(), "2".into()).into(),
2502 ast_rule("qux", &[("command", "baz"), ("pool", "$out")]).into(),
2503 ast_explicit_build(vec!["bar".into()], "qux".into(), vec![], vec![]).into(),
2504 ]),
2505 Ok(create_pool_config(None, &[("bar", 2)]))
2506 );
2507 }
2508
2509 #[test]
2510 fn fail_to_compile_unknown_pool() {
2511 assert_eq!(
2512 compile_root_module(vec![
2513 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2514 ast_explicit_build(vec!["bar".into()], "qux".into(), vec![], vec![]).into(),
2515 ]),
2516 Err(CompileError::PoolNotFound("foo".into()))
2517 );
2518 }
2519
2520 #[test]
2521 fn fail_to_compile_unknown_pool_in_phony_build() {
2522 assert_eq!(
2523 compile_root_module(vec![
2524 ast_explicit_build(
2525 vec!["bar".into()],
2526 "phony".into(),
2527 vec![],
2528 vec![ast::VariableDefinition::new("pool".into(), "foo".into())]
2529 )
2530 .into(),
2531 ]),
2532 Err(CompileError::PoolNotFound("foo".into()))
2533 );
2534 }
2535
2536 #[test]
2537 fn fail_to_compile_pool_declared_after_build() {
2538 assert_eq!(
2539 compile_root_module(vec![
2540 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2541 ast_explicit_build(vec!["bar".into()], "qux".into(), vec![], vec![]).into(),
2542 ast::Pool::new("foo".into(), "2".into()).into(),
2543 ]),
2544 Err(CompileError::PoolNotFound("foo".into()))
2545 );
2546 }
2547
2548 #[test]
2549 fn fail_to_compile_duplicate_pool() {
2550 assert_eq!(
2551 compile_root_module(vec![
2552 ast::Pool::new("foo".into(), "1".into()).into(),
2553 ast::Pool::new("foo".into(), "2".into()).into(),
2554 ]),
2555 Err(CompileError::DuplicatePool("foo".into()))
2556 );
2557 }
2558
2559 #[test]
2560 fn fail_to_compile_duplicate_console_pool() {
2561 assert_eq!(
2562 compile_root_module(vec![ast::Pool::new("console".into(), "foo".into()).into()]),
2563 Err(CompileError::DuplicatePool("console".into()))
2564 );
2565 }
2566
2567 #[test]
2568 fn fail_to_compile_invalid_pool_depth() {
2569 for depth in ["", "foo", "-1", "1x", "99999999999999999999"] {
2570 assert_eq!(
2571 compile_root_module(vec![ast::Pool::new("foo".into(), depth.into()).into()]),
2572 Err(CompileError::InvalidPoolDepth("foo".into(), depth.into()))
2573 );
2574 }
2575 }
2576
2577 mod submodule {
2578 use super::*;
2579 use pretty_assertions::assert_eq;
2580
2581 const SUBMODULE_PATH: &str = "foo.ninja";
2582
2583 fn compile_with_submodule(
2584 statements: Vec<ast::Statement>,
2585 submodule_statements: Vec<ast::Statement>,
2586 ) -> Result<Config, CompileError> {
2587 compile(
2588 &[
2589 (ROOT_MODULE_PATH.clone(), ast::Module::new(statements)),
2590 (
2591 SUBMODULE_PATH.into(),
2592 ast::Module::new(submodule_statements),
2593 ),
2594 ]
2595 .into_iter()
2596 .collect(),
2597 &[(
2598 ROOT_MODULE_PATH.clone(),
2599 [(SUBMODULE_PATH.into(), PathBuf::from(SUBMODULE_PATH))]
2600 .into_iter()
2601 .collect(),
2602 )]
2603 .into_iter()
2604 .collect(),
2605 &ROOT_MODULE_PATH,
2606 )
2607 }
2608
2609 #[test]
2610 fn reference_variable_in_parent_module() {
2611 assert_eq!(
2612 compile(
2613 &[
2614 (
2615 ROOT_MODULE_PATH.clone(),
2616 ast::Module::new(vec![
2617 ast::VariableDefinition::new("x".into(), "42".into()).into(),
2618 ast::Submodule::new(SUBMODULE_PATH.into()).into(),
2619 ])
2620 ),
2621 (
2622 SUBMODULE_PATH.into(),
2623 ast::Module::new(vec![
2624 ast_rule("foo", &[("command", "$x")]).into(),
2625 ast_explicit_build(
2626 vec!["bar".into()],
2627 "foo".into(),
2628 vec![],
2629 vec![]
2630 )
2631 .into()
2632 ])
2633 )
2634 ]
2635 .into_iter()
2636 .collect(),
2637 &[(
2638 ROOT_MODULE_PATH.clone(),
2639 [(SUBMODULE_PATH.into(), PathBuf::from(SUBMODULE_PATH))]
2640 .into_iter()
2641 .collect()
2642 )]
2643 .into_iter()
2644 .collect(),
2645 &ROOT_MODULE_PATH
2646 )
2647 .unwrap(),
2648 create_simple_config(
2649 [(
2650 "bar".into(),
2651 ir_explicit_build(vec!["bar".into()], Rule::new("42".into(), None), vec![])
2652 .into()
2653 )]
2654 .into_iter()
2655 .collect(),
2656 ["bar".into()].into_iter().collect()
2657 )
2658 );
2659 }
2660
2661 #[test]
2662 fn reference_rule_in_parent_module() {
2663 assert_eq!(
2664 compile(
2665 &[
2666 (
2667 ROOT_MODULE_PATH.clone(),
2668 ast::Module::new(vec![
2669 ast::VariableDefinition::new("x".into(), "42".into()).into(),
2670 ast_rule("foo", &[("command", "$x")]).into(),
2671 ast::Submodule::new(SUBMODULE_PATH.into()).into(),
2672 ])
2673 ),
2674 (
2675 SUBMODULE_PATH.into(),
2676 ast::Module::new(vec![
2677 ast_explicit_build(
2678 vec!["bar".into()],
2679 "foo".into(),
2680 vec![],
2681 vec![]
2682 )
2683 .into()
2684 ])
2685 )
2686 ]
2687 .into_iter()
2688 .collect(),
2689 &[(
2690 ROOT_MODULE_PATH.clone(),
2691 [(SUBMODULE_PATH.into(), PathBuf::from(SUBMODULE_PATH))]
2692 .into_iter()
2693 .collect()
2694 )]
2695 .into_iter()
2696 .collect(),
2697 &ROOT_MODULE_PATH
2698 )
2699 .unwrap(),
2700 create_simple_config(
2701 [(
2702 "bar".into(),
2703 ir_explicit_build(vec!["bar".into()], Rule::new("42".into(), None), vec![])
2704 .into()
2705 )]
2706 .into_iter()
2707 .collect(),
2708 ["bar".into()].into_iter().collect()
2709 )
2710 );
2711 }
2712
2713 #[test]
2714 fn do_not_overwrite_variable_in_parent_module() {
2715 assert_eq!(
2716 compile(
2717 &[
2718 (
2719 ROOT_MODULE_PATH.clone(),
2720 ast::Module::new(vec![
2721 ast::VariableDefinition::new("x".into(), "42".into()).into(),
2722 ast_rule("foo", &[("command", "$x")]).into(),
2723 ast::Submodule::new(SUBMODULE_PATH.into()).into(),
2724 ast_explicit_build(
2725 vec!["bar".into()],
2726 "foo".into(),
2727 vec![],
2728 vec![]
2729 )
2730 .into(),
2731 ])
2732 ),
2733 (
2734 SUBMODULE_PATH.into(),
2735 ast::Module::new(vec![
2736 ast::VariableDefinition::new("x".into(), "13".into()).into(),
2737 ])
2738 )
2739 ]
2740 .into_iter()
2741 .collect(),
2742 &[(
2743 ROOT_MODULE_PATH.clone(),
2744 [(SUBMODULE_PATH.into(), PathBuf::from(SUBMODULE_PATH))]
2745 .into_iter()
2746 .collect()
2747 )]
2748 .into_iter()
2749 .collect(),
2750 &ROOT_MODULE_PATH
2751 )
2752 .unwrap(),
2753 create_simple_config(
2754 [(
2755 "bar".into(),
2756 ir_explicit_build(vec!["bar".into()], Rule::new("42".into(), None), vec![])
2757 .into()
2758 )]
2759 .into_iter()
2760 .collect(),
2761 ["bar".into()].into_iter().collect()
2762 )
2763 );
2764 }
2765
2766 #[test]
2767 fn reference_pool_in_parent_module() {
2768 assert_eq!(
2769 compile_with_submodule(
2770 vec![
2771 ast::Pool::new("foo".into(), "2".into()).into(),
2772 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2773 ast::Submodule::new(SUBMODULE_PATH.into()).into(),
2774 ],
2775 vec![
2776 ast_explicit_build(vec!["bar".into()], "qux".into(), vec![], vec![]).into()
2777 ],
2778 ),
2779 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2780 );
2781 }
2782
2783 #[test]
2784 fn reference_pool_in_child_module() {
2785 assert_eq!(
2786 compile_with_submodule(
2787 vec![
2788 ast::Submodule::new(SUBMODULE_PATH.into()).into(),
2789 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2790 ast_explicit_build(vec!["bar".into()], "qux".into(), vec![], vec![]).into(),
2791 ],
2792 vec![ast::Pool::new("foo".into(), "2".into()).into()],
2793 ),
2794 Ok(create_pool_config(limited_pool("foo"), &[("foo", 2)]))
2795 );
2796 }
2797
2798 #[test]
2799 fn fail_to_reference_pool_declared_after_submodule() {
2800 assert_eq!(
2801 compile_with_submodule(
2802 vec![
2803 ast_rule("qux", &[("command", "baz"), ("pool", "foo")]).into(),
2804 ast::Submodule::new(SUBMODULE_PATH.into()).into(),
2805 ast::Pool::new("foo".into(), "2".into()).into(),
2806 ],
2807 vec![
2808 ast_explicit_build(vec!["bar".into()], "qux".into(), vec![], vec![]).into()
2809 ],
2810 ),
2811 Err(CompileError::PoolNotFound("foo".into()))
2812 );
2813 }
2814
2815 #[test]
2816 fn fail_to_declare_pool_in_submodule_referenced_twice() {
2817 assert_eq!(
2818 compile_with_submodule(
2819 vec![
2820 ast::Submodule::new(SUBMODULE_PATH.into()).into(),
2821 ast::Submodule::new(SUBMODULE_PATH.into()).into(),
2822 ],
2823 vec![ast::Pool::new("foo".into(), "2".into()).into()],
2824 ),
2825 Err(CompileError::DuplicatePool("foo".into()))
2826 );
2827 }
2828 }
2829}