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