1pub(crate) mod compiler;
2
3use std::{collections::BTreeMap, fs, path::Path};
4
5use serde::Deserialize;
6
7use compiler::{
8 identify_assembler, identify_compiler,
10 identify_linker,
11 Assembler,
12 Compiler,
13 ExeLinker,
14};
15
16#[derive(Debug, Deserialize)]
17pub struct ToolchainFile {
18 msvc_platforms: Option<Vec<String>>,
19 c_compiler: Option<Vec<String>>,
20 cpp_compiler: Option<Vec<String>>,
21 nasm_assembler: Option<Vec<String>>,
22 static_linker: Option<Vec<String>>,
23 exe_linker: Option<Vec<String>>,
24 profile: Option<BTreeMap<String, Profile>>,
25 }
27
28#[derive(Default)]
29pub struct Toolchain {
30 pub msvc_platforms: Vec<String>,
31 pub c_compiler: Option<Box<dyn Compiler>>,
32 pub cpp_compiler: Option<Box<dyn Compiler>>,
33 pub nasm_assembler: Option<Box<dyn Assembler>>,
34 pub static_linker: Option<Vec<String>>,
35 pub exe_linker: Option<Box<dyn ExeLinker>>,
36 pub profile: BTreeMap<String, Profile>,
37}
38
39#[derive(Clone, Debug, Default, Deserialize)]
40pub struct Profile {
41 #[serde(default)]
42 pub c_compile_flags: Vec<String>,
43 #[serde(default)]
44 pub cpp_compile_flags: Vec<String>,
45 #[serde(default)]
46 pub nasm_assemble_flags: Vec<String>,
47 pub vcxproj: Option<VcxprojProfile>,
48}
49
50#[derive(Clone, Debug, Default, Deserialize)]
51pub struct VcxprojProfile {
52 pub preprocessor_definitions: Vec<String>,
53 pub property_group: BTreeMap<String, String>,
54 pub cl_compile: BTreeMap<String, String>,
55 pub link: BTreeMap<String, String>,
56}
57
58pub fn get_toolchain(toolchain_path: &Path, for_msvc: bool) -> Result<Toolchain, String> {
59 let toolchain_toml = match fs::read_to_string(toolchain_path) {
60 Ok(x) => x,
61 Err(e) => return Err(format!("Error opening toolchain file \"{}\": {}", toolchain_path.display(), e)),
62 };
63
64 let toolchain_file = match toml::from_str::<ToolchainFile>(&toolchain_toml) {
65 Ok(x) => x,
66 Err(e) => return Err(format!("Error reading toolchain file \"{}\": {}", toolchain_path.display(), e)),
67 };
68
69 let msvc_platforms = toolchain_file.msvc_platforms.unwrap_or_default();
70
71 let nasm_assembler = match toolchain_file.nasm_assembler {
72 Some(x) => match identify_assembler(x) {
73 Ok(y) => Some(y),
74 Err(e) => return Err(format!("Error identifying NASM assembler: {}", e)),
75 },
76 None => None,
77 };
78 let c_compiler: Option<Box<dyn Compiler>> = if for_msvc {
79 Some(compiler::msvc_compiler())
80 } else {
81 match toolchain_file.c_compiler {
82 Some(x) => match identify_compiler(x) {
83 Ok(y) => Some(y),
84 Err(e) => return Err(format!("Error identifying C compiler: {}", e)),
85 },
86 None => None,
87 }
88 };
89 let cpp_compiler: Option<Box<dyn Compiler>> = if for_msvc {
90 Some(compiler::msvc_compiler())
91 } else {
92 match toolchain_file.cpp_compiler {
93 Some(x) => match identify_compiler(x) {
94 Ok(y) => Some(y),
95 Err(e) => return Err(format!("Error identifying C++ compiler: {}", e)),
96 },
97 None => None,
98 }
99 };
100 let static_linker = toolchain_file.static_linker;
101
102 let exe_linker = match toolchain_file.exe_linker {
103 Some(x) => match identify_linker(x) {
104 Ok(linker) => Some(linker),
105 Err(e) => return Err(format!("Error identifying linker: {}", e)),
106 },
107 None => None,
108 };
109
110 let profile = toolchain_file.profile.unwrap_or_default();
111
112 if let Some(ref c_compiler) = c_compiler {
114 if c_compiler.position_independent_code_flag().is_none() {
115 log::info!("position_idependent_code not supported by the specified C compiler");
116 }
117 }
118 if let Some(ref cpp_compiler) = cpp_compiler {
119 if cpp_compiler.position_independent_code_flag().is_none() {
120 log::info!("position_idependent_code not supported by the specified C++ compiler");
121 }
122 }
123
124 let toolchain = Toolchain {
125 msvc_platforms,
126 nasm_assembler,
127 c_compiler,
128 cpp_compiler,
129 static_linker,
130 exe_linker,
131 profile,
132 };
133
134 Ok(toolchain)
135}