1
use std::path::Path;
2

            
3
use serde::Deserialize;
4
use serde::Serialize;
5

            
6
use super::commands::arguments::clang_args;
7
use super::commands::arguments::Argument;
8
use super::commands::arguments::Arguments;
9
use crate::cache::CompilersMetadata;
10
use crate::{
11
    cache::EnvVars,
12
    project_model::{compiler::CppCompiler, ZorkModel},
13
};
14

            
15
/// Convenient datastructure to hold the common args for all the [`super::commands::command_lines::SourceCommandLine`]
16
/// once they are initialized and stored on the cache, so we just move them once (into this type)
17
/// and we can pass the struct around to the executors
18
2
#[derive(Serialize, Deserialize, Default, Debug)]
19
pub struct FlyweightData<'a> {
20
    pub general_args: Arguments<'a>,
21
    pub shared_args: Arguments<'a>,
22
    pub std_references: Arguments<'a>, // the correct format of explicitly add the std modular libs
23
    // to the compiler
24
    pub compile_but_dont_link: [Argument<'a>; 1],
25
    pub env_vars: EnvVars,
26
}
27

            
28
impl<'a> FlyweightData<'a> {
29
2
    pub fn new(program_data: &'a ZorkModel, compilers_metadata: &CompilersMetadata) -> Self {
30
2
        let mut general_args = Arguments::default();
31
2
        general_args.push(program_data.compiler.language_level_arg());
32
2
        general_args.extend_from_slice(&program_data.compiler.extra_args);
33

            
34
4
        let (shared_args, std_references, env_vars) = match program_data.compiler.cpp_compiler {
35
            CppCompiler::CLANG => {
36
1
                let shared_args = generate_clang_flyweight_args(program_data, compilers_metadata);
37
1
                (
38
1
                    shared_args.0,
39
1
                    shared_args.1,
40
1
                    &compilers_metadata.msvc.env_vars,
41
                )
42
1
            }
43
            CppCompiler::MSVC => {
44
                let shared_args = generate_msvc_flyweight_args(program_data, compilers_metadata);
45
                (
46
                    shared_args.0,
47
                    shared_args.1,
48
                    &compilers_metadata.msvc.env_vars,
49
                )
50
            }
51
            CppCompiler::GCC => {
52
1
                let shared_args = generate_gcc_flyweight_args();
53
1
                (
54
1
                    shared_args.0,
55
1
                    shared_args.1,
56
1
                    &compilers_metadata.msvc.env_vars,
57
                )
58
1
            }
59
        };
60

            
61
        let compile_but_dont_link: [Argument; 1] =
62
4
            [Argument::from(match program_data.compiler.cpp_compiler {
63
2
                CppCompiler::CLANG | CppCompiler::GCC => "-c",
64
                CppCompiler::MSVC => "/c",
65
            })];
66

            
67
2
        Self {
68
2
            general_args,
69
            shared_args,
70
            std_references,
71
            compile_but_dont_link,
72
2
            env_vars: env_vars.clone(),
73
        }
74
2
    }
75
}
76

            
77
fn generate_msvc_flyweight_args<'a>(
78
    program_data: &ZorkModel<'_>,
79
    compilers_metadata: &CompilersMetadata<'_>,
80
) -> SharedArgsStdRefsTuple<'a> {
81
    let out_dir: &Path = program_data.build.output_dir.as_ref();
82
    let mut compiler_flyweight_args = Arguments::default();
83
    let mut std_references = Arguments::default();
84

            
85
    compiler_flyweight_args.push("/EHsc"); // exception_handling_model
86
    compiler_flyweight_args.push("/nologo");
87

            
88
    compiler_flyweight_args.push("/ifcSearchDir");
89
    compiler_flyweight_args.push(
90
        out_dir
91
            .join(program_data.compiler.cpp_compiler.as_ref())
92
            .join("modules")
93
            .join("interfaces"),
94
    );
95

            
96
    std_references.push("/reference");
97
    std_references.push(compilers_metadata.msvc.stdlib_bmi_path.clone());
98
    std_references.push("/reference");
99
    std_references.push(compilers_metadata.msvc.ccompat_stdlib_bmi_path.clone());
100

            
101
    (compiler_flyweight_args, std_references)
102
}
103

            
104
type SharedArgsStdRefsTuple<'a> = (Arguments<'a>, Arguments<'a>);
105

            
106
1
fn generate_clang_flyweight_args<'a>(
107
    program_data: &'a ZorkModel<'_>,
108
    compilers_metadata: &CompilersMetadata<'_>,
109
) -> SharedArgsStdRefsTuple<'a> {
110
1
    let mut compiler_flyweight_args = Arguments::default();
111
1
    let mut std_references = Arguments::default();
112

            
113
1
    let out_dir: &Path = program_data.build.output_dir.as_ref();
114
1
    let clang_metadata = &compilers_metadata.clang;
115
1
    let major = clang_metadata.major;
116

            
117
1
    compiler_flyweight_args.push_opt(program_data.compiler.stdlib_arg());
118
1
    compiler_flyweight_args.push(clang_args::add_prebuilt_module_path(out_dir));
119

            
120
1
    if major <= 17 {
121
1
        compiler_flyweight_args.push("-fimplicit-modules");
122
1
        compiler_flyweight_args.push(clang_args::implicit_module_map(out_dir));
123
    } else {
124
        std_references.push(format!(
125
            "-fmodule-file=std={}",
126
            clang_metadata.stdlib_pcm.display()
127
        ));
128
        std_references.push(format!(
129
            "-fmodule-file=std.compat={}",
130
            clang_metadata.ccompat_pcm.display()
131
        ));
132
    }
133

            
134
1
    (compiler_flyweight_args, std_references)
135
1
}
136

            
137
1
fn generate_gcc_flyweight_args<'a>() -> SharedArgsStdRefsTuple<'a> {
138
1
    let mut compiler_flyweight_args = Arguments::default();
139
1
    compiler_flyweight_args.push("-fmodules-ts");
140
1
    (compiler_flyweight_args, Arguments::default())
141
1
}