catapult/
toolchain.rs

1pub(crate) mod compiler;
2
3use std::{collections::BTreeMap, fs, path::Path};
4
5use serde::Deserialize;
6
7use compiler::{
8	identify_assembler, //
9	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	// env: Option<HashMap<String, String>>
26}
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	// Sanity checks
113	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}