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