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