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
37#[derive(Serialize, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
38pub enum OptLevel {
39 #[default]
40 Opt0 = 0,
41 Opt1 = 1,
42}
43
44impl<'de> serde::Deserialize<'de> for OptLevel {
45 fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
46 let num = u8::deserialize(d)?;
47 match num {
48 0 => Ok(OptLevel::Opt0),
49 1 => Ok(OptLevel::Opt1),
50 _ => Err(serde::de::Error::custom(format!("invalid opt level {num}"))),
51 }
52 }
53}
54
55#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, PartialEq, Eq)]
57pub struct PrintAsm {
58 #[serde(rename = "virtual")]
59 pub virtual_abstract: bool,
60 #[serde(rename = "allocated")]
61 pub allocated_abstract: bool,
62 pub r#final: bool,
63}
64
65impl PrintAsm {
66 pub fn all() -> Self {
67 Self {
68 virtual_abstract: true,
69 allocated_abstract: true,
70 r#final: true,
71 }
72 }
73
74 pub fn abstract_virtual() -> Self {
75 Self {
76 virtual_abstract: true,
77 ..Self::default()
78 }
79 }
80
81 pub fn abstract_allocated() -> Self {
82 Self {
83 allocated_abstract: true,
84 ..Self::default()
85 }
86 }
87
88 pub fn r#final() -> Self {
89 Self {
90 r#final: true,
91 ..Self::default()
92 }
93 }
94}
95
96impl std::ops::BitOrAssign for PrintAsm {
97 fn bitor_assign(&mut self, rhs: Self) {
98 self.virtual_abstract |= rhs.virtual_abstract;
99 self.allocated_abstract |= rhs.allocated_abstract;
100 self.r#final |= rhs.r#final;
101 }
102}
103
104#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
106pub struct PrintIr {
107 pub initial: bool,
108 pub r#final: bool,
109 #[serde(rename = "modified")]
110 pub modified_only: bool,
111 pub passes: Vec<String>,
112}
113
114impl Default for PrintIr {
115 fn default() -> Self {
116 Self {
117 initial: false,
118 r#final: false,
119 modified_only: true, passes: vec![],
121 }
122 }
123}
124
125impl PrintIr {
126 pub fn all(modified_only: bool) -> Self {
127 Self {
128 initial: true,
129 r#final: true,
130 modified_only,
131 passes: PassManager::OPTIMIZATION_PASSES
132 .iter()
133 .map(|pass| pass.to_string())
134 .collect_vec(),
135 }
136 }
137
138 pub fn r#final() -> Self {
139 Self {
140 r#final: true,
141 ..Self::default()
142 }
143 }
144}
145
146impl std::ops::BitOrAssign for PrintIr {
147 fn bitor_assign(&mut self, rhs: Self) {
148 self.initial |= rhs.initial;
149 self.r#final |= rhs.r#final;
150 self.modified_only &= rhs.modified_only;
155 for pass in rhs.passes {
156 if !self.passes.contains(&pass) {
157 self.passes.push(pass);
158 }
159 }
160 }
161}
162
163impl From<&PrintIr> for PrintPassesOpts {
164 fn from(value: &PrintIr) -> Self {
165 Self {
166 initial: value.initial,
167 r#final: value.r#final,
168 modified_only: value.modified_only,
169 passes: HashSet::from_iter(value.passes.iter().cloned()),
170 }
171 }
172}
173
174#[derive(Clone)]
176pub struct BuildConfig {
177 pub(crate) build_target: BuildTarget,
179 pub(crate) canonical_root_module: Arc<PathBuf>,
182 pub(crate) print_dca_graph: Option<String>,
183 pub(crate) print_dca_graph_url_format: Option<String>,
184 pub(crate) print_asm: PrintAsm,
185 pub(crate) print_bytecode: bool,
186 pub(crate) print_bytecode_spans: bool,
187 pub(crate) print_ir: PrintIr,
188 pub(crate) include_tests: bool,
189 pub(crate) optimization_level: OptLevel,
190 pub time_phases: bool,
191 pub profile: bool,
192 pub metrics_outfile: Option<String>,
193 pub lsp_mode: Option<LspConfig>,
194}
195
196impl BuildConfig {
197 pub fn root_from_file_name_and_manifest_path(
206 root_module: PathBuf,
207 canonical_manifest_dir: PathBuf,
208 build_target: BuildTarget,
209 ) -> Self {
210 assert!(
211 canonical_manifest_dir.has_root(),
212 "manifest dir must be a canonical path",
213 );
214 let canonical_root_module = match root_module.has_root() {
215 true => root_module,
216 false => {
217 assert!(
218 root_module.starts_with(canonical_manifest_dir.file_name().unwrap()),
219 "file_name must be either absolute or relative to manifest directory",
220 );
221 canonical_manifest_dir
222 .parent()
223 .expect("unable to retrieve manifest directory parent")
224 .join(&root_module)
225 }
226 };
227 Self {
228 build_target,
229 canonical_root_module: Arc::new(canonical_root_module),
230 print_dca_graph: None,
231 print_dca_graph_url_format: None,
232 print_asm: PrintAsm::default(),
233 print_bytecode: false,
234 print_bytecode_spans: false,
235 print_ir: PrintIr::default(),
236 include_tests: false,
237 time_phases: false,
238 profile: false,
239 metrics_outfile: None,
240 optimization_level: OptLevel::Opt0,
241 lsp_mode: None,
242 }
243 }
244
245 pub fn with_print_dca_graph(self, a: Option<String>) -> Self {
246 Self {
247 print_dca_graph: a,
248 ..self
249 }
250 }
251
252 pub fn with_print_dca_graph_url_format(self, a: Option<String>) -> Self {
253 Self {
254 print_dca_graph_url_format: a,
255 ..self
256 }
257 }
258
259 pub fn with_print_asm(self, print_asm: PrintAsm) -> Self {
260 Self { print_asm, ..self }
261 }
262
263 pub fn with_print_bytecode(self, bytecode: bool, bytecode_spans: bool) -> Self {
264 Self {
265 print_bytecode: bytecode,
266 print_bytecode_spans: bytecode_spans,
267 ..self
268 }
269 }
270
271 pub fn with_print_ir(self, a: PrintIr) -> Self {
272 Self {
273 print_ir: a,
274 ..self
275 }
276 }
277
278 pub fn with_time_phases(self, a: bool) -> Self {
279 Self {
280 time_phases: a,
281 ..self
282 }
283 }
284
285 pub fn with_profile(self, a: bool) -> Self {
286 Self { profile: a, ..self }
287 }
288
289 pub fn with_metrics(self, a: Option<String>) -> Self {
290 Self {
291 metrics_outfile: a,
292 ..self
293 }
294 }
295
296 pub fn with_optimization_level(self, optimization_level: OptLevel) -> Self {
297 Self {
298 optimization_level,
299 ..self
300 }
301 }
302
303 pub fn with_include_tests(self, include_tests: bool) -> Self {
309 Self {
310 include_tests,
311 ..self
312 }
313 }
314
315 pub fn with_lsp_mode(self, lsp_mode: Option<LspConfig>) -> Self {
316 Self { lsp_mode, ..self }
317 }
318
319 pub fn canonical_root_module(&self) -> Arc<PathBuf> {
320 self.canonical_root_module.clone()
321 }
322}
323
324#[derive(Clone, Debug, Default)]
325pub struct LspConfig {
326 pub optimized_build: bool,
331 pub file_versions: BTreeMap<PathBuf, Option<u64>>,
334}
335
336#[cfg(test)]
337mod test {
338 use super::*;
339 #[test]
340 fn test_root_from_file_name_and_manifest_path() {
341 let root_module = PathBuf::from("mock_path/src/main.sw");
342 let canonical_manifest_dir = PathBuf::from("/tmp/sway_project/mock_path");
343 BuildConfig::root_from_file_name_and_manifest_path(
344 root_module,
345 canonical_manifest_dir,
346 BuildTarget::default(),
347 );
348 }
349
350 #[test]
351 fn test_root_from_file_name_and_manifest_path_contains_dot() {
352 let root_module = PathBuf::from("mock_path_contains_._dot/src/main.sw");
353 let canonical_manifest_dir = PathBuf::from("/tmp/sway_project/mock_path_contains_._dot");
354 BuildConfig::root_from_file_name_and_manifest_path(
355 root_module,
356 canonical_manifest_dir,
357 BuildTarget::default(),
358 );
359 }
360}