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