Skip to content

Commit 09f6b32

Browse files
alexandergrey33nbaztecklkvr
committed
refactor: make Contract generic for Compiler and add metadata to CompilerOutput (#224)
Right now the compiler abstraction has two couplings that force foundry-zksync to implement it's [own fork of compilers](https://github.com/Moonsong-Labs/compilers/tree/zksync-v0.11.6): 1. `Contract` is specific to `solc`/EVM contracts. Era VM contracts, while requiring different fields, still could use most of the functionality of the `compilers` pipeline. 2. `CompilerOutput` has `solc` specific fields. `zksolc` compilation has relevant information that is useful to have later on, for example when storing `BuildInfo` This PR implements changes to address this. If implemented, it would allow `foundry-zksync` (and potentially other non EVM implementations of foundry) to get rid of all overrides and only maintain ZKsync specific data structures/trait implementations. See [sample PR](Moonsong-Labs/compilers#42). Changes include: 1. Make `Compiler` generic over `Contract` (using `CompilerContract` as a trait type). 2. Add `metadata` field to `CompilerOutput` in order to add arbitrary data to compilation output. --------- Co-authored-by: Nisheeth Barthwal <[email protected]> Co-authored-by: Arsenii Kulikov <[email protected]>
1 parent b88073f commit 09f6b32

File tree

18 files changed

+337
-144
lines changed

18 files changed

+337
-144
lines changed

crates/compilers/src/artifact_output/configurable.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,12 @@ impl ConfigurableArtifacts {
169169

170170
impl ArtifactOutput for ConfigurableArtifacts {
171171
type Artifact = ConfigurableContractArtifact;
172+
type CompilerContract = Contract;
172173

173174
/// Writes extra files for compiled artifact based on [Self::additional_files]
174175
fn handle_artifacts(
175176
&self,
176-
contracts: &crate::VersionedContracts,
177+
contracts: &crate::VersionedContracts<Contract>,
177178
artifacts: &crate::Artifacts<Self::Artifact>,
178179
) -> Result<(), SolcError> {
179180
for (file, contracts) in contracts.as_ref().iter() {

crates/compilers/src/artifact_output/hh.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub struct HardhatArtifacts {
1313

1414
impl ArtifactOutput for HardhatArtifacts {
1515
type Artifact = HardhatArtifact;
16+
type CompilerContract = Contract;
1617

1718
fn contract_to_artifact(
1819
&self,

crates/compilers/src/artifact_output/mod.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use crate::{
3737
contracts::VersionedContracts,
3838
sources::{VersionedSourceFile, VersionedSourceFiles},
3939
},
40-
ProjectPathsConfig,
40+
CompilerContract, ProjectPathsConfig,
4141
};
4242

4343
/// Represents unique artifact metadata for identifying artifacts on output
@@ -110,7 +110,7 @@ impl ArtifactId {
110110
}
111111
}
112112

113-
/// Represents an artifact file representing a [`crate::Contract`]
113+
/// Represents an artifact file representing a [`crate::compilers::CompilerContract`]
114114
#[derive(Clone, Debug, PartialEq, Eq)]
115115
pub struct ArtifactFile<T> {
116116
/// The Artifact that was written
@@ -428,7 +428,7 @@ impl<T> Artifacts<T> {
428428
}
429429
}
430430

431-
/// A trait representation for a [`crate::Contract`] artifact
431+
/// A trait representation for a [`crate::compilers::CompilerContract`] artifact
432432
pub trait Artifact {
433433
/// Returns the artifact's [`JsonAbi`] and bytecode.
434434
fn into_inner(self) -> (Option<JsonAbi>, Option<Bytes>);
@@ -596,9 +596,9 @@ where
596596

597597
/// Handler invoked with the output of `solc`
598598
///
599-
/// Implementers of this trait are expected to take care of [`crate::Contract`] to
600-
/// [`crate::ArtifactOutput::Artifact`] conversion and how that `Artifact` type is stored on disk,
601-
/// this includes artifact file location and naming.
599+
/// Implementers of this trait are expected to take care of [`crate::compilers::CompilerContract`]
600+
/// to [`crate::ArtifactOutput::Artifact`] conversion and how that `Artifact` type is stored on
601+
/// disk, this includes artifact file location and naming.
602602
///
603603
/// Depending on the [`crate::Project`] contracts and their compatible versions,
604604
/// The project compiler may invoke different `solc` executables on the same
@@ -609,16 +609,17 @@ where
609609
pub trait ArtifactOutput {
610610
/// Represents the artifact that will be stored for a `Contract`
611611
type Artifact: Artifact + DeserializeOwned + Serialize + fmt::Debug + Send + Sync;
612+
type CompilerContract: CompilerContract;
612613

613614
/// Handle the aggregated set of compiled contracts from the solc [`crate::CompilerOutput`].
614615
///
615616
/// This will be invoked with all aggregated contracts from (multiple) solc `CompilerOutput`.
616617
/// See [`crate::AggregatedCompilerOutput`]
617-
fn on_output<C>(
618+
fn on_output<L>(
618619
&self,
619-
contracts: &VersionedContracts,
620+
contracts: &VersionedContracts<Self::CompilerContract>,
620621
sources: &VersionedSourceFiles,
621-
layout: &ProjectPathsConfig<C>,
622+
layout: &ProjectPathsConfig<L>,
622623
ctx: OutputContext<'_>,
623624
) -> Result<Artifacts<Self::Artifact>> {
624625
let mut artifacts = self.output_to_artifacts(contracts, sources, ctx, layout);
@@ -638,7 +639,7 @@ pub trait ArtifactOutput {
638639
/// Invoked after artifacts has been written to disk for additional processing.
639640
fn handle_artifacts(
640641
&self,
641-
_contracts: &VersionedContracts,
642+
_contracts: &VersionedContracts<Self::CompilerContract>,
642643
_artifacts: &Artifacts<Self::Artifact>,
643644
) -> Result<()> {
644645
Ok(())
@@ -800,7 +801,7 @@ pub trait ArtifactOutput {
800801
&self,
801802
_file: &Path,
802803
_name: &str,
803-
contract: Contract,
804+
contract: Self::CompilerContract,
804805
source_file: Option<&SourceFile>,
805806
) -> Self::Artifact;
806807

@@ -845,7 +846,7 @@ pub trait ArtifactOutput {
845846
/// [`Self::on_output()`]
846847
fn output_to_artifacts<C>(
847848
&self,
848-
contracts: &VersionedContracts,
849+
contracts: &VersionedContracts<Self::CompilerContract>,
849850
sources: &VersionedSourceFiles,
850851
ctx: OutputContext<'_>,
851852
layout: &ProjectPathsConfig<C>,
@@ -1083,6 +1084,7 @@ pub struct MinimalCombinedArtifacts {
10831084

10841085
impl ArtifactOutput for MinimalCombinedArtifacts {
10851086
type Artifact = CompactContractBytecode;
1087+
type CompilerContract = Contract;
10861088

10871089
fn contract_to_artifact(
10881090
&self,
@@ -1112,10 +1114,11 @@ pub struct MinimalCombinedArtifactsHardhatFallback {
11121114

11131115
impl ArtifactOutput for MinimalCombinedArtifactsHardhatFallback {
11141116
type Artifact = CompactContractBytecode;
1117+
type CompilerContract = Contract;
11151118

11161119
fn on_output<C>(
11171120
&self,
1118-
output: &VersionedContracts,
1121+
output: &VersionedContracts<Contract>,
11191122
sources: &VersionedSourceFiles,
11201123
layout: &ProjectPathsConfig<C>,
11211124
ctx: OutputContext<'_>,

crates/compilers/src/buildinfo.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Represents an entire build
22
3-
use crate::compilers::{CompilationError, CompilerInput, CompilerOutput, Language};
3+
use crate::compilers::{
4+
CompilationError, CompilerContract, CompilerInput, CompilerOutput, Language,
5+
};
46
use alloy_primitives::hex;
57
use foundry_compilers_core::{error::Result, utils};
68
use md5::Digest;
@@ -43,7 +45,7 @@ pub struct BuildContext<L> {
4345
}
4446

4547
impl<L: Language> BuildContext<L> {
46-
pub fn new<I, E>(input: &I, output: &CompilerOutput<E>) -> Result<Self>
48+
pub fn new<I, E, C>(input: &I, output: &CompilerOutput<E, C>) -> Result<Self>
4749
where
4850
I: CompilerInput<Language = L>,
4951
{
@@ -87,9 +89,9 @@ pub struct RawBuildInfo<L> {
8789

8890
impl<L: Language> RawBuildInfo<L> {
8991
/// Serializes a `BuildInfo` object
90-
pub fn new<I: CompilerInput<Language = L>, E: CompilationError>(
92+
pub fn new<I: CompilerInput<Language = L>, E: CompilationError, C: CompilerContract>(
9193
input: &I,
92-
output: &CompilerOutput<E>,
94+
output: &CompilerOutput<E, C>,
9395
full_build_info: bool,
9496
) -> Result<Self> {
9597
let version = input.version().clone();
@@ -130,7 +132,7 @@ impl<L: Language> RawBuildInfo<L> {
130132
mod tests {
131133
use super::*;
132134
use crate::compilers::solc::SolcVersionedInput;
133-
use foundry_compilers_artifacts::{sources::Source, Error, SolcLanguage, Sources};
135+
use foundry_compilers_artifacts::{sources::Source, Contract, Error, SolcLanguage, Sources};
134136
use std::path::PathBuf;
135137

136138
#[test]
@@ -142,9 +144,9 @@ mod tests {
142144
SolcLanguage::Solidity,
143145
v,
144146
);
145-
let output = CompilerOutput::<Error>::default();
147+
let output = CompilerOutput::<Error, Contract>::default();
146148
let raw_info = RawBuildInfo::new(&input, &output, true).unwrap();
147-
let _info: BuildInfo<SolcVersionedInput, CompilerOutput<Error>> =
149+
let _info: BuildInfo<SolcVersionedInput, CompilerOutput<Error, Contract>> =
148150
serde_json::from_str(&serde_json::to_string(&raw_info).unwrap()).unwrap();
149151
}
150152
}

crates/compilers/src/cache.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,11 @@ impl GroupedSources {
623623
/// A helper abstraction over the [`CompilerCache`] used to determine what files need to compiled
624624
/// and which `Artifacts` can be reused.
625625
#[derive(Debug)]
626-
pub(crate) struct ArtifactsCacheInner<'a, T: ArtifactOutput, C: Compiler> {
626+
pub(crate) struct ArtifactsCacheInner<
627+
'a,
628+
T: ArtifactOutput<CompilerContract = C::CompilerContract>,
629+
C: Compiler,
630+
> {
627631
/// The preexisting cache file.
628632
pub cache: CompilerCache<C::Settings>,
629633

@@ -652,7 +656,9 @@ pub(crate) struct ArtifactsCacheInner<'a, T: ArtifactOutput, C: Compiler> {
652656
pub content_hashes: HashMap<PathBuf, String>,
653657
}
654658

655-
impl<T: ArtifactOutput, C: Compiler> ArtifactsCacheInner<'_, T, C> {
659+
impl<T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
660+
ArtifactsCacheInner<'_, T, C>
661+
{
656662
/// Creates a new cache entry for the file
657663
fn create_cache_entry(&mut self, file: PathBuf, source: &Source) {
658664
let imports = self
@@ -905,20 +911,26 @@ impl<T: ArtifactOutput, C: Compiler> ArtifactsCacheInner<'_, T, C> {
905911
/// Abstraction over configured caching which can be either non-existent or an already loaded cache
906912
#[allow(clippy::large_enum_variant)]
907913
#[derive(Debug)]
908-
pub(crate) enum ArtifactsCache<'a, T: ArtifactOutput, C: Compiler> {
914+
pub(crate) enum ArtifactsCache<
915+
'a,
916+
T: ArtifactOutput<CompilerContract = C::CompilerContract>,
917+
C: Compiler,
918+
> {
909919
/// Cache nothing on disk
910920
Ephemeral(GraphEdges<C::ParsedSource>, &'a Project<C, T>),
911921
/// Handles the actual cached artifacts, detects artifacts that can be reused
912922
Cached(ArtifactsCacheInner<'a, T, C>),
913923
}
914924

915-
impl<'a, T: ArtifactOutput, C: Compiler> ArtifactsCache<'a, T, C> {
925+
impl<'a, T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>
926+
ArtifactsCache<'a, T, C>
927+
{
916928
/// Create a new cache instance with the given files
917929
pub fn new(project: &'a Project<C, T>, edges: GraphEdges<C::ParsedSource>) -> Result<Self> {
918930
/// Returns the [CompilerCache] to use
919931
///
920932
/// Returns a new empty cache if the cache does not exist or `invalidate_cache` is set.
921-
fn get_cache<T: ArtifactOutput, C: Compiler>(
933+
fn get_cache<T: ArtifactOutput<CompilerContract = C::CompilerContract>, C: Compiler>(
922934
project: &Project<C, T>,
923935
invalidate_cache: bool,
924936
) -> CompilerCache<C::Settings> {

0 commit comments

Comments
 (0)