Skip to content

Commit 56a31ee

Browse files
committed
feat: allow multiple compiler configs
1 parent c16927b commit 56a31ee

File tree

9 files changed

+299
-102
lines changed

9 files changed

+299
-102
lines changed

crates/compilers/src/compile/project.rs

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,15 @@ use semver::Version;
117117
use std::{collections::HashMap, path::PathBuf, time::Instant};
118118

119119
/// A set of different Solc installations with their version and the sources to be compiled
120-
pub(crate) type VersionedSources<L> = HashMap<L, HashMap<Version, Sources>>;
120+
pub(crate) type VersionedSources<L, S> = HashMap<L, Vec<(Version, Sources, S)>>;
121121

122122
#[derive(Debug)]
123123
pub struct ProjectCompiler<'a, T: ArtifactOutput, C: Compiler> {
124124
/// Contains the relationship of the source files and their imports
125125
edges: GraphEdges<C::ParsedSource>,
126126
project: &'a Project<C, T>,
127127
/// how to compile all the sources
128-
sources: CompilerSources<C::Language>,
128+
sources: CompilerSources<C::Language, C::Settings>,
129129
}
130130

131131
impl<'a, T: ArtifactOutput, C: Compiler> ProjectCompiler<'a, T, C> {
@@ -146,11 +146,7 @@ impl<'a, T: ArtifactOutput, C: Compiler> ProjectCompiler<'a, T, C> {
146146
sources.retain(|f, _| filter.is_match(f))
147147
}
148148
let graph = Graph::resolve_sources(&project.paths, sources)?;
149-
let (sources, edges) = graph.into_sources_by_version(
150-
project.offline,
151-
&project.locked_versions,
152-
&project.compiler,
153-
)?;
149+
let (sources, edges) = graph.into_sources_by_version(project)?;
154150

155151
// If there are multiple different versions, and we can use multiple jobs we can compile
156152
// them in parallel.
@@ -217,7 +213,7 @@ impl<'a, T: ArtifactOutput, C: Compiler> ProjectCompiler<'a, T, C> {
217213
#[derive(Debug)]
218214
struct PreprocessedState<'a, T: ArtifactOutput, C: Compiler> {
219215
/// Contains all the sources to compile.
220-
sources: CompilerSources<C::Language>,
216+
sources: CompilerSources<C::Language, C::Settings>,
221217

222218
/// Cache that holds `CacheEntry` objects if caching is enabled and the project is recompiled
223219
cache: ArtifactsCache<'a, T, C>,
@@ -357,14 +353,14 @@ impl<'a, T: ArtifactOutput, C: Compiler> ArtifactsState<'a, T, C> {
357353

358354
/// Determines how the `solc <-> sources` pairs are executed.
359355
#[derive(Debug, Clone)]
360-
struct CompilerSources<L> {
356+
struct CompilerSources<L, S> {
361357
/// The sources to compile.
362-
sources: VersionedSources<L>,
358+
sources: VersionedSources<L, S>,
363359
/// The number of jobs to use for parallel compilation.
364360
jobs: Option<usize>,
365361
}
366362

367-
impl<L: Language> CompilerSources<L> {
363+
impl<L: Language, S: CompilerSettings> CompilerSources<L, S> {
368364
/// Converts all `\\` separators to `/`.
369365
///
370366
/// This effectively ensures that `solc` can find imported files like `/src/Cheats.sol` in the
@@ -394,7 +390,7 @@ impl<L: Language> CompilerSources<L> {
394390
) {
395391
cache.remove_dirty_sources();
396392
for versioned_sources in self.sources.values_mut() {
397-
for (version, sources) in versioned_sources {
393+
for (version, sources, _) in versioned_sources {
398394
trace!("Filtering {} sources for {}", sources.len(), version);
399395
cache.filter(sources, version);
400396
trace!(
@@ -407,7 +403,7 @@ impl<L: Language> CompilerSources<L> {
407403
}
408404

409405
/// Compiles all the files with `Solc`
410-
fn compile<C: Compiler<Language = L>, T: ArtifactOutput>(
406+
fn compile<C: Compiler<Language = L, Settings = S>, T: ArtifactOutput>(
411407
self,
412408
cache: &mut ArtifactsCache<'_, T, C>,
413409
) -> Result<AggregatedCompilerOutput<C>> {
@@ -424,7 +420,7 @@ impl<L: Language> CompilerSources<L> {
424420

425421
let mut jobs = Vec::new();
426422
for (language, versioned_sources) in self.sources {
427-
for (version, sources) in versioned_sources {
423+
for (version, sources, mut opt_settings) in versioned_sources {
428424
if sources.is_empty() {
429425
// nothing to compile
430426
trace!("skip {} for empty sources set", version);
@@ -433,7 +429,6 @@ impl<L: Language> CompilerSources<L> {
433429

434430
// depending on the composition of the filtered sources, the output selection can be
435431
// optimized
436-
let mut opt_settings = project.settings.clone();
437432
let actually_dirty =
438433
sparse_output.sparse_sources(&sources, &mut opt_settings, graph);
439434

@@ -678,7 +673,7 @@ mod tests {
678673
// single solc
679674
assert_eq!(len, 1);
680675

681-
let filtered = &sources.values().next().unwrap().values().next().unwrap();
676+
let filtered = &sources.values().next().unwrap()[0].1;
682677

683678
// 3 contracts total
684679
assert_eq!(filtered.0.len(), 3);

crates/compilers/src/compilers/mod.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,18 @@ impl fmt::Display for CompilerVersion {
6161
}
6262
}
6363

64+
pub trait CompilerSettingsRestrictions: Debug + Sync + Send + Clone + Default {
65+
fn merge(&mut self, other: &Self);
66+
}
67+
6468
/// Compilation settings including evm_version, output_selection, etc.
6569
pub trait CompilerSettings:
6670
Default + Serialize + DeserializeOwned + Clone + Debug + Send + Sync + 'static
6771
{
72+
/// We allow configuring settings restrictions which might optionally contain specific
73+
/// requiremets for compiler configuration. e.g. min/max evm_version, optimizer runs
74+
type Restrictions: CompilerSettingsRestrictions;
75+
6876
/// Executes given fn with mutable reference to configured [OutputSelection].
6977
fn update_output_selection(&mut self, f: impl FnOnce(&mut OutputSelection) + Copy);
7078

@@ -97,6 +105,9 @@ pub trait CompilerSettings:
97105
fn with_include_paths(self, _include_paths: &BTreeSet<PathBuf>) -> Self {
98106
self
99107
}
108+
109+
/// Returns whether current settings satisfy given restrictions.
110+
fn satisfies_restrictions(&self, restrictions: &Self::Restrictions) -> bool;
100111
}
101112

102113
/// Input of a compiler, including sources and settings used for their compilation.

crates/compilers/src/compilers/multi.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use super::{
2-
solc::{SolcCompiler, SolcVersionedInput, SOLC_EXTENSIONS},
2+
solc::{SolcCompiler, SolcSettings, SolcVersionedInput, SOLC_EXTENSIONS},
33
vyper::{
44
input::VyperVersionedInput, parser::VyperParsedSource, Vyper, VyperLanguage,
55
VYPER_EXTENSIONS,
@@ -10,7 +10,9 @@ use super::{
1010
use crate::{
1111
artifacts::vyper::{VyperCompilationError, VyperSettings},
1212
resolver::parse::SolData,
13-
solc::SolcSettings,
13+
settings::VyperRestrictions,
14+
solc::SolcRestrictions,
15+
CompilerSettingsRestrictions,
1416
};
1517
use foundry_compilers_artifacts::{
1618
error::SourceLocation,
@@ -129,6 +131,19 @@ impl fmt::Display for MultiCompilerError {
129131
}
130132
}
131133

134+
#[derive(Clone, Copy, Debug, Default)]
135+
pub struct MultiCompilerRestrictions {
136+
pub solc: SolcRestrictions,
137+
pub vyper: VyperRestrictions,
138+
}
139+
140+
impl CompilerSettingsRestrictions for MultiCompilerRestrictions {
141+
fn merge(&mut self, other: &Self) {
142+
self.solc.merge(&other.solc);
143+
self.vyper.merge(&other.vyper);
144+
}
145+
}
146+
132147
/// Settings for the [MultiCompiler]. Includes settings for both Solc and Vyper compilers.
133148
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
134149
pub struct MultiCompilerSettings {
@@ -137,6 +152,8 @@ pub struct MultiCompilerSettings {
137152
}
138153

139154
impl CompilerSettings for MultiCompilerSettings {
155+
type Restrictions = MultiCompilerRestrictions;
156+
140157
fn can_use_cached(&self, other: &Self) -> bool {
141158
self.solc.can_use_cached(&other.solc) && self.vyper.can_use_cached(&other.vyper)
142159
}
@@ -173,6 +190,11 @@ impl CompilerSettings for MultiCompilerSettings {
173190
vyper: self.vyper.with_remappings(remappings),
174191
}
175192
}
193+
194+
fn satisfies_restrictions(&self, restrictions: &Self::Restrictions) -> bool {
195+
self.solc.satisfies_restrictions(&restrictions.solc)
196+
&& self.vyper.satisfies_restrictions(&restrictions.vyper)
197+
}
176198
}
177199

178200
impl From<MultiCompilerSettings> for SolcSettings {

crates/compilers/src/compilers/solc/mod.rs

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ use super::{
22
CompilationError, Compiler, CompilerInput, CompilerOutput, CompilerSettings, CompilerVersion,
33
Language, ParsedSource,
44
};
5-
use crate::resolver::parse::SolData;
5+
use crate::{resolver::parse::SolData, CompilerSettingsRestrictions};
66
pub use foundry_compilers_artifacts::SolcLanguage;
77
use foundry_compilers_artifacts::{
88
error::SourceLocation,
99
output_selection::OutputSelection,
1010
remappings::Remapping,
1111
sources::{Source, Sources},
12-
Error, Settings, Severity, SolcInput,
12+
Error, EvmVersion, Settings, Severity, SolcInput,
1313
};
1414
use foundry_compilers_core::error::Result;
1515
use itertools::Itertools;
@@ -190,7 +190,58 @@ impl DerefMut for SolcSettings {
190190
}
191191
}
192192

193+
#[derive(Debug, Clone, Copy, Default)]
194+
pub struct EvmVersionRestriction {
195+
pub min_evm_version: Option<EvmVersion>,
196+
pub max_evm_version: Option<EvmVersion>,
197+
}
198+
199+
impl EvmVersionRestriction {
200+
/// Returns true if the given version satisfies the restrictions
201+
///
202+
/// If given None, only returns true if no restrictions are set
203+
pub fn satisfies(&self, version: Option<EvmVersion>) -> bool {
204+
self.min_evm_version.map_or(true, |min| version.map_or(false, |v| v >= min))
205+
&& self.max_evm_version.map_or(true, |max| version.map_or(false, |v| v <= max))
206+
}
207+
208+
pub fn merge(&mut self, other: &Self) {
209+
let Self { min_evm_version, max_evm_version } = other;
210+
211+
if let Some(min_evm_version) = min_evm_version {
212+
if self.min_evm_version.map_or(true, |e| e < *min_evm_version) {
213+
self.min_evm_version.replace(*min_evm_version);
214+
}
215+
}
216+
217+
if let Some(max_evm_version) = max_evm_version {
218+
if self.max_evm_version.map_or(true, |e| e > *max_evm_version) {
219+
self.max_evm_version.replace(*max_evm_version);
220+
}
221+
}
222+
}
223+
}
224+
225+
#[derive(Debug, Clone, Copy, Default)]
226+
pub struct SolcRestrictions {
227+
pub evm_version: EvmVersionRestriction,
228+
pub via_ir: Option<bool>,
229+
}
230+
231+
impl CompilerSettingsRestrictions for SolcRestrictions {
232+
fn merge(&mut self, other: &Self) {
233+
self.evm_version.merge(&other.evm_version);
234+
235+
// Preserve true
236+
if self.via_ir.map_or(true, |via_ir| !via_ir) {
237+
self.via_ir = other.via_ir;
238+
}
239+
}
240+
}
241+
193242
impl CompilerSettings for SolcSettings {
243+
type Restrictions = SolcRestrictions;
244+
194245
fn update_output_selection(&mut self, f: impl FnOnce(&mut OutputSelection) + Copy) {
195246
f(&mut self.settings.output_selection)
196247
}
@@ -247,6 +298,16 @@ impl CompilerSettings for SolcSettings {
247298
self.cli_settings.include_paths.clone_from(include_paths);
248299
self
249300
}
301+
302+
fn satisfies_restrictions(&self, restrictions: &Self::Restrictions) -> bool {
303+
let mut satisfies = true;
304+
305+
satisfies &= restrictions.evm_version.satisfies(self.evm_version);
306+
satisfies &=
307+
restrictions.via_ir.map_or(true, |via_ir| via_ir == self.via_ir.unwrap_or_default());
308+
309+
satisfies
310+
}
250311
}
251312

252313
impl ParsedSource for SolData {

crates/compilers/src/compilers/vyper/settings.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
11
use std::{collections::BTreeSet, path::PathBuf};
22

33
pub use crate::artifacts::vyper::VyperSettings;
4-
use crate::compilers::CompilerSettings;
4+
use crate::{
5+
compilers::CompilerSettings, solc::EvmVersionRestriction, CompilerSettingsRestrictions,
6+
};
57
use foundry_compilers_artifacts::output_selection::OutputSelection;
68

9+
#[derive(Clone, Copy, Debug, Default)]
10+
pub struct VyperRestrictions {
11+
pub evm_version: EvmVersionRestriction,
12+
}
13+
14+
impl CompilerSettingsRestrictions for VyperRestrictions {
15+
fn merge(&mut self, other: &Self) {
16+
self.evm_version.merge(&other.evm_version);
17+
}
18+
}
19+
720
impl CompilerSettings for VyperSettings {
21+
type Restrictions = VyperRestrictions;
22+
823
fn update_output_selection(&mut self, f: impl FnOnce(&mut OutputSelection)) {
924
f(&mut self.output_selection)
1025
}
@@ -30,4 +45,8 @@ impl CompilerSettings for VyperSettings {
3045
self.search_paths = Some(include_paths.clone());
3146
self
3247
}
48+
49+
fn satisfies_restrictions(&self, restrictions: &Self::Restrictions) -> bool {
50+
restrictions.evm_version.satisfies(self.evm_version)
51+
}
3352
}

0 commit comments

Comments
 (0)