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, VerifyPassesOpts};
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 none() -> Self {
86 Self::default()
87 }
88
89 pub fn abstract_virtual() -> Self {
90 Self {
91 virtual_abstract: true,
92 ..Self::default()
93 }
94 }
95
96 pub fn abstract_allocated() -> Self {
97 Self {
98 allocated_abstract: true,
99 ..Self::default()
100 }
101 }
102
103 pub fn r#final() -> Self {
104 Self {
105 r#final: true,
106 ..Self::default()
107 }
108 }
109}
110
111impl std::ops::BitOrAssign for PrintAsm {
112 fn bitor_assign(&mut self, rhs: Self) {
113 self.virtual_abstract |= rhs.virtual_abstract;
114 self.allocated_abstract |= rhs.allocated_abstract;
115 self.r#final |= rhs.r#final;
116 }
117}
118
119#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
121pub struct IrCli {
122 pub initial: bool,
123 pub r#final: bool,
124 #[serde(rename = "modified")]
125 pub modified_only: bool,
126 pub passes: Vec<String>,
127}
128
129impl Default for IrCli {
130 fn default() -> Self {
131 Self {
132 initial: false,
133 r#final: false,
134 modified_only: true, passes: vec![],
136 }
137 }
138}
139
140impl IrCli {
141 pub fn all(modified_only: bool) -> Self {
142 Self {
143 initial: true,
144 r#final: true,
145 modified_only,
146 passes: PassManager::OPTIMIZATION_PASSES
147 .iter()
148 .map(|pass| pass.to_string())
149 .collect_vec(),
150 }
151 }
152
153 pub fn none() -> Self {
154 Self::default()
155 }
156
157 pub fn r#final() -> Self {
158 Self {
159 r#final: true,
160 ..Self::default()
161 }
162 }
163}
164
165impl std::ops::BitOrAssign for IrCli {
166 fn bitor_assign(&mut self, rhs: Self) {
167 self.initial |= rhs.initial;
168 self.r#final |= rhs.r#final;
169 self.modified_only &= rhs.modified_only;
174 for pass in rhs.passes {
175 if !self.passes.contains(&pass) {
176 self.passes.push(pass);
177 }
178 }
179 }
180}
181
182impl From<&IrCli> for PrintPassesOpts {
183 fn from(value: &IrCli) -> Self {
184 Self {
185 initial: value.initial,
186 r#final: value.r#final,
187 modified_only: value.modified_only,
188 passes: HashSet::from_iter(value.passes.iter().cloned()),
189 }
190 }
191}
192
193impl From<&IrCli> for VerifyPassesOpts {
194 fn from(value: &IrCli) -> Self {
195 Self {
196 initial: value.initial,
197 r#final: value.r#final,
198 modified_only: value.modified_only,
199 passes: HashSet::from_iter(value.passes.iter().cloned()),
200 }
201 }
202}
203
204#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
205#[serde(rename_all = "snake_case")]
206pub enum Backtrace {
207 All,
208 #[default]
209 AllExceptNever,
210 OnlyAlways,
211 None,
212}
213
214impl From<Backtrace> for sway_ir::Backtrace {
215 fn from(value: Backtrace) -> Self {
216 match value {
217 Backtrace::All => sway_ir::Backtrace::All,
218 Backtrace::AllExceptNever => sway_ir::Backtrace::AllExceptNever,
219 Backtrace::OnlyAlways => sway_ir::Backtrace::OnlyAlways,
220 Backtrace::None => sway_ir::Backtrace::None,
221 }
222 }
223}
224
225#[derive(Clone)]
227pub struct BuildConfig {
228 pub(crate) build_target: BuildTarget,
230 pub(crate) dbg_generation: DbgGeneration,
231 pub(crate) canonical_root_module: Arc<PathBuf>,
234 pub(crate) print_dca_graph: Option<String>,
235 pub(crate) print_dca_graph_url_format: Option<String>,
236 pub(crate) print_asm: PrintAsm,
237 pub(crate) print_bytecode: bool,
238 pub(crate) print_bytecode_spans: bool,
239 pub(crate) print_ir: IrCli,
240 pub(crate) verify_ir: IrCli,
241 pub(crate) include_tests: bool,
242 pub(crate) optimization_level: OptLevel,
243 pub(crate) backtrace: Backtrace,
244 pub time_phases: bool,
245 pub profile: bool,
246 pub metrics_outfile: Option<String>,
247 pub lsp_mode: Option<LspConfig>,
248}
249
250impl BuildConfig {
251 pub fn root_from_file_name_and_manifest_path(
260 root_module: PathBuf,
261 canonical_manifest_dir: PathBuf,
262 build_target: BuildTarget,
263 dbg_generation: DbgGeneration,
264 ) -> Self {
265 assert!(
266 canonical_manifest_dir.has_root(),
267 "manifest dir must be a canonical path",
268 );
269 let canonical_root_module = match root_module.has_root() {
270 true => root_module,
271 false => {
272 assert!(
273 root_module.starts_with(canonical_manifest_dir.file_name().unwrap()),
274 "file_name must be either absolute or relative to manifest directory",
275 );
276 canonical_manifest_dir
277 .parent()
278 .expect("unable to retrieve manifest directory parent")
279 .join(&root_module)
280 }
281 };
282 Self {
283 build_target,
284 dbg_generation,
285 canonical_root_module: Arc::new(canonical_root_module),
286 print_dca_graph: None,
287 print_dca_graph_url_format: None,
288 print_asm: PrintAsm::default(),
289 print_bytecode: false,
290 print_bytecode_spans: false,
291 print_ir: IrCli::default(),
292 verify_ir: IrCli::default(),
293 include_tests: false,
294 time_phases: false,
295 profile: false,
296 metrics_outfile: None,
297 optimization_level: OptLevel::default(),
298 backtrace: Backtrace::default(),
299 lsp_mode: None,
300 }
301 }
302
303 pub fn dummy_for_asm_generation() -> Self {
306 Self::root_from_file_name_and_manifest_path(
307 PathBuf::from("/"),
308 PathBuf::from("/"),
309 BuildTarget::default(),
310 DbgGeneration::None,
311 )
312 }
313
314 pub fn with_print_dca_graph(self, a: Option<String>) -> Self {
315 Self {
316 print_dca_graph: a,
317 ..self
318 }
319 }
320
321 pub fn with_print_dca_graph_url_format(self, a: Option<String>) -> Self {
322 Self {
323 print_dca_graph_url_format: a,
324 ..self
325 }
326 }
327
328 pub fn with_print_asm(self, print_asm: PrintAsm) -> Self {
329 Self { print_asm, ..self }
330 }
331
332 pub fn with_print_bytecode(self, bytecode: bool, bytecode_spans: bool) -> Self {
333 Self {
334 print_bytecode: bytecode,
335 print_bytecode_spans: bytecode_spans,
336 ..self
337 }
338 }
339
340 pub fn with_print_ir(self, a: IrCli) -> Self {
341 Self {
342 print_ir: a,
343 ..self
344 }
345 }
346
347 pub fn with_verify_ir(self, a: IrCli) -> Self {
348 Self {
349 verify_ir: a,
350 ..self
351 }
352 }
353
354 pub fn with_time_phases(self, a: bool) -> Self {
355 Self {
356 time_phases: a,
357 ..self
358 }
359 }
360
361 pub fn with_profile(self, a: bool) -> Self {
362 Self { profile: a, ..self }
363 }
364
365 pub fn with_metrics(self, a: Option<String>) -> Self {
366 Self {
367 metrics_outfile: a,
368 ..self
369 }
370 }
371
372 pub fn with_optimization_level(self, optimization_level: OptLevel) -> Self {
373 Self {
374 optimization_level,
375 ..self
376 }
377 }
378
379 pub fn with_backtrace(self, backtrace: Backtrace) -> Self {
380 Self { backtrace, ..self }
381 }
382
383 pub fn with_include_tests(self, include_tests: bool) -> Self {
389 Self {
390 include_tests,
391 ..self
392 }
393 }
394
395 pub fn with_lsp_mode(self, lsp_mode: Option<LspConfig>) -> Self {
396 Self { lsp_mode, ..self }
397 }
398
399 pub fn canonical_root_module(&self) -> Arc<PathBuf> {
400 self.canonical_root_module.clone()
401 }
402}
403
404#[derive(Clone, Debug, Default)]
405pub struct LspConfig {
406 pub optimized_build: bool,
411 pub file_versions: BTreeMap<PathBuf, Option<u64>>,
414}
415
416#[cfg(test)]
417mod test {
418 use super::*;
419 #[test]
420 fn test_root_from_file_name_and_manifest_path() {
421 let root_module = PathBuf::from("mock_path/src/main.sw");
422 let canonical_manifest_dir = PathBuf::from("/tmp/sway_project/mock_path");
423 BuildConfig::root_from_file_name_and_manifest_path(
424 root_module,
425 canonical_manifest_dir,
426 BuildTarget::default(),
427 DbgGeneration::Full,
428 );
429 }
430
431 #[test]
432 fn test_root_from_file_name_and_manifest_path_contains_dot() {
433 let root_module = PathBuf::from("mock_path_contains_._dot/src/main.sw");
434 let canonical_manifest_dir = PathBuf::from("/tmp/sway_project/mock_path_contains_._dot");
435 BuildConfig::root_from_file_name_and_manifest_path(
436 root_module,
437 canonical_manifest_dir,
438 BuildTarget::default(),
439 DbgGeneration::Full,
440 );
441 }
442}