Skip to main content

turtle_build/
compile.rs

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";
35// Matches the default prefix of `cl.exe`'s own `/showIncludes` output.
36const 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
41/// Compiles modules.
42pub 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}