1use super::{
2 restrictions::CompilerSettingsRestrictions,
3 solc::{SolcCompiler, SolcSettings, SolcVersionedInput, SOLC_EXTENSIONS},
4 vyper::{
5 input::VyperVersionedInput, parser::VyperParsedSource, Vyper, VyperLanguage,
6 VYPER_EXTENSIONS,
7 },
8 CompilationError, Compiler, CompilerInput, CompilerOutput, CompilerSettings, CompilerVersion,
9 Language, ParsedSource,
10};
11use crate::{
12 artifacts::vyper::{VyperCompilationError, VyperSettings},
13 resolver::parse::SolData,
14 settings::VyperRestrictions,
15 solc::SolcRestrictions,
16};
17use foundry_compilers_artifacts::{
18 error::SourceLocation,
19 output_selection::OutputSelection,
20 remappings::Remapping,
21 sources::{Source, Sources},
22 Contract, Error, Severity, SolcLanguage,
23};
24use foundry_compilers_core::error::{Result, SolcError};
25use semver::Version;
26use serde::{Deserialize, Serialize};
27use std::{
28 borrow::Cow,
29 collections::BTreeSet,
30 fmt,
31 path::{Path, PathBuf},
32};
33
34#[derive(Clone, Debug)]
36pub struct MultiCompiler {
37 pub solc: Option<SolcCompiler>,
38 pub vyper: Option<Vyper>,
39}
40
41impl Default for MultiCompiler {
42 fn default() -> Self {
43 let vyper = Vyper::new("vyper").ok();
44
45 #[cfg(feature = "svm-solc")]
46 let solc = Some(SolcCompiler::AutoDetect);
47 #[cfg(not(feature = "svm-solc"))]
48 let solc = crate::solc::Solc::new("solc").map(SolcCompiler::Specific).ok();
49
50 Self { solc, vyper }
51 }
52}
53
54impl MultiCompiler {
55 pub fn new(solc: Option<SolcCompiler>, vyper_path: Option<PathBuf>) -> Result<Self> {
56 let vyper = vyper_path.map(Vyper::new).transpose()?;
57 Ok(Self { solc, vyper })
58 }
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
63#[serde(untagged)]
64pub enum MultiCompilerLanguage {
65 Solc(SolcLanguage),
66 Vyper(VyperLanguage),
67}
68
69impl From<SolcLanguage> for MultiCompilerLanguage {
70 fn from(language: SolcLanguage) -> Self {
71 Self::Solc(language)
72 }
73}
74
75impl From<VyperLanguage> for MultiCompilerLanguage {
76 fn from(language: VyperLanguage) -> Self {
77 Self::Vyper(language)
78 }
79}
80
81impl Language for MultiCompilerLanguage {
82 const FILE_EXTENSIONS: &'static [&'static str] = &["sol", "vy", "vyi", "yul"];
83}
84
85impl fmt::Display for MultiCompilerLanguage {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 match self {
88 Self::Solc(lang) => lang.fmt(f),
89 Self::Vyper(lang) => lang.fmt(f),
90 }
91 }
92}
93
94#[derive(Clone, Debug)]
96pub enum MultiCompilerParsedSource {
97 Solc(SolData),
98 Vyper(VyperParsedSource),
99}
100
101impl MultiCompilerParsedSource {
102 fn solc(&self) -> Option<&SolData> {
103 match self {
104 Self::Solc(parsed) => Some(parsed),
105 _ => None,
106 }
107 }
108
109 fn vyper(&self) -> Option<&VyperParsedSource> {
110 match self {
111 Self::Vyper(parsed) => Some(parsed),
112 _ => None,
113 }
114 }
115}
116
117#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
119#[serde(untagged)]
120pub enum MultiCompilerError {
121 Solc(Error),
122 Vyper(VyperCompilationError),
123}
124
125impl fmt::Display for MultiCompilerError {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 match self {
128 Self::Solc(error) => error.fmt(f),
129 Self::Vyper(error) => error.fmt(f),
130 }
131 }
132}
133
134#[derive(Clone, Copy, Debug, Default)]
135pub struct MultiCompilerRestrictions {
136 pub solc: SolcRestrictions,
137 pub vyper: VyperRestrictions,
138}
139
140impl CompilerSettingsRestrictions for MultiCompilerRestrictions {
141 fn merge(self, other: Self) -> Option<Self> {
142 Some(Self { solc: self.solc.merge(other.solc)?, vyper: self.vyper.merge(other.vyper)? })
143 }
144}
145
146#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
148pub struct MultiCompilerSettings {
149 pub solc: SolcSettings,
150 pub vyper: VyperSettings,
151}
152
153impl CompilerSettings for MultiCompilerSettings {
154 type Restrictions = MultiCompilerRestrictions;
155
156 fn can_use_cached(&self, other: &Self) -> bool {
157 self.solc.can_use_cached(&other.solc) && self.vyper.can_use_cached(&other.vyper)
158 }
159
160 fn update_output_selection(&mut self, f: impl FnOnce(&mut OutputSelection) + Copy) {
161 self.solc.update_output_selection(f);
162 self.vyper.update_output_selection(f);
163 }
164
165 fn with_allow_paths(self, allowed_paths: &BTreeSet<PathBuf>) -> Self {
166 Self {
167 solc: self.solc.with_allow_paths(allowed_paths),
168 vyper: self.vyper.with_allow_paths(allowed_paths),
169 }
170 }
171
172 fn with_base_path(self, base_path: &Path) -> Self {
173 Self {
174 solc: self.solc.with_base_path(base_path),
175 vyper: self.vyper.with_base_path(base_path),
176 }
177 }
178
179 fn with_include_paths(self, include_paths: &BTreeSet<PathBuf>) -> Self {
180 Self {
181 solc: self.solc.with_include_paths(include_paths),
182 vyper: self.vyper.with_include_paths(include_paths),
183 }
184 }
185
186 fn with_remappings(self, remappings: &[Remapping]) -> Self {
187 Self {
188 solc: self.solc.with_remappings(remappings),
189 vyper: self.vyper.with_remappings(remappings),
190 }
191 }
192
193 fn satisfies_restrictions(&self, restrictions: &Self::Restrictions) -> bool {
194 self.solc.satisfies_restrictions(&restrictions.solc)
195 && self.vyper.satisfies_restrictions(&restrictions.vyper)
196 }
197}
198
199impl From<MultiCompilerSettings> for SolcSettings {
200 fn from(settings: MultiCompilerSettings) -> Self {
201 settings.solc
202 }
203}
204
205impl From<MultiCompilerSettings> for VyperSettings {
206 fn from(settings: MultiCompilerSettings) -> Self {
207 settings.vyper
208 }
209}
210
211#[derive(Clone, Debug, Serialize)]
213#[serde(untagged)]
214pub enum MultiCompilerInput {
215 Solc(SolcVersionedInput),
216 Vyper(VyperVersionedInput),
217}
218
219impl CompilerInput for MultiCompilerInput {
220 type Language = MultiCompilerLanguage;
221 type Settings = MultiCompilerSettings;
222
223 fn build(
224 sources: Sources,
225 settings: Self::Settings,
226 language: Self::Language,
227 version: Version,
228 ) -> Self {
229 match language {
230 MultiCompilerLanguage::Solc(language) => {
231 Self::Solc(SolcVersionedInput::build(sources, settings.solc, language, version))
232 }
233 MultiCompilerLanguage::Vyper(language) => {
234 Self::Vyper(VyperVersionedInput::build(sources, settings.vyper, language, version))
235 }
236 }
237 }
238
239 fn compiler_name(&self) -> Cow<'static, str> {
240 match self {
241 Self::Solc(input) => input.compiler_name(),
242 Self::Vyper(input) => input.compiler_name(),
243 }
244 }
245
246 fn language(&self) -> Self::Language {
247 match self {
248 Self::Solc(input) => MultiCompilerLanguage::Solc(input.language()),
249 Self::Vyper(input) => MultiCompilerLanguage::Vyper(input.language()),
250 }
251 }
252
253 fn strip_prefix(&mut self, base: &Path) {
254 match self {
255 Self::Solc(input) => input.strip_prefix(base),
256 Self::Vyper(input) => input.strip_prefix(base),
257 }
258 }
259
260 fn version(&self) -> &Version {
261 match self {
262 Self::Solc(input) => input.version(),
263 Self::Vyper(input) => input.version(),
264 }
265 }
266
267 fn sources(&self) -> impl Iterator<Item = (&Path, &Source)> {
268 let ret: Box<dyn Iterator<Item = _>> = match self {
269 Self::Solc(input) => Box::new(input.sources()),
270 Self::Vyper(input) => Box::new(input.sources()),
271 };
272
273 ret
274 }
275}
276
277impl Compiler for MultiCompiler {
278 type Input = MultiCompilerInput;
279 type CompilationError = MultiCompilerError;
280 type ParsedSource = MultiCompilerParsedSource;
281 type Settings = MultiCompilerSettings;
282 type Language = MultiCompilerLanguage;
283 type CompilerContract = Contract;
284
285 fn compile(
286 &self,
287 input: &Self::Input,
288 ) -> Result<CompilerOutput<Self::CompilationError, Self::CompilerContract>> {
289 match input {
290 MultiCompilerInput::Solc(input) => {
291 if let Some(solc) = &self.solc {
292 Compiler::compile(solc, input).map(|res| res.map_err(MultiCompilerError::Solc))
293 } else {
294 Err(SolcError::msg("solc compiler is not available"))
295 }
296 }
297 MultiCompilerInput::Vyper(input) => {
298 if let Some(vyper) = &self.vyper {
299 Compiler::compile(vyper, input)
300 .map(|res| res.map_err(MultiCompilerError::Vyper))
301 } else {
302 Err(SolcError::msg("vyper compiler is not available"))
303 }
304 }
305 }
306 }
307
308 fn available_versions(&self, language: &Self::Language) -> Vec<CompilerVersion> {
309 match language {
310 MultiCompilerLanguage::Solc(language) => {
311 self.solc.as_ref().map(|s| s.available_versions(language)).unwrap_or_default()
312 }
313 MultiCompilerLanguage::Vyper(language) => {
314 self.vyper.as_ref().map(|v| v.available_versions(language)).unwrap_or_default()
315 }
316 }
317 }
318}
319
320impl ParsedSource for MultiCompilerParsedSource {
321 type Language = MultiCompilerLanguage;
322
323 fn parse(content: &str, file: &std::path::Path) -> Result<Self> {
324 let Some(extension) = file.extension().and_then(|e| e.to_str()) else {
325 return Err(SolcError::msg("failed to resolve file extension"));
326 };
327
328 if SOLC_EXTENSIONS.contains(&extension) {
329 <SolData as ParsedSource>::parse(content, file).map(Self::Solc)
330 } else if VYPER_EXTENSIONS.contains(&extension) {
331 VyperParsedSource::parse(content, file).map(Self::Vyper)
332 } else {
333 Err(SolcError::msg("unexpected file extension"))
334 }
335 }
336
337 fn version_req(&self) -> Option<&semver::VersionReq> {
338 match self {
339 Self::Solc(parsed) => parsed.version_req(),
340 Self::Vyper(parsed) => parsed.version_req(),
341 }
342 }
343
344 fn contract_names(&self) -> &[String] {
345 match self {
346 Self::Solc(parsed) => parsed.contract_names(),
347 Self::Vyper(parsed) => parsed.contract_names(),
348 }
349 }
350
351 fn language(&self) -> Self::Language {
352 match self {
353 Self::Solc(parsed) => MultiCompilerLanguage::Solc(parsed.language()),
354 Self::Vyper(parsed) => MultiCompilerLanguage::Vyper(parsed.language()),
355 }
356 }
357
358 fn resolve_imports<C>(
359 &self,
360 paths: &crate::ProjectPathsConfig<C>,
361 include_paths: &mut BTreeSet<PathBuf>,
362 ) -> Result<Vec<PathBuf>> {
363 match self {
364 Self::Solc(parsed) => parsed.resolve_imports(paths, include_paths),
365 Self::Vyper(parsed) => parsed.resolve_imports(paths, include_paths),
366 }
367 }
368
369 fn compilation_dependencies<'a>(
370 &self,
371 imported_nodes: impl Iterator<Item = (&'a Path, &'a Self)>,
372 ) -> impl Iterator<Item = &'a Path>
373 where
374 Self: 'a,
375 {
376 match self {
377 Self::Solc(parsed) => parsed
378 .compilation_dependencies(
379 imported_nodes.filter_map(|(path, node)| node.solc().map(|node| (path, node))),
380 )
381 .collect::<Vec<_>>(),
382 Self::Vyper(parsed) => parsed
383 .compilation_dependencies(
384 imported_nodes.filter_map(|(path, node)| node.vyper().map(|node| (path, node))),
385 )
386 .collect::<Vec<_>>(),
387 }
388 .into_iter()
389 }
390}
391
392impl CompilationError for MultiCompilerError {
393 fn is_warning(&self) -> bool {
394 match self {
395 Self::Solc(error) => error.is_warning(),
396 Self::Vyper(error) => error.is_warning(),
397 }
398 }
399 fn is_error(&self) -> bool {
400 match self {
401 Self::Solc(error) => error.is_error(),
402 Self::Vyper(error) => error.is_error(),
403 }
404 }
405
406 fn source_location(&self) -> Option<SourceLocation> {
407 match self {
408 Self::Solc(error) => error.source_location(),
409 Self::Vyper(error) => error.source_location(),
410 }
411 }
412
413 fn severity(&self) -> Severity {
414 match self {
415 Self::Solc(error) => error.severity(),
416 Self::Vyper(error) => error.severity(),
417 }
418 }
419
420 fn error_code(&self) -> Option<u64> {
421 match self {
422 Self::Solc(error) => error.error_code(),
423 Self::Vyper(error) => error.error_code(),
424 }
425 }
426}