From 026c489dfaab26a03a15e4380325dc5dacba049e Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 8 Mar 2024 21:56:33 +0400 Subject: [PATCH 1/7] fix: try writing extra output for cached artifacts --- src/artifact_output/configurable.rs | 170 ++++++++++++++++++++-------- src/artifact_output/mod.rs | 12 +- src/cache.rs | 4 + src/compile/project.rs | 15 +++ 4 files changed, 151 insertions(+), 50 deletions(-) diff --git a/src/artifact_output/configurable.rs b/src/artifact_output/configurable.rs index 6aab634f..f486bbda 100644 --- a/src/artifact_output/configurable.rs +++ b/src/artifact_output/configurable.rs @@ -10,20 +10,14 @@ use crate::{ artifacts::{ - bytecode::{CompactBytecode, CompactDeployedBytecode}, - contract::{CompactContract, CompactContractBytecode, Contract}, - output_selection::{ + bytecode::{CompactBytecode, CompactDeployedBytecode}, contract::{CompactContract, CompactContractBytecode, Contract}, output_selection::{ BytecodeOutputSelection, ContractOutputSelection, DeployedBytecodeOutputSelection, EvmOutputSelection, EwasmOutputSelection, - }, - Ast, CompactContractBytecodeCow, DevDoc, Evm, Ewasm, FunctionDebugData, GasEstimates, - GeneratedSource, LosslessMetadata, Metadata, Offsets, Settings, StorageLayout, UserDoc, - }, - sources::VersionedSourceFile, - Artifact, ArtifactOutput, SolcConfig, SolcError, SourceFile, + }, Ast, BytecodeObject, CompactContractBytecodeCow, DevDoc, Evm, Ewasm, FunctionDebugData, GasEstimates, GeneratedSource, LosslessMetadata, Metadata, Offsets, Settings, StorageLayout, UserDoc + }, sources::VersionedSourceFile, ArtifactFile, ArtifactOutput, SolcConfig, SolcError, SourceFile }; use alloy_json_abi::JsonAbi; -use alloy_primitives::hex; +use alloy_primitives::{hex}; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, collections::BTreeMap, fs, path::Path}; @@ -178,7 +172,7 @@ impl ConfigurableArtifacts { /// Returns the `Settings` this configuration corresponds to pub fn settings(&self) -> Settings { - SolcConfig::builder().additional_outputs(self.output_selection()).build().into() + SolcConfig::builder().additional_outputs(self.output_selection()).build().into() } /// Returns the output selection corresponding to this configuration @@ -370,6 +364,48 @@ impl ArtifactOutput for ConfigurableArtifacts { ..Default::default() }) } + + fn can_write_extras_from_artifact(&self) -> bool { + if self.additional_files.ir && !self.additional_values.ir { + return false; + } + if self.additional_files.ir_optimized && !self.additional_values.ir_optimized { + return false; + } + if self.additional_files.metadata && !self.additional_values.metadata { + return false; + } + if self.additional_files.ewasm && !self.additional_values.ewasm { + return false; + } + if self.additional_files.assembly && !self.additional_values.assembly { + return false; + } + if self.additional_files.source_map && !self.additional_values.source_map { + return false; + } + if self.additional_files.generated_sources && !self.additional_values.generated_sources { + return false; + } + return true; + } + + fn try_write_extras_from_artifact(&self, artifact_file: &ArtifactFile) -> Result<(), SolcError> { + let file = &artifact_file.file; + let artifact = &artifact_file.artifact; + self.additional_files.process_abi(artifact.abi.as_ref(), &file)?; + self.additional_files.process_assembly(artifact.assembly.as_deref(), &file)?; + self.additional_files.process_bytecode(artifact.bytecode.as_ref().map(|b| &b.object), &file)?; + self.additional_files.process_deployed_bytecode(artifact.deployed_bytecode.as_ref().and_then(|d| d.bytecode.as_ref()).map(|b| &b.object), &file)?; + self.additional_files.process_generated_sources(Some(&artifact.generated_sources), &file)?; + self.additional_files.process_ir(artifact.ir.as_deref(), &file)?; + self.additional_files.process_ir_optimized(artifact.ir_optimized.as_deref(), &file)?; + self.additional_files.process_ewasm(artifact.ewasm.as_ref(), &file)?; + self.additional_files.process_metadata(artifact.metadata.as_ref(), &file)?; + self.additional_files.process_source_map(artifact.bytecode.as_ref().and_then(|b| b.source_map.as_deref()), &file)?; + + Ok(()) + } } /// Determines the additional values to include in the contract's artifact file @@ -596,90 +632,130 @@ impl ExtraOutputFiles { config } - /// Write the set values as separate files - pub fn write_extras(&self, contract: &Contract, file: &Path) -> Result<(), SolcError> { + fn process_abi(&self, abi: Option<&JsonAbi>, file: &Path) -> Result<(), SolcError> { if self.abi { - if let Some(ref abi) = contract.abi { + if let Some(abi) = abi { let file = file.with_extension("abi.json"); fs::write(&file, serde_json::to_string_pretty(abi)?) .map_err(|err| SolcError::io(err, file))? } } + Ok(()) + } + fn process_metadata(&self, metadata: Option<&Metadata>, file: &Path) -> Result<(), SolcError> { if self.metadata { - if let Some(ref metadata) = contract.metadata { + if let Some(metadata) = metadata { let file = file.with_extension("metadata.json"); - fs::write(&file, serde_json::to_string_pretty(&metadata.raw_json()?)?) + fs::write(&file, serde_json::to_string_pretty(metadata)?) .map_err(|err| SolcError::io(err, file))? } } + Ok(()) + } - if self.ir_optimized { - if let Some(ref iropt) = contract.ir_optimized { - let file = file.with_extension("iropt"); - fs::write(&file, iropt).map_err(|err| SolcError::io(err, file))? - } - } - + fn process_ir(&self, ir: Option<&str>, file: &Path) -> Result<(), SolcError> { if self.ir { - if let Some(ref ir) = contract.ir { + if let Some(ir) = ir { let file = file.with_extension("ir"); fs::write(&file, ir).map_err(|err| SolcError::io(err, file))? } } + Ok(()) + } + + fn process_ir_optimized(&self, ir_optimized: Option<&str>, file: &Path) -> Result<(), SolcError> { + if self.ir_optimized { + if let Some(ir_optimized) = ir_optimized { + let file = file.with_extension("iropt"); + fs::write(&file, ir_optimized).map_err(|err| SolcError::io(err, file))? + } + } + Ok(()) + } + fn process_ewasm(&self, ewasm: Option<&Ewasm>, file: &Path) -> Result<(), SolcError> { if self.ewasm { - if let Some(ref ewasm) = contract.ewasm { + if let Some(ewasm) = ewasm { let file = file.with_extension("ewasm"); fs::write(&file, serde_json::to_vec_pretty(ewasm)?) .map_err(|err| SolcError::io(err, file))?; } } + Ok(()) + } + fn process_assembly(&self, asm: Option<&str>, file: &Path) -> Result<(), SolcError> { if self.assembly { - if let Some(ref evm) = contract.evm { - if let Some(ref asm) = evm.assembly { - let file = file.with_extension("asm"); - fs::write(&file, asm).map_err(|err| SolcError::io(err, file))? - } + if let Some(asm) = asm { + let file = file.with_extension("asm"); + fs::write(&file, asm).map_err(|err| SolcError::io(err, file))? } } + Ok(()) + } + fn process_generated_sources(&self, generated_sources: Option<&Vec>, file: &Path) -> Result<(), SolcError> { if self.generated_sources { - if let Some(ref evm) = contract.evm { - if let Some(ref bytecode) = evm.bytecode { - let file = file.with_extension("gensources"); - fs::write(&file, serde_json::to_vec_pretty(&bytecode.generated_sources)?) - .map_err(|err| SolcError::io(err, file))?; - } + if let Some(generated_sources) = generated_sources { + let file = file.with_extension("gensources"); + fs::write(&file, serde_json::to_vec_pretty(generated_sources)?) + .map_err(|err| SolcError::io(err, file))?; } } + Ok(()) + } + fn process_source_map(&self, source_map: Option<&str>, file: &Path) -> Result<(), SolcError> { if self.source_map { - if let Some(ref evm) = contract.evm { - if let Some(ref bytecode) = evm.bytecode { - if let Some(ref sourcemap) = bytecode.source_map { - let file = file.with_extension("sourcemap"); - fs::write(&file, sourcemap).map_err(|err| SolcError::io(err, file))? - } - } + if let Some(source_map) = source_map { + let file = file.with_extension("sourcemap"); + fs::write(&file, source_map).map_err(|err| SolcError::io(err, file))? } } + Ok(()) + } + fn process_bytecode(&self, bytecode: Option<&BytecodeObject>, file: &Path) -> Result<(), SolcError> { if self.bytecode { - if let Some(ref code) = contract.get_bytecode_bytes() { - let code = hex::encode(code.as_ref()); + if let Some(bytecode) = bytecode { + let code = hex::encode(bytecode.as_ref()); let file = file.with_extension("bin"); fs::write(&file, code).map_err(|err| SolcError::io(err, file))? } } + Ok(()) + } + + fn process_deployed_bytecode(&self, deployed: Option<&BytecodeObject>, file: &Path) -> Result<(), SolcError> { if self.deployed_bytecode { - if let Some(ref code) = contract.get_deployed_bytecode_bytes() { - let code = hex::encode(code.as_ref()); + if let Some(deployed) = deployed { + let code = hex::encode(deployed.as_ref()); let file = file.with_extension("deployed-bin"); fs::write(&file, code).map_err(|err| SolcError::io(err, file))? } } + Ok(()) + } + + /// Write the set values as separate files + pub fn write_extras(&self, contract: &Contract, file: &Path) -> Result<(), SolcError> { + self.process_abi(contract.abi.as_ref(), file)?; + self.process_metadata(contract.metadata.as_ref().map(|m| &m.metadata), file)?; + self.process_ir(contract.ir.as_deref(), file)?; + self.process_ir_optimized(contract.ir_optimized.as_deref(), file)?; + self.process_ewasm(contract.ewasm.as_ref(), file)?; + + let evm = contract.evm.as_ref(); + self.process_assembly(evm.and_then(|evm| evm.assembly.as_deref()), file)?; + + let bytecode = evm.and_then(|evm| evm.bytecode.as_ref()); + self.process_generated_sources(bytecode.map(|b| &b.generated_sources), file)?; + + let deployed_bytecode = evm.and_then(|evm| evm.deployed_bytecode.as_ref()); + self.process_source_map(bytecode.and_then(|b| b.source_map.as_deref()), file)?; + self.process_bytecode(bytecode.map(|b| &b.object), file)?; + self.process_deployed_bytecode(deployed_bytecode.and_then(|d| d.bytecode.as_ref()).map(|b| &b.object), file)?; Ok(()) } diff --git a/src/artifact_output/mod.rs b/src/artifact_output/mod.rs index 6611ae50..13a42e45 100644 --- a/src/artifact_output/mod.rs +++ b/src/artifact_output/mod.rs @@ -2,9 +2,7 @@ use crate::{ artifacts::{ - contract::{CompactContract, CompactContractBytecode, Contract}, - BytecodeObject, CompactBytecode, CompactContractBytecodeCow, CompactDeployedBytecode, - FileToContractsMap, SourceFile, + contract::{CompactContract, CompactContractBytecode, Contract}, BytecodeObject, CompactBytecode, CompactContractBytecodeCow, CompactDeployedBytecode, FileToContractsMap, SourceFile }, compile::output::{contracts::VersionedContracts, sources::VersionedSourceFiles}, error::Result, @@ -985,6 +983,14 @@ pub trait ArtifactOutput { _path: &str, _file: &VersionedSourceFile, ) -> Option; + + fn can_write_extras_from_artifact(&self) -> bool { + false + } + + fn try_write_extras_from_artifact(&self, _artifact_file: &ArtifactFile) -> Result<()> { + Ok(()) + } } /// Additional context to use during [`ArtifactOutput::on_output()`] diff --git a/src/cache.rs b/src/cache.rs index d53826fa..024d70ff 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -804,6 +804,10 @@ impl<'a, T: ArtifactOutput> ArtifactsCacheInner<'a, T> { return true; } + if !self.project.artifacts_handler().can_write_extras_from_artifact() { + return true + } + // all things match, can be reused false } diff --git a/src/compile/project.rs b/src/compile/project.rs index e3fb7c60..21341904 100644 --- a/src/compile/project.rs +++ b/src/compile/project.rs @@ -329,6 +329,21 @@ impl<'a, T: ArtifactOutput> CompiledState<'a, T> { ctx, )?; + match cache { + ArtifactsCache::Cached(ref cache) => { + trace!("writing extra output files for cached artifacts"); + + for artifacts in cache.cached_artifacts.values() { + for artifacts in artifacts.values() { + for artifact_file in artifacts { + project.artifacts_handler().try_write_extras_from_artifact(artifact_file)?; + } + } + } + } + ArtifactsCache::Ephemeral(..) => {} + } + // emits all the build infos, if they exist output.write_build_infos(project.build_info_path())?; From ebf7e2ddb0695d8186eeca2cf74194699bb556b3 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 8 Mar 2024 21:58:02 +0400 Subject: [PATCH 2/7] clippy + fmt --- src/artifact_output/configurable.rs | 84 +++++++++++++++++++++-------- src/artifact_output/mod.rs | 9 +++- src/cache.rs | 2 +- src/compile/project.rs | 4 +- 4 files changed, 73 insertions(+), 26 deletions(-) diff --git a/src/artifact_output/configurable.rs b/src/artifact_output/configurable.rs index f486bbda..2c71d5ce 100644 --- a/src/artifact_output/configurable.rs +++ b/src/artifact_output/configurable.rs @@ -10,14 +10,21 @@ use crate::{ artifacts::{ - bytecode::{CompactBytecode, CompactDeployedBytecode}, contract::{CompactContract, CompactContractBytecode, Contract}, output_selection::{ + bytecode::{CompactBytecode, CompactDeployedBytecode}, + contract::{CompactContract, CompactContractBytecode, Contract}, + output_selection::{ BytecodeOutputSelection, ContractOutputSelection, DeployedBytecodeOutputSelection, EvmOutputSelection, EwasmOutputSelection, - }, Ast, BytecodeObject, CompactContractBytecodeCow, DevDoc, Evm, Ewasm, FunctionDebugData, GasEstimates, GeneratedSource, LosslessMetadata, Metadata, Offsets, Settings, StorageLayout, UserDoc - }, sources::VersionedSourceFile, ArtifactFile, ArtifactOutput, SolcConfig, SolcError, SourceFile + }, + Ast, BytecodeObject, CompactContractBytecodeCow, DevDoc, Evm, Ewasm, FunctionDebugData, + GasEstimates, GeneratedSource, LosslessMetadata, Metadata, Offsets, Settings, + StorageLayout, UserDoc, + }, + sources::VersionedSourceFile, + ArtifactFile, ArtifactOutput, SolcConfig, SolcError, SourceFile, }; use alloy_json_abi::JsonAbi; -use alloy_primitives::{hex}; +use alloy_primitives::hex; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, collections::BTreeMap, fs, path::Path}; @@ -172,7 +179,7 @@ impl ConfigurableArtifacts { /// Returns the `Settings` this configuration corresponds to pub fn settings(&self) -> Settings { - SolcConfig::builder().additional_outputs(self.output_selection()).build().into() + SolcConfig::builder().additional_outputs(self.output_selection()).build().into() } /// Returns the output selection corresponding to this configuration @@ -387,22 +394,36 @@ impl ArtifactOutput for ConfigurableArtifacts { if self.additional_files.generated_sources && !self.additional_values.generated_sources { return false; } - return true; + true } - fn try_write_extras_from_artifact(&self, artifact_file: &ArtifactFile) -> Result<(), SolcError> { + fn try_write_extras_from_artifact( + &self, + artifact_file: &ArtifactFile, + ) -> Result<(), SolcError> { let file = &artifact_file.file; let artifact = &artifact_file.artifact; - self.additional_files.process_abi(artifact.abi.as_ref(), &file)?; - self.additional_files.process_assembly(artifact.assembly.as_deref(), &file)?; - self.additional_files.process_bytecode(artifact.bytecode.as_ref().map(|b| &b.object), &file)?; - self.additional_files.process_deployed_bytecode(artifact.deployed_bytecode.as_ref().and_then(|d| d.bytecode.as_ref()).map(|b| &b.object), &file)?; - self.additional_files.process_generated_sources(Some(&artifact.generated_sources), &file)?; - self.additional_files.process_ir(artifact.ir.as_deref(), &file)?; - self.additional_files.process_ir_optimized(artifact.ir_optimized.as_deref(), &file)?; - self.additional_files.process_ewasm(artifact.ewasm.as_ref(), &file)?; - self.additional_files.process_metadata(artifact.metadata.as_ref(), &file)?; - self.additional_files.process_source_map(artifact.bytecode.as_ref().and_then(|b| b.source_map.as_deref()), &file)?; + self.additional_files.process_abi(artifact.abi.as_ref(), file)?; + self.additional_files.process_assembly(artifact.assembly.as_deref(), file)?; + self.additional_files + .process_bytecode(artifact.bytecode.as_ref().map(|b| &b.object), file)?; + self.additional_files.process_deployed_bytecode( + artifact + .deployed_bytecode + .as_ref() + .and_then(|d| d.bytecode.as_ref()) + .map(|b| &b.object), + file, + )?; + self.additional_files.process_generated_sources(Some(&artifact.generated_sources), file)?; + self.additional_files.process_ir(artifact.ir.as_deref(), file)?; + self.additional_files.process_ir_optimized(artifact.ir_optimized.as_deref(), file)?; + self.additional_files.process_ewasm(artifact.ewasm.as_ref(), file)?; + self.additional_files.process_metadata(artifact.metadata.as_ref(), file)?; + self.additional_files.process_source_map( + artifact.bytecode.as_ref().and_then(|b| b.source_map.as_deref()), + file, + )?; Ok(()) } @@ -664,7 +685,11 @@ impl ExtraOutputFiles { Ok(()) } - fn process_ir_optimized(&self, ir_optimized: Option<&str>, file: &Path) -> Result<(), SolcError> { + fn process_ir_optimized( + &self, + ir_optimized: Option<&str>, + file: &Path, + ) -> Result<(), SolcError> { if self.ir_optimized { if let Some(ir_optimized) = ir_optimized { let file = file.with_extension("iropt"); @@ -695,7 +720,11 @@ impl ExtraOutputFiles { Ok(()) } - fn process_generated_sources(&self, generated_sources: Option<&Vec>, file: &Path) -> Result<(), SolcError> { + fn process_generated_sources( + &self, + generated_sources: Option<&Vec>, + file: &Path, + ) -> Result<(), SolcError> { if self.generated_sources { if let Some(generated_sources) = generated_sources { let file = file.with_extension("gensources"); @@ -716,7 +745,11 @@ impl ExtraOutputFiles { Ok(()) } - fn process_bytecode(&self, bytecode: Option<&BytecodeObject>, file: &Path) -> Result<(), SolcError> { + fn process_bytecode( + &self, + bytecode: Option<&BytecodeObject>, + file: &Path, + ) -> Result<(), SolcError> { if self.bytecode { if let Some(bytecode) = bytecode { let code = hex::encode(bytecode.as_ref()); @@ -727,7 +760,11 @@ impl ExtraOutputFiles { Ok(()) } - fn process_deployed_bytecode(&self, deployed: Option<&BytecodeObject>, file: &Path) -> Result<(), SolcError> { + fn process_deployed_bytecode( + &self, + deployed: Option<&BytecodeObject>, + file: &Path, + ) -> Result<(), SolcError> { if self.deployed_bytecode { if let Some(deployed) = deployed { let code = hex::encode(deployed.as_ref()); @@ -755,7 +792,10 @@ impl ExtraOutputFiles { let deployed_bytecode = evm.and_then(|evm| evm.deployed_bytecode.as_ref()); self.process_source_map(bytecode.and_then(|b| b.source_map.as_deref()), file)?; self.process_bytecode(bytecode.map(|b| &b.object), file)?; - self.process_deployed_bytecode(deployed_bytecode.and_then(|d| d.bytecode.as_ref()).map(|b| &b.object), file)?; + self.process_deployed_bytecode( + deployed_bytecode.and_then(|d| d.bytecode.as_ref()).map(|b| &b.object), + file, + )?; Ok(()) } diff --git a/src/artifact_output/mod.rs b/src/artifact_output/mod.rs index 13a42e45..d48e604c 100644 --- a/src/artifact_output/mod.rs +++ b/src/artifact_output/mod.rs @@ -2,7 +2,9 @@ use crate::{ artifacts::{ - contract::{CompactContract, CompactContractBytecode, Contract}, BytecodeObject, CompactBytecode, CompactContractBytecodeCow, CompactDeployedBytecode, FileToContractsMap, SourceFile + contract::{CompactContract, CompactContractBytecode, Contract}, + BytecodeObject, CompactBytecode, CompactContractBytecodeCow, CompactDeployedBytecode, + FileToContractsMap, SourceFile, }, compile::output::{contracts::VersionedContracts, sources::VersionedSourceFiles}, error::Result, @@ -988,7 +990,10 @@ pub trait ArtifactOutput { false } - fn try_write_extras_from_artifact(&self, _artifact_file: &ArtifactFile) -> Result<()> { + fn try_write_extras_from_artifact( + &self, + _artifact_file: &ArtifactFile, + ) -> Result<()> { Ok(()) } } diff --git a/src/cache.rs b/src/cache.rs index 024d70ff..0a5fb657 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -805,7 +805,7 @@ impl<'a, T: ArtifactOutput> ArtifactsCacheInner<'a, T> { } if !self.project.artifacts_handler().can_write_extras_from_artifact() { - return true + return true; } // all things match, can be reused diff --git a/src/compile/project.rs b/src/compile/project.rs index 21341904..731f625a 100644 --- a/src/compile/project.rs +++ b/src/compile/project.rs @@ -336,7 +336,9 @@ impl<'a, T: ArtifactOutput> CompiledState<'a, T> { for artifacts in cache.cached_artifacts.values() { for artifacts in artifacts.values() { for artifact_file in artifacts { - project.artifacts_handler().try_write_extras_from_artifact(artifact_file)?; + project + .artifacts_handler() + .try_write_extras_from_artifact(artifact_file)?; } } } From ebe6b8a012ce682d2185e38944e3ab48fafcd871 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Fri, 8 Mar 2024 23:23:10 +0400 Subject: [PATCH 3/7] impl for Project --- src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index d6259f0a..9e22a032 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -987,6 +987,17 @@ impl ArtifactOutput for Project { ) -> Option { self.artifacts_handler().standalone_source_file_to_artifact(path, file) } + + fn can_write_extras_from_artifact(&self) -> bool { + self.artifacts_handler().can_write_extras_from_artifact() + } + + fn try_write_extras_from_artifact( + &self, + artifact_file: &ArtifactFile, + ) -> Result<()> { + self.artifacts_handler().try_write_extras_from_artifact(artifact_file) + } } // Rebases the given path to the base directory lexically. From e689ed284780f925266b3401e00663281f4ce3b7 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 10 Mar 2024 00:04:08 +0400 Subject: [PATCH 4/7] refactor --- src/artifact_output/configurable.rs | 173 ++++++++++++++++++++-------- src/artifact_output/mod.rs | 45 ++------ src/cache.rs | 14 ++- src/compile/project.rs | 12 +- src/lib.rs | 19 +-- 5 files changed, 151 insertions(+), 112 deletions(-) diff --git a/src/artifact_output/configurable.rs b/src/artifact_output/configurable.rs index 2c71d5ce..c700c6d7 100644 --- a/src/artifact_output/configurable.rs +++ b/src/artifact_output/configurable.rs @@ -21,7 +21,7 @@ use crate::{ StorageLayout, UserDoc, }, sources::VersionedSourceFile, - ArtifactFile, ArtifactOutput, SolcConfig, SolcError, SourceFile, + utils, ArtifactFile, ArtifactOutput, SolcConfig, SolcError, SourceFile, }; use alloy_json_abi::JsonAbi; use alloy_primitives::hex; @@ -234,8 +234,24 @@ impl ConfigurableArtifacts { impl ArtifactOutput for ConfigurableArtifacts { type Artifact = ConfigurableContractArtifact; - fn write_contract_extras(&self, contract: &Contract, file: &Path) -> Result<(), SolcError> { - self.additional_files.write_extras(contract, file) + /// Writes extra files for compiled artifact based on [Self::additional_files] + fn handle_artifacts( + &self, + contracts: &crate::contracts::VersionedContracts, + artifacts: &crate::Artifacts, + ) -> Result<(), SolcError> { + for (file, contracts) in contracts.as_ref().iter() { + for (name, versioned_contracts) in contracts { + for contract in versioned_contracts { + if let Some(artifact) = artifacts.find_artifact(file, name, &contract.version) { + let file = &artifact.file; + utils::create_parent_dir_all(file)?; + self.additional_files.write_extras(&contract.contract, file)?; + } + } + } + } + Ok(()) } fn contract_to_artifact( @@ -372,58 +388,46 @@ impl ArtifactOutput for ConfigurableArtifacts { }) } - fn can_write_extras_from_artifact(&self) -> bool { - if self.additional_files.ir && !self.additional_values.ir { - return false; - } - if self.additional_files.ir_optimized && !self.additional_values.ir_optimized { - return false; - } - if self.additional_files.metadata && !self.additional_values.metadata { - return false; - } - if self.additional_files.ewasm && !self.additional_values.ewasm { - return false; - } - if self.additional_files.assembly && !self.additional_values.assembly { - return false; - } - if self.additional_files.source_map && !self.additional_values.source_map { - return false; - } - if self.additional_files.generated_sources && !self.additional_values.generated_sources { - return false; - } - true + /// We want to enforce recompilation if we are missing any extra files. + fn is_dirty(&self, artifact_file: &ArtifactFile) -> Result { + self.additional_files.is_missing_extras(&artifact_file.file) } - fn try_write_extras_from_artifact( + /// Writes extra files for cached artifacts based on [Self::additional_files]. + fn handle_cached_artifacts( &self, - artifact_file: &ArtifactFile, + artifacts: &crate::Artifacts, ) -> Result<(), SolcError> { - let file = &artifact_file.file; - let artifact = &artifact_file.artifact; - self.additional_files.process_abi(artifact.abi.as_ref(), file)?; - self.additional_files.process_assembly(artifact.assembly.as_deref(), file)?; - self.additional_files - .process_bytecode(artifact.bytecode.as_ref().map(|b| &b.object), file)?; - self.additional_files.process_deployed_bytecode( - artifact - .deployed_bytecode - .as_ref() - .and_then(|d| d.bytecode.as_ref()) - .map(|b| &b.object), - file, - )?; - self.additional_files.process_generated_sources(Some(&artifact.generated_sources), file)?; - self.additional_files.process_ir(artifact.ir.as_deref(), file)?; - self.additional_files.process_ir_optimized(artifact.ir_optimized.as_deref(), file)?; - self.additional_files.process_ewasm(artifact.ewasm.as_ref(), file)?; - self.additional_files.process_metadata(artifact.metadata.as_ref(), file)?; - self.additional_files.process_source_map( - artifact.bytecode.as_ref().and_then(|b| b.source_map.as_deref()), - file, - )?; + for artifacts in artifacts.values() { + for artifacts in artifacts.values() { + for artifact_file in artifacts { + let file = &artifact_file.file; + let artifact = &artifact_file.artifact; + + self.additional_files.process_abi(artifact.abi.as_ref(), file)?; + self.additional_files.process_assembly(artifact.assembly.as_deref(), file)?; + self.additional_files + .process_bytecode(artifact.bytecode.as_ref().map(|b| &b.object), file)?; + self.additional_files.process_deployed_bytecode( + artifact + .deployed_bytecode + .as_ref() + .and_then(|d| d.bytecode.as_ref()) + .map(|b| &b.object), + file, + )?; + self.additional_files.process_generated_sources(Some(&artifact.generated_sources), file)?; + self.additional_files.process_ir(artifact.ir.as_deref(), file)?; + self.additional_files.process_ir_optimized(artifact.ir_optimized.as_deref(), file)?; + self.additional_files.process_ewasm(artifact.ewasm.as_ref(), file)?; + self.additional_files.process_metadata(artifact.metadata.as_ref(), file)?; + self.additional_files.process_source_map( + artifact.bytecode.as_ref().and_then(|b| b.source_map.as_deref()), + file, + )?; + } + } + } Ok(()) } @@ -657,6 +661,7 @@ impl ExtraOutputFiles { if self.abi { if let Some(abi) = abi { let file = file.with_extension("abi.json"); + println!("WRITING EXTRA ABI to {:?}", file); fs::write(&file, serde_json::to_string_pretty(abi)?) .map_err(|err| SolcError::io(err, file))? } @@ -665,6 +670,7 @@ impl ExtraOutputFiles { } fn process_metadata(&self, metadata: Option<&Metadata>, file: &Path) -> Result<(), SolcError> { + println!("PROCESSING METADATA {:?}", metadata); if self.metadata { if let Some(metadata) = metadata { let file = file.with_extension("metadata.json"); @@ -778,6 +784,7 @@ impl ExtraOutputFiles { /// Write the set values as separate files pub fn write_extras(&self, contract: &Contract, file: &Path) -> Result<(), SolcError> { self.process_abi(contract.abi.as_ref(), file)?; + println!("METADATA IN CONTRACT {:?}", contract.metadata); self.process_metadata(contract.metadata.as_ref().map(|m| &m.metadata), file)?; self.process_ir(contract.ir.as_deref(), file)?; self.process_ir_optimized(contract.ir_optimized.as_deref(), file)?; @@ -799,4 +806,68 @@ impl ExtraOutputFiles { Ok(()) } + + pub fn is_missing_extras(&self, file: &Path) -> Result { + if self.abi { + let file = file.with_extension("abi.json"); + if !file.try_exists().map_err(|err| SolcError::io(err, file))? { + return Ok(true); + } + } + if self.metadata { + let file = file.with_extension("metadata.json"); + if !file.try_exists().map_err(|err| SolcError::io(err, file))? { + return Ok(true); + } + } + if self.ir { + let file = file.with_extension("ir"); + if !file.try_exists().map_err(|err| SolcError::io(err, file))? { + return Ok(true); + } + } + if self.ir_optimized { + let file = file.with_extension("iropt"); + if !file.try_exists().map_err(|err| SolcError::io(err, file))? { + return Ok(true); + } + } + if self.ewasm { + let file = file.with_extension("ewasm"); + if !file.try_exists().map_err(|err| SolcError::io(err, file))? { + return Ok(true); + } + } + if self.assembly { + let file = file.with_extension("asm"); + if !file.try_exists().map_err(|err| SolcError::io(err, file))? { + return Ok(true); + } + } + if self.generated_sources { + let file = file.with_extension("gensources"); + if !file.try_exists().map_err(|err| SolcError::io(err, file))? { + return Ok(true); + } + } + if self.source_map { + let file = file.with_extension("sourcemap"); + if !file.try_exists().map_err(|err| SolcError::io(err, file))? { + return Ok(true); + } + } + if self.bytecode { + let file = file.with_extension("bin"); + if !file.try_exists().map_err(|err| SolcError::io(err, file))? { + return Ok(true); + } + } + if self.deployed_bytecode { + let file = file.with_extension("deployed-bin"); + if !file.try_exists().map_err(|err| SolcError::io(err, file))? { + return Ok(true); + } + } + Ok(false) + } } diff --git a/src/artifact_output/mod.rs b/src/artifact_output/mod.rs index d48e604c..3f54210c 100644 --- a/src/artifact_output/mod.rs +++ b/src/artifact_output/mod.rs @@ -623,41 +623,17 @@ pub trait ArtifactOutput { artifacts.join_all(&layout.artifacts); artifacts.write_all()?; - self.write_extras(contracts, &artifacts)?; + self.handle_artifacts(contracts, &artifacts)?; Ok(artifacts) } - /// Write additional files for the contract - fn write_contract_extras(&self, contract: &Contract, file: &Path) -> Result<()> { - ExtraOutputFiles::all().write_extras(contract, file) - } - - /// Writes additional files for the contracts if the included in the `Contract`, such as `ir`, - /// `ewasm`, `iropt`. - /// - /// By default, these fields are _not_ enabled in the [`crate::artifacts::Settings`], see - /// [`crate::artifacts::output_selection::OutputSelection::default_output_selection()`], and the - /// respective fields of the [`Contract`] will `None`. If they'll be manually added to the - /// `output_selection`, then we're also creating individual files for this output, such as - /// `Greeter.iropt`, `Gretter.ewasm` - fn write_extras( + /// Invoked after artifacts has been written to disk for additional processing. + fn handle_artifacts( &self, - contracts: &VersionedContracts, - artifacts: &Artifacts, + _contracts: &VersionedContracts, + _artifacts: &Artifacts, ) -> Result<()> { - for (file, contracts) in contracts.as_ref().iter() { - for (name, versioned_contracts) in contracts { - for c in versioned_contracts { - if let Some(artifact) = artifacts.find_artifact(file, name, &c.version) { - let file = &artifact.file; - utils::create_parent_dir_all(file)?; - self.write_contract_extras(&c.contract, file)?; - } - } - } - } - Ok(()) } @@ -986,14 +962,13 @@ pub trait ArtifactOutput { _file: &VersionedSourceFile, ) -> Option; - fn can_write_extras_from_artifact(&self) -> bool { - false + /// Handler allowing artifacts handler to enforce artifact recompilation. + fn is_dirty(&self, _artifact_file: &ArtifactFile) -> Result { + Ok(false) } - fn try_write_extras_from_artifact( - &self, - _artifact_file: &ArtifactFile, - ) -> Result<()> { + /// Invoked with all artifacts that were not recompiled. + fn handle_cached_artifacts(&self, _artifacts: &Artifacts) -> Result<()> { Ok(()) } } diff --git a/src/cache.rs b/src/cache.rs index 0a5fb657..af5e4ddb 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -804,8 +804,18 @@ impl<'a, T: ArtifactOutput> ArtifactsCacheInner<'a, T> { return true; } - if !self.project.artifacts_handler().can_write_extras_from_artifact() { - return true; + // If any requested extra files are missing for any artifact, mark source as dirty to + // generate them + for artifacts in self.cached_artifacts.values() { + for artifacts in artifacts.values() { + for artifact_file in artifacts { + println!("{:?}", artifact_file.file); + if self.project.artifacts_handler().is_dirty(artifact_file).unwrap_or(true) { + println!("HERE"); + return true; + } + } + } } // all things match, can be reused diff --git a/src/compile/project.rs b/src/compile/project.rs index 731f625a..80726321 100644 --- a/src/compile/project.rs +++ b/src/compile/project.rs @@ -331,17 +331,7 @@ impl<'a, T: ArtifactOutput> CompiledState<'a, T> { match cache { ArtifactsCache::Cached(ref cache) => { - trace!("writing extra output files for cached artifacts"); - - for artifacts in cache.cached_artifacts.values() { - for artifacts in artifacts.values() { - for artifact_file in artifacts { - project - .artifacts_handler() - .try_write_extras_from_artifact(artifact_file)?; - } - } - } + project.artifacts_handler().handle_cached_artifacts(&cache.cached_artifacts)?; } ArtifactsCache::Ephemeral(..) => {} } diff --git a/src/lib.rs b/src/lib.rs index 9e22a032..af485256 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -904,16 +904,12 @@ impl ArtifactOutput for Project { self.artifacts_handler().on_output(contracts, sources, layout, ctx) } - fn write_contract_extras(&self, contract: &Contract, file: &Path) -> Result<()> { - self.artifacts_handler().write_contract_extras(contract, file) - } - - fn write_extras( + fn handle_artifacts( &self, contracts: &VersionedContracts, artifacts: &Artifacts, ) -> Result<()> { - self.artifacts_handler().write_extras(contracts, artifacts) + self.artifacts_handler().handle_artifacts(contracts, artifacts) } fn output_file_name(name: impl AsRef) -> PathBuf { @@ -988,15 +984,12 @@ impl ArtifactOutput for Project { self.artifacts_handler().standalone_source_file_to_artifact(path, file) } - fn can_write_extras_from_artifact(&self) -> bool { - self.artifacts_handler().can_write_extras_from_artifact() + fn is_dirty(&self, artifact_file: &ArtifactFile) -> Result { + self.artifacts_handler().is_dirty(artifact_file) } - fn try_write_extras_from_artifact( - &self, - artifact_file: &ArtifactFile, - ) -> Result<()> { - self.artifacts_handler().try_write_extras_from_artifact(artifact_file) + fn handle_cached_artifacts(&self, artifacts: &Artifacts) -> Result<()> { + self.artifacts_handler().handle_cached_artifacts(artifacts) } } From 429a310ed94b03aa5d2516c67b6715adc41a76bb Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 10 Mar 2024 00:06:03 +0400 Subject: [PATCH 5/7] rm println --- src/cache.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/cache.rs b/src/cache.rs index af5e4ddb..a26735f7 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -809,9 +809,7 @@ impl<'a, T: ArtifactOutput> ArtifactsCacheInner<'a, T> { for artifacts in self.cached_artifacts.values() { for artifacts in artifacts.values() { for artifact_file in artifacts { - println!("{:?}", artifact_file.file); if self.project.artifacts_handler().is_dirty(artifact_file).unwrap_or(true) { - println!("HERE"); return true; } } From ded12909357fea99b41636175c7e41b293c281f8 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 10 Mar 2024 00:10:15 +0400 Subject: [PATCH 6/7] rm println --- src/artifact_output/configurable.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/artifact_output/configurable.rs b/src/artifact_output/configurable.rs index c700c6d7..aa50990c 100644 --- a/src/artifact_output/configurable.rs +++ b/src/artifact_output/configurable.rs @@ -784,7 +784,6 @@ impl ExtraOutputFiles { /// Write the set values as separate files pub fn write_extras(&self, contract: &Contract, file: &Path) -> Result<(), SolcError> { self.process_abi(contract.abi.as_ref(), file)?; - println!("METADATA IN CONTRACT {:?}", contract.metadata); self.process_metadata(contract.metadata.as_ref().map(|m| &m.metadata), file)?; self.process_ir(contract.ir.as_deref(), file)?; self.process_ir_optimized(contract.ir_optimized.as_deref(), file)?; From 5118a90cb3df36677c9c955804ac50b9e5ad73f0 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Sun, 10 Mar 2024 01:30:32 +0400 Subject: [PATCH 7/7] add test --- src/artifact_output/configurable.rs | 123 +++++++++++----------------- src/compile/project.rs | 26 +++++- 2 files changed, 72 insertions(+), 77 deletions(-) diff --git a/src/artifact_output/configurable.rs b/src/artifact_output/configurable.rs index aa50990c..9cc83444 100644 --- a/src/artifact_output/configurable.rs +++ b/src/artifact_output/configurable.rs @@ -21,7 +21,7 @@ use crate::{ StorageLayout, UserDoc, }, sources::VersionedSourceFile, - utils, ArtifactFile, ArtifactOutput, SolcConfig, SolcError, SourceFile, + utils, Artifact, ArtifactFile, ArtifactOutput, SolcConfig, SolcError, SourceFile, }; use alloy_json_abi::JsonAbi; use alloy_primitives::hex; @@ -388,9 +388,47 @@ impl ArtifactOutput for ConfigurableArtifacts { }) } - /// We want to enforce recompilation if we are missing any extra files. + /// We want to enforce recompilation if artifact is missing data we need for writing extra + /// files. fn is_dirty(&self, artifact_file: &ArtifactFile) -> Result { - self.additional_files.is_missing_extras(&artifact_file.file) + let artifact = &artifact_file.artifact; + let ExtraOutputFiles { + abi: _, + metadata, + ir, + ir_optimized, + ewasm, + assembly, + source_map, + generated_sources, + bytecode: _, + deployed_bytecode: _, + __non_exhaustive: _, + } = self.additional_files; + + if metadata && artifact.metadata.is_none() { + return Ok(true); + } + if ir && artifact.ir.is_none() { + return Ok(true); + } + if ir_optimized && artifact.ir_optimized.is_none() { + return Ok(true); + } + if ewasm && artifact.ewasm.is_none() { + return Ok(true); + } + if assembly && artifact.assembly.is_none() { + return Ok(true); + } + if source_map && artifact.get_source_map_str().is_none() { + return Ok(true); + } + if generated_sources { + // We can't check if generated sources are missing or just empty. + return Ok(true); + } + Ok(false) } /// Writes extra files for cached artifacts based on [Self::additional_files]. @@ -403,7 +441,6 @@ impl ArtifactOutput for ConfigurableArtifacts { for artifact_file in artifacts { let file = &artifact_file.file; let artifact = &artifact_file.artifact; - self.additional_files.process_abi(artifact.abi.as_ref(), file)?; self.additional_files.process_assembly(artifact.assembly.as_deref(), file)?; self.additional_files @@ -416,15 +453,15 @@ impl ArtifactOutput for ConfigurableArtifacts { .map(|b| &b.object), file, )?; - self.additional_files.process_generated_sources(Some(&artifact.generated_sources), file)?; + self.additional_files + .process_generated_sources(Some(&artifact.generated_sources), file)?; self.additional_files.process_ir(artifact.ir.as_deref(), file)?; - self.additional_files.process_ir_optimized(artifact.ir_optimized.as_deref(), file)?; + self.additional_files + .process_ir_optimized(artifact.ir_optimized.as_deref(), file)?; self.additional_files.process_ewasm(artifact.ewasm.as_ref(), file)?; self.additional_files.process_metadata(artifact.metadata.as_ref(), file)?; - self.additional_files.process_source_map( - artifact.bytecode.as_ref().and_then(|b| b.source_map.as_deref()), - file, - )?; + self.additional_files + .process_source_map(artifact.get_source_map_str().as_deref(), file)?; } } } @@ -661,7 +698,6 @@ impl ExtraOutputFiles { if self.abi { if let Some(abi) = abi { let file = file.with_extension("abi.json"); - println!("WRITING EXTRA ABI to {:?}", file); fs::write(&file, serde_json::to_string_pretty(abi)?) .map_err(|err| SolcError::io(err, file))? } @@ -670,7 +706,6 @@ impl ExtraOutputFiles { } fn process_metadata(&self, metadata: Option<&Metadata>, file: &Path) -> Result<(), SolcError> { - println!("PROCESSING METADATA {:?}", metadata); if self.metadata { if let Some(metadata) = metadata { let file = file.with_extension("metadata.json"); @@ -805,68 +840,4 @@ impl ExtraOutputFiles { Ok(()) } - - pub fn is_missing_extras(&self, file: &Path) -> Result { - if self.abi { - let file = file.with_extension("abi.json"); - if !file.try_exists().map_err(|err| SolcError::io(err, file))? { - return Ok(true); - } - } - if self.metadata { - let file = file.with_extension("metadata.json"); - if !file.try_exists().map_err(|err| SolcError::io(err, file))? { - return Ok(true); - } - } - if self.ir { - let file = file.with_extension("ir"); - if !file.try_exists().map_err(|err| SolcError::io(err, file))? { - return Ok(true); - } - } - if self.ir_optimized { - let file = file.with_extension("iropt"); - if !file.try_exists().map_err(|err| SolcError::io(err, file))? { - return Ok(true); - } - } - if self.ewasm { - let file = file.with_extension("ewasm"); - if !file.try_exists().map_err(|err| SolcError::io(err, file))? { - return Ok(true); - } - } - if self.assembly { - let file = file.with_extension("asm"); - if !file.try_exists().map_err(|err| SolcError::io(err, file))? { - return Ok(true); - } - } - if self.generated_sources { - let file = file.with_extension("gensources"); - if !file.try_exists().map_err(|err| SolcError::io(err, file))? { - return Ok(true); - } - } - if self.source_map { - let file = file.with_extension("sourcemap"); - if !file.try_exists().map_err(|err| SolcError::io(err, file))? { - return Ok(true); - } - } - if self.bytecode { - let file = file.with_extension("bin"); - if !file.try_exists().map_err(|err| SolcError::io(err, file))? { - return Ok(true); - } - } - if self.deployed_bytecode { - let file = file.with_extension("deployed-bin"); - if !file.try_exists().map_err(|err| SolcError::io(err, file))? { - return Ok(true); - } - } - Ok(false) - } } diff --git a/src/compile/project.rs b/src/compile/project.rs index 80726321..34a4f4d6 100644 --- a/src/compile/project.rs +++ b/src/compile/project.rs @@ -686,7 +686,10 @@ fn compile_parallel( #[cfg(all(feature = "project-util", feature = "svm-solc"))] mod tests { use super::*; - use crate::{project_util::TempProject, MinimalCombinedArtifacts}; + use crate::{ + artifacts::output_selection::ContractOutputSelection, project_util::TempProject, + ConfigurableArtifacts, MinimalCombinedArtifacts, + }; fn init_tracing() { let _ = tracing_subscriber::fmt() @@ -839,4 +842,25 @@ mod tests { let compiler = ProjectCompiler::new(&project).unwrap(); let _out = compiler.compile().unwrap(); } + + #[test] + fn extra_output_cached() { + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/dapp-sample"); + let paths = ProjectPathsConfig::builder().sources(root.join("src")).lib(root.join("lib")); + let mut project = TempProject::::new(paths.clone()).unwrap(); + + // Compile once without enabled extra output + project.compile().unwrap(); + + // Enable extra output of abi + project.project_mut().artifacts = + ConfigurableArtifacts::new([], [ContractOutputSelection::Abi]); + + // Ensure that abi appears after compilation and that we didn't recompile anything + let abi_path = project.project().paths.artifacts.join("Dapp.sol/Dapp.abi.json"); + assert!(!abi_path.exists()); + let output = project.compile().unwrap(); + assert!(output.compiler_output.is_empty()); + assert!(abi_path.exists()); + } }