1use itertools::Itertools;
2use serde::{Deserialize, Deserializer, Serialize};
3use std::{
4 collections::{BTreeMap, HashSet},
5 path::PathBuf,
6 sync::Arc,
7};
8use strum::{Display, EnumString};
9use sway_ir::{PassManager, PrintPassesOpts};
10
11#[derive(
12 Clone,
13 Copy,
14 Debug,
15 Display,
16 Default,
17 Eq,
18 PartialEq,
19 Hash,
20 Serialize,
21 Deserialize,
22 clap::ValueEnum,
23 EnumString,
24)]
25pub enum BuildTarget {
26 #[default]
27 #[serde(rename = "fuel")]
28 #[clap(name = "fuel")]
29 #[strum(serialize = "fuel")]
30 Fuel,
31 #[serde(rename = "evm")]
32 #[clap(name = "evm")]
33 #[strum(serialize = "evm")]
34 EVM,
35}
36
37impl BuildTarget {
38 pub const CFG: &'static [&'static str] = &["evm", "fuel"];
39}
40
41#[derive(Default, Clone, Copy)]
42pub enum DbgGeneration {
43 Full,
44 #[default]
45 None,
46}
47
48#[derive(Serialize, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
49pub enum OptLevel {
50 #[default]
51 Opt0 = 0,
52 Opt1 = 1,
53}
54
55impl<'de> serde::Deserialize<'de> for OptLevel {
56 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
57 let num = u8::deserialize(d)?;
58 match num {
59 0 => Ok(OptLevel::Opt0),
60 1 => Ok(OptLevel::Opt1),
61 _ => Err(serde::de::Error::custom(format!("invalid opt level {num}"))),
62 }
63 }
64}
65
66#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, PartialEq, Eq)]
68pub struct PrintAsm {
69 #[serde(rename = "virtual")]
70 pub virtual_abstract: bool,
71 #[serde(rename = "allocated")]
72 pub allocated_abstract: bool,
73 pub r#final: bool,
74}
75
76impl PrintAsm {
77 pub fn all() -> Self {
78 Self {
79 virtual_abstract: true,
80 allocated_abstract: true,
81 r#final: true,
82 }
83 }
84
85 pub fn abstract_virtual() -> Self {
86 Self {
87 virtual_abstract: true,
88 ..Self::default()
89 }
90 }
91
92 pub fn abstract_allocated() -> Self {
93 Self {
94 allocated_abstract: true,
95 ..Self::default()
96 }
97 }
98
99 pub fn r#final() -> Self {
100 Self {
101 r#final: true,
102 ..Self::default()
103 }
104 }
105}
106
107impl std::ops::BitOrAssign for PrintAsm {
108 fn bitor_assign(&mut self, rhs: Self) {
109 self.virtual_abstract |= rhs.virtual_abstract;
110 self.allocated_abstract |= rhs.allocated_abstract;
111 self.r#final |= rhs.r#final;
112 }
113}
114
115#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
117pub struct PrintIr {
118 pub initial: bool,
119 pub r#final: bool,
120 #[serde(rename = "modified")]
121 pub modified_only: bool,
122 pub passes: Vec<String>,
123}
124
125impl Default for PrintIr {
126 fn default() -> Self {
127 Self {
128 initial: false,
129 r#final: false,
130 modified_only: true, passes: vec![],
132 }
133 }
134}
135
136impl PrintIr {
137 pub fn all(modified_only: bool) -> Self {
138 Self {
139 initial: true,
140 r#final: true,
141 modified_only,
142 passes: PassManager::OPTIMIZATION_PASSES
143 .iter()
144 .map(|pass| pass.to_string())
145 .collect_vec(),
146 }
147 }
148
149 pub fn r#final() -> Self {
150 Self {
151 r#final: true,
152 ..Self::default()
153 }
154 }
155}
156
157impl std::ops::BitOrAssign for PrintIr {
158 fn bitor_assign(&mut self, rhs: Self) {
159 self.initial |= rhs.initial;
160 self.r#final |= rhs.r#final;
161 self.modified_only &= rhs.modified_only;
166 for pass in rhs.passes {
167 if !self.passes.contains(&pass) {
168 self.passes.push(pass);
169 }
170 }
171 }
172}
173
174impl From<&PrintIr> for PrintPassesOpts {
175 fn from(value: &PrintIr) -> Self {
176 Self {
177 initial: value.initial,
178 r#final: value.r#final,
179 modified_only: value.modified_only,
180 passes: HashSet::from_iter(value.passes.iter().cloned()),
181 }
182 }
183}
184
185#[derive(Clone)]
187pub struct BuildConfig {
188 pub(crate) build_target: BuildTarget,
190 pub(crate) dbg_generation: DbgGeneration,
191 pub(crate) canonical_root_module: Arc<PathBuf>,
194 pub(crate) print_dca_graph: Option<String>,
195 pub(crate) print_dca_graph_url_format: Option<String>,
196 pub(crate) print_asm: PrintAsm,
197 pub(crate) print_bytecode: bool,
198 pub(crate) print_bytecode_spans: bool,
199 pub(crate) print_ir: PrintIr,
200 pub(crate) include_tests: bool,
201 pub(crate) optimization_level: OptLevel,
202 pub time_phases: bool,
203 pub profile: bool,
204 pub metrics_outfile: Option<String>,
205 pub lsp_mode: Option<LspConfig>,
206}
207
208impl BuildConfig {
209 pub fn root_from_file_name_and_manifest_path(
218 root_module: PathBuf,
219 canonical_manifest_dir: PathBuf,
220 build_target: BuildTarget,
221 dbg_generation: DbgGeneration,
222 ) -> Self {
223 assert!(
224 canonical_manifest_dir.has_root(),
225 "manifest dir must be a canonical path",
226 );
227 let canonical_root_module = match root_module.has_root() {
228 true => root_module,
229 false => {
230 assert!(
231 root_module.starts_with(canonical_manifest_dir.file_name().unwrap()),
232 "file_name must be either absolute or relative to manifest directory",
233 );
234 canonical_manifest_dir
235 .parent()
236 .expect("unable to retrieve manifest directory parent")
237 .join(&root_module)
238 }
239 };
240 Self {
241 build_target,
242 dbg_generation,
243 canonical_root_module: Arc::new(canonical_root_module),
244 print_dca_graph: None,
245 print_dca_graph_url_format: None,
246 print_asm: PrintAsm::default(),
247 print_bytecode: false,
248 print_bytecode_spans: false,
249 print_ir: PrintIr::default(),
250 include_tests: false,
251 time_phases: false,
252 profile: false,
253 metrics_outfile: None,
254 optimization_level: OptLevel::Opt0,
255 lsp_mode: None,
256 }
257 }
258
259 pub fn with_print_dca_graph(self, a: Option<String>) -> Self {
260 Self {
261 print_dca_graph: a,
262 ..self
263 }
264 }
265
266 pub fn with_print_dca_graph_url_format(self, a: Option<String>) -> Self {
267 Self {
268 print_dca_graph_url_format: a,
269 ..self
270 }
271 }
272
273 pub fn with_print_asm(self, print_asm: PrintAsm) -> Self {
274 Self { print_asm, ..self }
275 }
276
277 pub fn with_print_bytecode(self, bytecode: bool, bytecode_spans: bool) -> Self {
278 Self {
279 print_bytecode: bytecode,
280 print_bytecode_spans: bytecode_spans,
281 ..self
282 }
283 }
284
285 pub fn with_print_ir(self, a: PrintIr) -> Self {
286 Self {
287 print_ir: a,
288 ..self
289 }
290 }
291
292 pub fn with_time_phases(self, a: bool) -> Self {
293 Self {
294 time_phases: a,
295 ..self
296 }
297 }
298
299 pub fn with_profile(self, a: bool) -> Self {
300 Self { profile: a, ..self }
301 }
302
303 pub fn with_metrics(self, a: Option<String>) -> Self {
304 Self {
305 metrics_outfile: a,
306 ..self
307 }
308 }
309
310 pub fn with_optimization_level(self, optimization_level: OptLevel) -> Self {
311 Self {
312 optimization_level,
313 ..self
314 }
315 }
316
317 pub fn with_include_tests(self, include_tests: bool) -> Self {
323 Self {
324 include_tests,
325 ..self
326 }
327 }
328
329 pub fn with_lsp_mode(self, lsp_mode: Option<LspConfig>) -> Self {
330 Self { lsp_mode, ..self }
331 }
332
333 pub fn canonical_root_module(&self) -> Arc<PathBuf> {
334 self.canonical_root_module.clone()
335 }
336}
337
338#[derive(Clone, Debug, Default)]
339pub struct LspConfig {
340 pub optimized_build: bool,
345 pub file_versions: BTreeMap<PathBuf, Option<u64>>,
348}
349
350#[cfg(test)]
351mod test {
352 use super::*;
353 #[test]
354 fn test_root_from_file_name_and_manifest_path() {
355 let root_module = PathBuf::from("mock_path/src/main.sw");
356 let canonical_manifest_dir = PathBuf::from("/tmp/sway_project/mock_path");
357 BuildConfig::root_from_file_name_and_manifest_path(
358 root_module,
359 canonical_manifest_dir,
360 BuildTarget::default(),
361 DbgGeneration::Full,
362 );
363 }
364
365 #[test]
366 fn test_root_from_file_name_and_manifest_path_contains_dot() {
367 let root_module = PathBuf::from("mock_path_contains_._dot/src/main.sw");
368 let canonical_manifest_dir = PathBuf::from("/tmp/sway_project/mock_path_contains_._dot");
369 BuildConfig::root_from_file_name_and_manifest_path(
370 root_module,
371 canonical_manifest_dir,
372 BuildTarget::default(),
373 DbgGeneration::Full,
374 );
375 }
376}