feat(solc): add configurable Artifact type (#907)
* refactor: make artifacts a sub mod * feat: more options for output selection * feat(solc): make ArtifactOutput struct functions * fix: migrate all features * feat: add configurable artifacts type * refactor: move configurable to separate file * feat: impl ArtifactOutput * refactor: write extras * simplify write extra * feat: more helper functions * feat: implement delegate * fix: failing doc test * fix: rustfmt * chore: update CHANGELOG * feat: add helper functions * refactor: remove flatten * feat: add link function * feat: replace default type * fix: doc tests * feat: more utility functions * fix: failing tests * chore: rename types * chore: bump ethers-solc 0.3.0 * fix: set metadata file extension properly
This commit is contained in:
parent
8ce58bfcf3
commit
19d2fd1155
|
@ -54,6 +54,8 @@
|
|||
|
||||
### Unreleased
|
||||
|
||||
- Let `Project` take ownership of `ArtifactOutput` and change trait interface
|
||||
[#907](https://github.com/gakonst/ethers-rs/pull/907)
|
||||
- Total revamp of the `Project::compile` pipeline
|
||||
[#802](https://github.com/gakonst/ethers-rs/pull/802)
|
||||
- Support multiple versions of compiled contracts
|
||||
|
|
|
@ -1375,7 +1375,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ethers-solc"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"colored",
|
||||
"criterion",
|
||||
|
|
|
@ -88,7 +88,7 @@ ethers-core = { version = "^0.6.0", default-features = false, path = "./ethers-c
|
|||
ethers-providers = { version = "^0.6.0", default-features = false, path = "./ethers-providers" }
|
||||
ethers-signers = { version = "^0.6.0", default-features = false, path = "./ethers-signers" }
|
||||
ethers-middleware = { version = "^0.6.0", default-features = false, path = "./ethers-middleware" }
|
||||
ethers-solc = { version = "^0.2.0", default-features = false, path = "./ethers-solc" }
|
||||
ethers-solc = { version = "^0.3.0", default-features = false, path = "./ethers-solc" }
|
||||
ethers-etherscan = { version = "^0.2.0", default-features = false, path = "./ethers-etherscan" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -32,7 +32,7 @@ ethers-contract-abigen = { version = "^0.6.0", path = "ethers-contract-abigen" }
|
|||
ethers-contract-derive = { version = "^0.6.0", path = "ethers-contract-derive" }
|
||||
ethers-core = { version = "^0.6.0", path = "../ethers-core", default-features = false, features = ["eip712"]}
|
||||
ethers-derive-eip712 = { version = "^0.2.0", path = "../ethers-core/ethers-derive-eip712"}
|
||||
ethers-solc = { version = "^0.2.0", path = "../ethers-solc", default-features = false }
|
||||
ethers-solc = { version = "^0.3.0", path = "../ethers-solc", default-features = false }
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
tokio = { version = "1.5", default-features = false, features = ["macros"] }
|
||||
|
|
|
@ -15,7 +15,7 @@ keywords = ["ethereum", "web3", "etherscan", "ethers"]
|
|||
|
||||
[dependencies]
|
||||
ethers-core = { version = "^0.6.0", path = "../ethers-core", default-features = false }
|
||||
ethers-solc = { version = "^0.2.0", path = "../ethers-solc", default-features = false }
|
||||
ethers-solc = { version = "^0.3.0", path = "../ethers-solc", default-features = false }
|
||||
reqwest = { version = "0.11.9", default-features = false, features = ["json"] }
|
||||
serde = { version = "1.0.124", default-features = false, features = ["derive"] }
|
||||
serde_json = { version = "1.0.64", default-features = false }
|
||||
|
|
|
@ -311,7 +311,7 @@ mod tests {
|
|||
.sources(&root)
|
||||
.build()
|
||||
.expect("failed to resolve project paths");
|
||||
let project = Project::<MinimalCombinedArtifacts>::builder()
|
||||
let project = Project::builder()
|
||||
.paths(paths)
|
||||
.build()
|
||||
.expect("failed to build the project");
|
||||
|
|
|
@ -42,7 +42,7 @@ hex = { version = "0.4.3", default-features = false, features = ["std"] }
|
|||
rand = { version = "0.8.5", default-features = false }
|
||||
ethers-providers = { version = "^0.6.0", path = "../ethers-providers", default-features = false, features = ["ws", "rustls"] }
|
||||
once_cell = "1.8.0"
|
||||
ethers-solc = { version = "^0.2.0", path = "../ethers-solc", default-features = false }
|
||||
ethers-solc = { version = "^0.3.0", path = "../ethers-solc", default-features = false }
|
||||
serial_test = "0.5.1"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ethers-solc"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
authors = ["Matthias Seitz <matthias.seitz@outlook.de>", "Georgios Konstantopoulos <me@gakonst.com>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2018"
|
||||
|
|
|
@ -0,0 +1,486 @@
|
|||
//! A configurable artifacts handler implementation
|
||||
|
||||
use crate::{
|
||||
artifacts::{
|
||||
output_selection::{ContractOutputSelection, EvmOutputSelection, EwasmOutputSelection},
|
||||
CompactBytecode, CompactContract, CompactContractBytecode, CompactDeployedBytecode,
|
||||
CompactEvm, DevDoc, Ewasm, GasEstimates, Metadata, Offsets, Settings, StorageLayout,
|
||||
UserDoc,
|
||||
},
|
||||
ArtifactOutput, Contract, SolcConfig, SolcError,
|
||||
};
|
||||
use ethers_core::abi::Abi;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::BTreeMap, fs, path::Path};
|
||||
|
||||
/// Represents the `Artifact` that `ConfigurableArtifacts` emits.
|
||||
///
|
||||
/// This is essentially a superset of [`CompactContractBytecode`].
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ConfigurableContractArtifact {
|
||||
/// The Ethereum Contract ABI. If empty, it is represented as an empty
|
||||
/// array. See https://docs.soliditylang.org/en/develop/abi-spec.html
|
||||
pub abi: Option<Abi>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub bytecode: Option<CompactBytecode>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub deployed_bytecode: Option<CompactDeployedBytecode>,
|
||||
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub assembly: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub method_identifiers: Option<BTreeMap<String, String>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub gas_estimates: Option<GasEstimates>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub metadata: Option<Metadata>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub storage_layout: Option<StorageLayout>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub userdoc: Option<UserDoc>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub devdoc: Option<DevDoc>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ir: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ir_optimized: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub ewasm: Option<Ewasm>,
|
||||
}
|
||||
|
||||
impl ConfigurableContractArtifact {
|
||||
/// Returns the inner element that contains the core bytecode related information
|
||||
pub fn into_contract_bytecode(self) -> CompactContractBytecode {
|
||||
self.into()
|
||||
}
|
||||
|
||||
/// Looks for all link references in deployment and runtime bytecodes
|
||||
pub fn all_link_references(&self) -> BTreeMap<String, BTreeMap<String, Vec<Offsets>>> {
|
||||
let mut links = BTreeMap::new();
|
||||
if let Some(bcode) = &self.bytecode {
|
||||
links.extend(bcode.link_references.clone());
|
||||
}
|
||||
|
||||
if let Some(d_bcode) = &self.deployed_bytecode {
|
||||
if let Some(bcode) = &d_bcode.bytecode {
|
||||
links.extend(bcode.link_references.clone());
|
||||
}
|
||||
}
|
||||
links
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigurableContractArtifact> for CompactContractBytecode {
|
||||
fn from(artifact: ConfigurableContractArtifact) -> Self {
|
||||
CompactContractBytecode {
|
||||
abi: artifact.abi,
|
||||
bytecode: artifact.bytecode,
|
||||
deployed_bytecode: artifact.deployed_bytecode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigurableContractArtifact> for CompactContract {
|
||||
fn from(artifact: ConfigurableContractArtifact) -> Self {
|
||||
CompactContractBytecode::from(artifact).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// An `Artifact` implementation that can be configured to include additional content and emit
|
||||
/// additional files
|
||||
///
|
||||
/// Creates a single json artifact with
|
||||
/// ```json
|
||||
/// {
|
||||
/// "abi": [],
|
||||
/// "bytecode": {...},
|
||||
/// "deployedBytecode": {...}
|
||||
/// // additional values
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||
pub struct ConfigurableArtifacts {
|
||||
/// A set of additional values to include in the contract's artifact file
|
||||
pub additional_values: ExtraOutputValues,
|
||||
|
||||
/// A set of values that should be written to a separate file
|
||||
pub additional_files: ExtraOutputFiles,
|
||||
|
||||
/// PRIVATE: This structure may grow, As such, constructing this structure should
|
||||
/// _always_ be done using a public constructor or update syntax:
|
||||
///
|
||||
/// ```rust
|
||||
///
|
||||
/// use ethers_solc::{ExtraOutputFiles, ConfigurableArtifacts};
|
||||
/// let config = ConfigurableArtifacts {
|
||||
/// additional_files: ExtraOutputFiles { metadata: true, ..Default::default() },
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
/// ```
|
||||
#[doc(hidden)]
|
||||
pub __non_exhaustive: (),
|
||||
}
|
||||
|
||||
impl ConfigurableArtifacts {
|
||||
pub fn new(
|
||||
extra_values: impl IntoIterator<Item = ContractOutputSelection>,
|
||||
extra_files: impl IntoIterator<Item = ContractOutputSelection>,
|
||||
) -> Self {
|
||||
Self {
|
||||
additional_values: ExtraOutputValues::from_output_selection(extra_values),
|
||||
additional_files: ExtraOutputFiles::from_output_selection(extra_files),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `Settings` this configuration corresponds to
|
||||
pub fn settings(&self) -> Settings {
|
||||
SolcConfig::builder().additional_outputs(self.output_selection()).build().into()
|
||||
}
|
||||
|
||||
/// Returns the output selection corresponding to this configuration
|
||||
pub fn output_selection(&self) -> Vec<ContractOutputSelection> {
|
||||
let mut selection = ContractOutputSelection::basic();
|
||||
if self.additional_values.ir {
|
||||
selection.push(ContractOutputSelection::Ir);
|
||||
}
|
||||
if self.additional_values.ir_optimized || self.additional_files.ir_optimized {
|
||||
selection.push(ContractOutputSelection::IrOptimized);
|
||||
}
|
||||
if self.additional_values.metadata || self.additional_files.metadata {
|
||||
selection.push(ContractOutputSelection::Metadata);
|
||||
}
|
||||
if self.additional_values.storage_layout {
|
||||
selection.push(ContractOutputSelection::StorageLayout);
|
||||
}
|
||||
if self.additional_values.devdoc {
|
||||
selection.push(ContractOutputSelection::DevDoc);
|
||||
}
|
||||
if self.additional_values.userdoc {
|
||||
selection.push(ContractOutputSelection::UserDoc);
|
||||
}
|
||||
if self.additional_values.gas_estimates {
|
||||
selection.push(EvmOutputSelection::GasEstimates.into());
|
||||
}
|
||||
if self.additional_values.assembly || self.additional_files.assembly {
|
||||
selection.push(EvmOutputSelection::Assembly.into());
|
||||
}
|
||||
if self.additional_values.ewasm || self.additional_files.ewasm {
|
||||
selection.push(EwasmOutputSelection::All.into());
|
||||
}
|
||||
selection
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
fn contract_to_artifact(&self, _file: &str, _name: &str, contract: Contract) -> Self::Artifact {
|
||||
let mut artifact_userdoc = None;
|
||||
let mut artifact_devdoc = None;
|
||||
let mut artifact_metadata = None;
|
||||
let mut artifact_ir = None;
|
||||
let mut artifact_ir_optimized = None;
|
||||
let mut artifact_ewasm = None;
|
||||
let mut artifact_bytecode = None;
|
||||
let mut artifact_deployed_bytecode = None;
|
||||
let mut artifact_gas_estimates = None;
|
||||
let mut artifact_method_identifiers = None;
|
||||
let mut artifact_assembly = None;
|
||||
let mut artifact_storage_layout = None;
|
||||
|
||||
let Contract {
|
||||
abi,
|
||||
metadata,
|
||||
userdoc,
|
||||
devdoc,
|
||||
ir,
|
||||
storage_layout,
|
||||
evm,
|
||||
ewasm,
|
||||
ir_optimized,
|
||||
} = contract;
|
||||
|
||||
if self.additional_values.metadata {
|
||||
artifact_metadata = metadata;
|
||||
}
|
||||
if self.additional_values.userdoc {
|
||||
artifact_userdoc = Some(userdoc);
|
||||
}
|
||||
if self.additional_values.devdoc {
|
||||
artifact_devdoc = Some(devdoc);
|
||||
}
|
||||
if self.additional_values.ewasm {
|
||||
artifact_ewasm = ewasm;
|
||||
}
|
||||
if self.additional_values.ir {
|
||||
artifact_ir = ir;
|
||||
}
|
||||
if self.additional_values.ir_optimized {
|
||||
artifact_ir_optimized = ir_optimized;
|
||||
}
|
||||
if self.additional_values.storage_layout {
|
||||
artifact_storage_layout = Some(storage_layout);
|
||||
}
|
||||
|
||||
if let Some(evm) = evm {
|
||||
let CompactEvm {
|
||||
assembly,
|
||||
bytecode,
|
||||
deployed_bytecode,
|
||||
method_identifiers,
|
||||
gas_estimates,
|
||||
..
|
||||
} = evm.into_compact();
|
||||
|
||||
artifact_bytecode = bytecode;
|
||||
artifact_deployed_bytecode = deployed_bytecode;
|
||||
|
||||
if self.additional_values.gas_estimates {
|
||||
artifact_gas_estimates = gas_estimates;
|
||||
}
|
||||
if self.additional_values.method_identifiers {
|
||||
artifact_method_identifiers = Some(method_identifiers);
|
||||
}
|
||||
if self.additional_values.assembly {
|
||||
artifact_assembly = assembly;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigurableContractArtifact {
|
||||
abi,
|
||||
bytecode: artifact_bytecode,
|
||||
deployed_bytecode: artifact_deployed_bytecode,
|
||||
assembly: artifact_assembly,
|
||||
method_identifiers: artifact_method_identifiers,
|
||||
gas_estimates: artifact_gas_estimates,
|
||||
metadata: artifact_metadata,
|
||||
storage_layout: artifact_storage_layout,
|
||||
userdoc: artifact_userdoc,
|
||||
devdoc: artifact_devdoc,
|
||||
ir: artifact_ir,
|
||||
ir_optimized: artifact_ir_optimized,
|
||||
ewasm: artifact_ewasm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines the additional values to include in the contract's artifact file
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||
pub struct ExtraOutputValues {
|
||||
pub ast: bool,
|
||||
pub userdoc: bool,
|
||||
pub devdoc: bool,
|
||||
pub method_identifiers: bool,
|
||||
pub storage_layout: bool,
|
||||
pub assembly: bool,
|
||||
pub gas_estimates: bool,
|
||||
pub compact_format: bool,
|
||||
pub metadata: bool,
|
||||
pub ir: bool,
|
||||
pub ir_optimized: bool,
|
||||
pub ewasm: bool,
|
||||
|
||||
/// PRIVATE: This structure may grow, As such, constructing this structure should
|
||||
/// _always_ be done using a public constructor or update syntax:
|
||||
///
|
||||
/// ```rust
|
||||
///
|
||||
/// use ethers_solc::ExtraOutputValues;
|
||||
/// let config = ExtraOutputValues {
|
||||
/// ir: true,
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
/// ```
|
||||
#[doc(hidden)]
|
||||
pub __non_exhaustive: (),
|
||||
}
|
||||
|
||||
impl ExtraOutputValues {
|
||||
/// Returns an instance where all values are set to `true`
|
||||
pub fn all() -> Self {
|
||||
Self {
|
||||
ast: true,
|
||||
userdoc: true,
|
||||
devdoc: true,
|
||||
method_identifiers: true,
|
||||
storage_layout: true,
|
||||
assembly: true,
|
||||
gas_estimates: true,
|
||||
compact_format: true,
|
||||
metadata: true,
|
||||
ir: true,
|
||||
ir_optimized: true,
|
||||
ewasm: true,
|
||||
__non_exhaustive: (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the values based on a set of `ContractOutputSelection`
|
||||
pub fn from_output_selection(
|
||||
settings: impl IntoIterator<Item = ContractOutputSelection>,
|
||||
) -> Self {
|
||||
let mut config = Self::default();
|
||||
for value in settings.into_iter() {
|
||||
match value {
|
||||
ContractOutputSelection::DevDoc => {
|
||||
config.devdoc = true;
|
||||
}
|
||||
ContractOutputSelection::UserDoc => {
|
||||
config.userdoc = true;
|
||||
}
|
||||
ContractOutputSelection::Metadata => {
|
||||
config.metadata = true;
|
||||
}
|
||||
ContractOutputSelection::Ir => {
|
||||
config.ir = true;
|
||||
}
|
||||
ContractOutputSelection::IrOptimized => {
|
||||
config.ir_optimized = true;
|
||||
}
|
||||
ContractOutputSelection::StorageLayout => {
|
||||
config.storage_layout = true;
|
||||
}
|
||||
ContractOutputSelection::Evm(evm) => match evm {
|
||||
EvmOutputSelection::All => {
|
||||
config.assembly = true;
|
||||
config.gas_estimates = true;
|
||||
config.method_identifiers = true;
|
||||
}
|
||||
EvmOutputSelection::Assembly => {
|
||||
config.assembly = true;
|
||||
}
|
||||
EvmOutputSelection::MethodIdentifiers => {
|
||||
config.method_identifiers = true;
|
||||
}
|
||||
EvmOutputSelection::GasEstimates => {
|
||||
config.gas_estimates = true;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
ContractOutputSelection::Ewasm(_) => {
|
||||
config.ewasm = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines what to emit as additional file
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||
pub struct ExtraOutputFiles {
|
||||
pub metadata: bool,
|
||||
pub ir_optimized: bool,
|
||||
pub ewasm: bool,
|
||||
pub assembly: bool,
|
||||
|
||||
/// PRIVATE: This structure may grow, As such, constructing this structure should
|
||||
/// _always_ be done using a public constructor or update syntax:
|
||||
///
|
||||
/// ```rust
|
||||
///
|
||||
/// use ethers_solc::ExtraOutputFiles;
|
||||
/// let config = ExtraOutputFiles {
|
||||
/// metadata: true,
|
||||
/// ..Default::default()
|
||||
/// };
|
||||
/// ```
|
||||
#[doc(hidden)]
|
||||
pub __non_exhaustive: (),
|
||||
}
|
||||
|
||||
impl ExtraOutputFiles {
|
||||
/// Returns an instance where all values are set to `true`
|
||||
pub fn all() -> Self {
|
||||
Self {
|
||||
metadata: true,
|
||||
ir_optimized: true,
|
||||
ewasm: true,
|
||||
assembly: true,
|
||||
__non_exhaustive: (),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the values based on a set of `ContractOutputSelection`
|
||||
pub fn from_output_selection(
|
||||
settings: impl IntoIterator<Item = ContractOutputSelection>,
|
||||
) -> Self {
|
||||
let mut config = Self::default();
|
||||
for value in settings.into_iter() {
|
||||
match value {
|
||||
ContractOutputSelection::Metadata => {
|
||||
config.metadata = true;
|
||||
}
|
||||
ContractOutputSelection::IrOptimized => {
|
||||
config.ir_optimized = true;
|
||||
}
|
||||
ContractOutputSelection::Evm(evm) => match evm {
|
||||
EvmOutputSelection::All => {
|
||||
config.assembly = true;
|
||||
}
|
||||
EvmOutputSelection::Assembly => {
|
||||
config.assembly = true;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
ContractOutputSelection::Ewasm(_) => {
|
||||
config.ewasm = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
config
|
||||
}
|
||||
|
||||
/// Write the set values as separate files
|
||||
pub fn write_extras(&self, contract: &Contract, file: &Path) -> Result<(), SolcError> {
|
||||
if self.metadata {
|
||||
if let Some(ref metadata) = contract.metadata {
|
||||
let file = file.with_extension("metadata.json");
|
||||
fs::write(&file, serde_json::to_string_pretty(metadata)?)
|
||||
.map_err(|err| SolcError::io(err, file))?
|
||||
}
|
||||
}
|
||||
|
||||
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))?
|
||||
}
|
||||
}
|
||||
|
||||
if self.ewasm {
|
||||
if let Some(ref ir) = contract.ir {
|
||||
let file = file.with_extension("ir");
|
||||
fs::write(&file, ir).map_err(|err| SolcError::io(err, file))?
|
||||
}
|
||||
}
|
||||
|
||||
if self.ewasm {
|
||||
if let Some(ref ewasm) = contract.ewasm {
|
||||
let file = file.with_extension("ewasm");
|
||||
fs::write(&file, serde_json::to_vec_pretty(ewasm)?)
|
||||
.map_err(|err| SolcError::io(err, file))?;
|
||||
}
|
||||
}
|
||||
|
||||
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))?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -15,6 +15,9 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
mod configurable;
|
||||
pub use configurable::*;
|
||||
|
||||
/// Represents an artifact file representing a [`crate::Contract`]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ArtifactFile<T> {
|
||||
|
@ -314,18 +317,24 @@ pub trait ArtifactOutput {
|
|||
/// This will be invoked with all aggregated contracts from (multiple) solc `CompilerOutput`.
|
||||
/// See [`crate::AggregatedCompilerOutput`]
|
||||
fn on_output(
|
||||
&self,
|
||||
contracts: &VersionedContracts,
|
||||
layout: &ProjectPathsConfig,
|
||||
) -> Result<Artifacts<Self::Artifact>> {
|
||||
let mut artifacts = Self::output_to_artifacts(contracts);
|
||||
let mut artifacts = self.output_to_artifacts(contracts);
|
||||
artifacts.join_all(&layout.artifacts);
|
||||
artifacts.write_all()?;
|
||||
|
||||
Self::write_extras(contracts, layout)?;
|
||||
self.write_extras(contracts, layout)?;
|
||||
|
||||
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`.
|
||||
///
|
||||
|
@ -334,7 +343,11 @@ pub trait ArtifactOutput {
|
|||
/// [`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(contracts: &VersionedContracts, layout: &ProjectPathsConfig) -> Result<()> {
|
||||
fn write_extras(
|
||||
&self,
|
||||
contracts: &VersionedContracts,
|
||||
layout: &ProjectPathsConfig,
|
||||
) -> Result<()> {
|
||||
for (file, contracts) in contracts.as_ref().iter() {
|
||||
for (name, versioned_contracts) in contracts {
|
||||
for c in versioned_contracts {
|
||||
|
@ -347,30 +360,7 @@ pub trait ArtifactOutput {
|
|||
let file = layout.artifacts.join(artifact_path);
|
||||
utils::create_parent_dir_all(&file)?;
|
||||
|
||||
if let Some(iropt) = &c.contract.ir_optimized {
|
||||
fs::write(&file.with_extension("iropt"), iropt)
|
||||
.map_err(|err| SolcError::io(err, file.with_extension("iropt")))?
|
||||
}
|
||||
|
||||
if let Some(ir) = &c.contract.ir {
|
||||
fs::write(&file.with_extension("ir"), ir)
|
||||
.map_err(|err| SolcError::io(err, file.with_extension("ir")))?
|
||||
}
|
||||
|
||||
if let Some(ewasm) = &c.contract.ewasm {
|
||||
fs::write(
|
||||
&file.with_extension("ewasm"),
|
||||
serde_json::to_vec_pretty(&ewasm)?,
|
||||
)
|
||||
.map_err(|err| SolcError::io(err, file.with_extension("ewasm")))?;
|
||||
}
|
||||
|
||||
if let Some(evm) = &c.contract.evm {
|
||||
if let Some(asm) = &evm.assembly {
|
||||
fs::write(&file.with_extension("asm"), asm)
|
||||
.map_err(|err| SolcError::io(err, file.with_extension("asm")))?
|
||||
}
|
||||
}
|
||||
self.write_contract_extras(&c.contract, &file)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -474,13 +464,13 @@ pub trait ArtifactOutput {
|
|||
///
|
||||
/// This is the core conversion function that takes care of converting a `Contract` into the
|
||||
/// associated `Artifact` type
|
||||
fn contract_to_artifact(_file: &str, _name: &str, contract: Contract) -> Self::Artifact;
|
||||
fn contract_to_artifact(&self, _file: &str, _name: &str, contract: Contract) -> Self::Artifact;
|
||||
|
||||
/// Convert the compiler output into a set of artifacts
|
||||
///
|
||||
/// **Note:** This does only convert, but _NOT_ write the artifacts to disk, See
|
||||
/// [`Self::on_output()`]
|
||||
fn output_to_artifacts(contracts: &VersionedContracts) -> Artifacts<Self::Artifact> {
|
||||
fn output_to_artifacts(&self, contracts: &VersionedContracts) -> Artifacts<Self::Artifact> {
|
||||
let mut artifacts = ArtifactsMap::new();
|
||||
for (file, contracts) in contracts.as_ref().iter() {
|
||||
let mut entries = BTreeMap::new();
|
||||
|
@ -493,8 +483,7 @@ pub trait ArtifactOutput {
|
|||
} else {
|
||||
Self::output_file(file, name)
|
||||
};
|
||||
let artifact =
|
||||
Self::contract_to_artifact(file, name, contract.contract.clone());
|
||||
let artifact = self.contract_to_artifact(file, name, contract.contract.clone());
|
||||
|
||||
contracts.push(ArtifactFile {
|
||||
artifact,
|
||||
|
@ -511,40 +500,45 @@ pub trait ArtifactOutput {
|
|||
}
|
||||
}
|
||||
|
||||
/// An Artifacts implementation that uses a compact representation
|
||||
/// An `Artifact` implementation that uses a compact representation
|
||||
///
|
||||
/// Creates a single json artifact with
|
||||
/// ```json
|
||||
/// {
|
||||
/// "abi": [],
|
||||
/// "bin": "...",
|
||||
/// "runtime-bin": "..."
|
||||
/// "bytecode": {...},
|
||||
/// "deployedBytecode": {...}
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct MinimalCombinedArtifacts;
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||
pub struct MinimalCombinedArtifacts {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl ArtifactOutput for MinimalCombinedArtifacts {
|
||||
type Artifact = CompactContractBytecode;
|
||||
|
||||
fn contract_to_artifact(_file: &str, _name: &str, contract: Contract) -> Self::Artifact {
|
||||
fn contract_to_artifact(&self, _file: &str, _name: &str, contract: Contract) -> Self::Artifact {
|
||||
Self::Artifact::from(contract)
|
||||
}
|
||||
}
|
||||
|
||||
/// An Artifacts handler implementation that works the same as `MinimalCombinedArtifacts` but also
|
||||
/// supports reading hardhat artifacts if an initial attempt to deserialize an artifact failed
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct MinimalCombinedArtifactsHardhatFallback;
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||
pub struct MinimalCombinedArtifactsHardhatFallback {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl ArtifactOutput for MinimalCombinedArtifactsHardhatFallback {
|
||||
type Artifact = CompactContractBytecode;
|
||||
|
||||
fn on_output(
|
||||
&self,
|
||||
output: &VersionedContracts,
|
||||
layout: &ProjectPathsConfig,
|
||||
) -> Result<Artifacts<Self::Artifact>> {
|
||||
MinimalCombinedArtifacts::on_output(output, layout)
|
||||
MinimalCombinedArtifacts::default().on_output(output, layout)
|
||||
}
|
||||
|
||||
fn read_cached_artifact(path: impl AsRef<Path>) -> Result<Self::Artifact> {
|
||||
|
@ -561,8 +555,8 @@ impl ArtifactOutput for MinimalCombinedArtifactsHardhatFallback {
|
|||
}
|
||||
}
|
||||
|
||||
fn contract_to_artifact(file: &str, name: &str, contract: Contract) -> Self::Artifact {
|
||||
MinimalCombinedArtifacts::contract_to_artifact(file, name, contract)
|
||||
fn contract_to_artifact(&self, file: &str, name: &str, contract: Contract) -> Self::Artifact {
|
||||
MinimalCombinedArtifacts::default().contract_to_artifact(file, name, contract)
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
|
|||
|
||||
pub mod output_selection;
|
||||
pub mod serde_helpers;
|
||||
use crate::artifacts::output_selection::ContractOutputSelection;
|
||||
pub use serde_helpers::{deserialize_bytes, deserialize_opt_bytes};
|
||||
|
||||
/// Solidity files are made up of multiple `source units`, a solidity contract is such a `source
|
||||
|
@ -229,6 +230,25 @@ impl Settings {
|
|||
)])
|
||||
}
|
||||
|
||||
/// Inserts a set of `ContractOutputSelection`
|
||||
pub fn push_all(&mut self, settings: impl IntoIterator<Item = ContractOutputSelection>) {
|
||||
for value in settings {
|
||||
self.push_output_selection(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// Inserts a set of `ContractOutputSelection`
|
||||
#[must_use]
|
||||
pub fn with_extra_output(
|
||||
mut self,
|
||||
settings: impl IntoIterator<Item = ContractOutputSelection>,
|
||||
) -> Self {
|
||||
for value in settings {
|
||||
self.push_output_selection(value)
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Inserts the value for all files and contracts
|
||||
///
|
||||
/// ```
|
||||
|
@ -922,6 +942,7 @@ impl From<Contract> for ContractBytecode {
|
|||
/// Unlike `CompactContractSome` which contains the `BytecodeObject`, this holds the whole
|
||||
/// `Bytecode` object.
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CompactContractBytecode {
|
||||
/// The Ethereum Contract ABI. If empty, it is represented as an empty
|
||||
/// array. See https://docs.soliditylang.org/en/develop/abi-spec.html
|
||||
|
@ -952,13 +973,8 @@ impl CompactContractBytecode {
|
|||
impl From<Contract> for CompactContractBytecode {
|
||||
fn from(c: Contract) -> Self {
|
||||
let (bytecode, deployed_bytecode) = if let Some(evm) = c.evm {
|
||||
let (maybe_bcode, maybe_runtime) = match (evm.bytecode, evm.deployed_bytecode) {
|
||||
(Some(bcode), Some(dbcode)) => (Some(bcode.into()), Some(dbcode.into())),
|
||||
(None, Some(dbcode)) => (None, Some(dbcode.into())),
|
||||
(Some(bcode), None) => (Some(bcode.into()), None),
|
||||
(None, None) => (None, None),
|
||||
};
|
||||
(maybe_bcode, maybe_runtime)
|
||||
let evm = evm.into_compact();
|
||||
(evm.bytecode, evm.deployed_bytecode)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
@ -1336,6 +1352,55 @@ pub struct Evm {
|
|||
pub gas_estimates: Option<GasEstimates>,
|
||||
}
|
||||
|
||||
impl Evm {
|
||||
/// Crate internal helper do transform the underlying bytecode artifacts into a more convenient
|
||||
/// structure
|
||||
pub(crate) fn into_compact(self) -> CompactEvm {
|
||||
let Evm {
|
||||
assembly,
|
||||
legacy_assembly,
|
||||
bytecode,
|
||||
deployed_bytecode,
|
||||
method_identifiers,
|
||||
gas_estimates,
|
||||
} = self;
|
||||
|
||||
let (bytecode, deployed_bytecode) = match (bytecode, deployed_bytecode) {
|
||||
(Some(bcode), Some(dbcode)) => (Some(bcode.into()), Some(dbcode.into())),
|
||||
(None, Some(dbcode)) => (None, Some(dbcode.into())),
|
||||
(Some(bcode), None) => (Some(bcode.into()), None),
|
||||
(None, None) => (None, None),
|
||||
};
|
||||
|
||||
CompactEvm {
|
||||
assembly,
|
||||
legacy_assembly,
|
||||
bytecode,
|
||||
deployed_bytecode,
|
||||
method_identifiers,
|
||||
gas_estimates,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct CompactEvm {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub assembly: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub legacy_assembly: Option<serde_json::Value>,
|
||||
pub bytecode: Option<CompactBytecode>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub deployed_bytecode: Option<CompactDeployedBytecode>,
|
||||
/// The list of function hashes
|
||||
#[serde(default, skip_serializing_if = "::std::collections::BTreeMap::is_empty")]
|
||||
pub method_identifiers: BTreeMap<String, String>,
|
||||
/// Function gas estimates
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub gas_estimates: Option<GasEstimates>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Bytecode {
|
||||
|
|
|
@ -4,7 +4,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|||
use std::{fmt, str::FromStr};
|
||||
|
||||
/// Contract level output selection
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum ContractOutputSelection {
|
||||
Abi,
|
||||
DevDoc,
|
||||
|
@ -17,6 +17,23 @@ pub enum ContractOutputSelection {
|
|||
Ewasm(EwasmOutputSelection),
|
||||
}
|
||||
|
||||
impl ContractOutputSelection {
|
||||
/// Returns the basic set of contract level settings that should be included in the `Contract`
|
||||
/// that solc emits:
|
||||
/// - "abi"
|
||||
/// - "evm.bytecode"
|
||||
/// - "evm.deployedBytecode"
|
||||
/// - "evm.methodIdentifiers"
|
||||
pub fn basic() -> Vec<ContractOutputSelection> {
|
||||
vec![
|
||||
ContractOutputSelection::Abi,
|
||||
BytecodeOutputSelection::All.into(),
|
||||
DeployedBytecodeOutputSelection::All.into(),
|
||||
EvmOutputSelection::MethodIdentifiers.into(),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ContractOutputSelection {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
|
@ -61,8 +78,12 @@ impl FromStr for ContractOutputSelection {
|
|||
"userdoc" => Ok(ContractOutputSelection::UserDoc),
|
||||
"metadata" => Ok(ContractOutputSelection::Metadata),
|
||||
"ir" => Ok(ContractOutputSelection::Ir),
|
||||
"irOptimized" => Ok(ContractOutputSelection::IrOptimized),
|
||||
"storageLayout" => Ok(ContractOutputSelection::StorageLayout),
|
||||
"ir-optimized" | "irOptimized" | "iroptimized" => {
|
||||
Ok(ContractOutputSelection::IrOptimized)
|
||||
}
|
||||
"storage-layout" | "storagelayout" | "storageLayout" => {
|
||||
Ok(ContractOutputSelection::StorageLayout)
|
||||
}
|
||||
s => EvmOutputSelection::from_str(s)
|
||||
.map(ContractOutputSelection::Evm)
|
||||
.or_else(|_| EwasmOutputSelection::from_str(s).map(ContractOutputSelection::Ewasm))
|
||||
|
@ -71,8 +92,20 @@ impl FromStr for ContractOutputSelection {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Into<EvmOutputSelection>> From<T> for ContractOutputSelection {
|
||||
fn from(evm: T) -> Self {
|
||||
ContractOutputSelection::Evm(evm.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<EwasmOutputSelection> for ContractOutputSelection {
|
||||
fn from(ewasm: EwasmOutputSelection) -> Self {
|
||||
ContractOutputSelection::Ewasm(ewasm)
|
||||
}
|
||||
}
|
||||
|
||||
/// Contract level output selection for `evm`
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum EvmOutputSelection {
|
||||
All,
|
||||
Assembly,
|
||||
|
@ -83,6 +116,18 @@ pub enum EvmOutputSelection {
|
|||
DeployedByteCode(DeployedBytecodeOutputSelection),
|
||||
}
|
||||
|
||||
impl From<BytecodeOutputSelection> for EvmOutputSelection {
|
||||
fn from(b: BytecodeOutputSelection) -> Self {
|
||||
EvmOutputSelection::ByteCode(b)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DeployedBytecodeOutputSelection> for EvmOutputSelection {
|
||||
fn from(b: DeployedBytecodeOutputSelection) -> Self {
|
||||
EvmOutputSelection::DeployedByteCode(b)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for EvmOutputSelection {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
|
@ -121,10 +166,12 @@ impl FromStr for EvmOutputSelection {
|
|||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"evm" => Ok(EvmOutputSelection::All),
|
||||
"evm.assembly" => Ok(EvmOutputSelection::Assembly),
|
||||
"asm" | "evm.assembly" => Ok(EvmOutputSelection::Assembly),
|
||||
"evm.legacyAssembly" => Ok(EvmOutputSelection::LegacyAssembly),
|
||||
"evm.methodIdentifiers" => Ok(EvmOutputSelection::MethodIdentifiers),
|
||||
"evm.gasEstimates" => Ok(EvmOutputSelection::GasEstimates),
|
||||
"methodidentifiers" | "evm.methodIdentifiers" | "evm.methodidentifiers" => {
|
||||
Ok(EvmOutputSelection::MethodIdentifiers)
|
||||
}
|
||||
"gas" | "evm.gasEstimates" | "evm.gasestimates" => Ok(EvmOutputSelection::GasEstimates),
|
||||
s => BytecodeOutputSelection::from_str(s)
|
||||
.map(EvmOutputSelection::ByteCode)
|
||||
.or_else(|_| {
|
||||
|
@ -137,7 +184,7 @@ impl FromStr for EvmOutputSelection {
|
|||
}
|
||||
|
||||
/// Contract level output selection for `evm.bytecode`
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum BytecodeOutputSelection {
|
||||
All,
|
||||
FunctionDebugData,
|
||||
|
@ -202,7 +249,7 @@ impl FromStr for BytecodeOutputSelection {
|
|||
}
|
||||
|
||||
/// Contract level output selection for `evm.deployedBytecode`
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum DeployedBytecodeOutputSelection {
|
||||
All,
|
||||
FunctionDebugData,
|
||||
|
@ -277,7 +324,7 @@ impl FromStr for DeployedBytecodeOutputSelection {
|
|||
}
|
||||
|
||||
/// Contract level output selection for `evm.ewasm`
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub enum EwasmOutputSelection {
|
||||
All,
|
||||
Wast,
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
//! The output of a compiled project
|
||||
|
||||
use crate::{
|
||||
artifacts::{CompactContractRef, Contract, Error, SourceFile, SourceFiles},
|
||||
artifacts::{
|
||||
CompactContractBytecode, CompactContractRef, Contract, Error, SourceFile, SourceFiles,
|
||||
},
|
||||
contracts::{VersionedContract, VersionedContracts},
|
||||
ArtifactOutput, Artifacts, CompilerOutput,
|
||||
ArtifactOutput, Artifacts, CompilerOutput, ConfigurableArtifacts,
|
||||
};
|
||||
use semver::Version;
|
||||
use std::{collections::BTreeMap, fmt, path::Path};
|
||||
|
@ -11,7 +13,7 @@ use std::{collections::BTreeMap, fmt, path::Path};
|
|||
/// Contains a mixture of already compiled/cached artifacts and the input set of sources that still
|
||||
/// need to be compiled.
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct ProjectCompileOutput<T: ArtifactOutput> {
|
||||
pub struct ProjectCompileOutput<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||
/// contains the aggregated `CompilerOutput`
|
||||
///
|
||||
/// See [`CompilerSources::compile`]
|
||||
|
@ -33,11 +35,11 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
|
|||
///
|
||||
/// ```no_run
|
||||
/// use std::collections::btree_map::BTreeMap;
|
||||
/// use ethers_solc::artifacts::CompactContractBytecode;
|
||||
/// use ethers_solc::ConfigurableContractArtifact;
|
||||
/// use ethers_solc::Project;
|
||||
///
|
||||
/// let project = Project::builder().build().unwrap();
|
||||
/// let contracts: BTreeMap<String, CompactContractBytecode> = project.compile().unwrap().into_artifacts().collect();
|
||||
/// let contracts: BTreeMap<String, ConfigurableContractArtifact> = project.compile().unwrap().into_artifacts().collect();
|
||||
/// ```
|
||||
pub fn into_artifacts(self) -> impl Iterator<Item = (String, T::Artifact)> {
|
||||
let Self { cached_artifacts, compiled_artifacts, .. } = self;
|
||||
|
@ -53,11 +55,10 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
|
|||
///
|
||||
/// ```no_run
|
||||
/// use std::collections::btree_map::BTreeMap;
|
||||
/// use ethers_solc::artifacts::CompactContractBytecode;
|
||||
/// use ethers_solc::Project;
|
||||
/// use ethers_solc::{ConfigurableContractArtifact, Project};
|
||||
///
|
||||
/// let project = Project::builder().build().unwrap();
|
||||
/// let contracts: Vec<(String, String, CompactContractBytecode)> = project.compile().unwrap().into_artifacts_with_files().collect();
|
||||
/// let contracts: Vec<(String, String, ConfigurableContractArtifact)> = project.compile().unwrap().into_artifacts_with_files().collect();
|
||||
/// ```
|
||||
///
|
||||
/// **NOTE** the `file` will be returned as is, see also [`Self::with_stripped_file_prefixes()`]
|
||||
|
@ -173,6 +174,27 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl ProjectCompileOutput<ConfigurableArtifacts> {
|
||||
/// A helper functions that extracts the underlying [`CompactContractBytecode`] from the
|
||||
/// [`ConfigurableContractArtifact`]
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::collections::btree_map::BTreeMap;
|
||||
/// use ethers_solc::artifacts::CompactContractBytecode;
|
||||
/// use ethers_solc::Project;
|
||||
///
|
||||
/// let project = Project::builder().build().unwrap();
|
||||
/// let contracts: BTreeMap<String, CompactContractBytecode> = project.compile().unwrap().into_contract_bytecodes().collect();
|
||||
/// ```
|
||||
pub fn into_contract_bytecodes(
|
||||
self,
|
||||
) -> impl Iterator<Item = (String, CompactContractBytecode)> {
|
||||
self.into_artifacts().map(|(name, artifact)| (name, artifact.into_contract_bytecode()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ArtifactOutput> fmt::Display for ProjectCompileOutput<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.compiler_output.is_unchanged() {
|
||||
|
|
|
@ -219,11 +219,15 @@ impl<'a, T: ArtifactOutput> CompiledState<'a, T> {
|
|||
/// Writes all output contracts to disk if enabled in the `Project`
|
||||
fn write_artifacts(self) -> Result<ArtifactsState<'a, T>> {
|
||||
let CompiledState { output, cache } = self;
|
||||
|
||||
// write all artifacts
|
||||
let compiled_artifacts = if !cache.project().no_artifacts {
|
||||
T::on_output(&output.contracts, &cache.project().paths)?
|
||||
cache
|
||||
.project()
|
||||
.artifacts_handler()
|
||||
.on_output(&output.contracts, &cache.project().paths)?
|
||||
} else {
|
||||
T::output_to_artifacts(&output.contracts)
|
||||
cache.project().artifacts_handler().output_to_artifacts(&output.contracts)
|
||||
};
|
||||
|
||||
Ok(ArtifactsState { output, cache, compiled_artifacts })
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::{
|
|||
utils, Source, Sources,
|
||||
};
|
||||
|
||||
use crate::artifacts::output_selection::ContractOutputSelection;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt::{self, Formatter},
|
||||
|
@ -465,9 +466,18 @@ impl SolcConfig {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<SolcConfig> for Settings {
|
||||
fn from(config: SolcConfig) -> Self {
|
||||
config.settings
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SolcConfigBuilder {
|
||||
settings: Option<Settings>,
|
||||
|
||||
/// additionally selected outputs that should be included in the `Contract` that `solc´ creates
|
||||
output_selection: Vec<ContractOutputSelection>,
|
||||
}
|
||||
|
||||
impl SolcConfigBuilder {
|
||||
|
@ -476,12 +486,34 @@ impl SolcConfigBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
/// Adds another `ContractOutputSelection` to the set
|
||||
#[must_use]
|
||||
pub fn additional_output(mut self, output: impl Into<ContractOutputSelection>) -> Self {
|
||||
self.output_selection.push(output.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds multiple `ContractOutputSelection` to the set
|
||||
#[must_use]
|
||||
pub fn additional_outputs<I, S>(mut self, outputs: I) -> Self
|
||||
where
|
||||
I: IntoIterator<Item = S>,
|
||||
S: Into<ContractOutputSelection>,
|
||||
{
|
||||
for out in outputs {
|
||||
self = self.additional_output(out);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Creates the solc config
|
||||
///
|
||||
/// If no solc version is configured then it will be determined by calling `solc --version`.
|
||||
pub fn build(self) -> SolcConfig {
|
||||
let Self { settings } = self;
|
||||
SolcConfig { settings: settings.unwrap_or_default() }
|
||||
let Self { settings, output_selection } = self;
|
||||
let mut settings = settings.unwrap_or_default();
|
||||
settings.push_all(output_selection);
|
||||
SolcConfig { settings }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,13 +78,15 @@ impl From<HardhatArtifact> for CompactContractBytecode {
|
|||
}
|
||||
|
||||
/// Hardhat style artifacts handler
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct HardhatArtifacts;
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)]
|
||||
pub struct HardhatArtifacts {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl ArtifactOutput for HardhatArtifacts {
|
||||
type Artifact = HardhatArtifact;
|
||||
|
||||
fn contract_to_artifact(file: &str, name: &str, contract: Contract) -> Self::Artifact {
|
||||
fn contract_to_artifact(&self, file: &str, name: &str, contract: Contract) -> Self::Artifact {
|
||||
let (bytecode, link_references, deployed_bytecode, deployed_link_references) =
|
||||
if let Some(evm) = contract.evm {
|
||||
let (deployed_bytecode, deployed_link_references) =
|
||||
|
|
|
@ -2,6 +2,7 @@ pub mod artifacts;
|
|||
pub mod sourcemap;
|
||||
|
||||
pub use artifacts::{CompilerInput, CompilerOutput, EvmVersion};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
mod artifact_output;
|
||||
pub mod cache;
|
||||
|
@ -30,13 +31,12 @@ pub mod utils;
|
|||
|
||||
use crate::{
|
||||
artifacts::{Contract, Sources},
|
||||
contracts::VersionedContracts,
|
||||
error::{SolcError, SolcIoError},
|
||||
};
|
||||
use error::Result;
|
||||
use std::{
|
||||
marker::PhantomData,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use semver::Version;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Utilities for creating, mocking and testing of (temporary) projects
|
||||
#[cfg(feature = "project-util")]
|
||||
|
@ -44,7 +44,7 @@ pub mod project_util;
|
|||
|
||||
/// Represents a project workspace and handles `solc` compiling of all contracts in that workspace.
|
||||
#[derive(Debug)]
|
||||
pub struct Project<Artifacts: ArtifactOutput = MinimalCombinedArtifacts> {
|
||||
pub struct Project<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||
/// The layout of the
|
||||
pub paths: ProjectPathsConfig,
|
||||
/// Where to find solc
|
||||
|
@ -57,8 +57,8 @@ pub struct Project<Artifacts: ArtifactOutput = MinimalCombinedArtifacts> {
|
|||
pub no_artifacts: bool,
|
||||
/// Whether writing artifacts to disk is enabled
|
||||
pub auto_detect: bool,
|
||||
/// How to handle compiler output
|
||||
pub artifacts: PhantomData<Artifacts>,
|
||||
/// Handles all artifacts related tasks, reading and writing from the artifact dir.
|
||||
pub artifacts: T,
|
||||
/// Errors/Warnings which match these error codes are not going to be logged
|
||||
pub ignored_error_codes: Vec<u64>,
|
||||
/// The paths which will be allowed for library inclusion
|
||||
|
@ -74,7 +74,7 @@ impl Project {
|
|||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Configure with `MinimalCombinedArtifacts` artifacts output
|
||||
/// Configure with `ConfigurableArtifacts` artifacts output
|
||||
///
|
||||
/// ```rust
|
||||
/// use ethers_solc::Project;
|
||||
|
@ -91,15 +91,15 @@ impl Project {
|
|||
/// or use the builder directly
|
||||
///
|
||||
/// ```rust
|
||||
/// use ethers_solc::{MinimalCombinedArtifacts, ProjectBuilder};
|
||||
/// let config = ProjectBuilder::<MinimalCombinedArtifacts>::default().build().unwrap();
|
||||
/// use ethers_solc::{ConfigurableArtifacts, ProjectBuilder};
|
||||
/// let config = ProjectBuilder::<ConfigurableArtifacts>::default().build().unwrap();
|
||||
/// ```
|
||||
pub fn builder() -> ProjectBuilder {
|
||||
ProjectBuilder::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Artifacts: ArtifactOutput> Project<Artifacts> {
|
||||
impl<T: ArtifactOutput> Project<T> {
|
||||
/// Returns the path to the artifacts directory
|
||||
pub fn artifacts_path(&self) -> &PathBuf {
|
||||
&self.paths.artifacts
|
||||
|
@ -120,6 +120,11 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
|
|||
&self.paths.root
|
||||
}
|
||||
|
||||
/// Returns the handler that takes care of processing all artifacts
|
||||
pub fn artifacts_handler(&self) -> &T {
|
||||
&self.artifacts
|
||||
}
|
||||
|
||||
/// Applies the configured settings to the given `Solc`
|
||||
fn configure_solc(&self, mut solc: Solc) -> Solc {
|
||||
if self.allowed_lib_paths.0.is_empty() {
|
||||
|
@ -187,7 +192,7 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
|
|||
/// # }
|
||||
/// ```
|
||||
#[tracing::instrument(skip_all, name = "compile")]
|
||||
pub fn compile(&self) -> Result<ProjectCompileOutput<Artifacts>> {
|
||||
pub fn compile(&self) -> Result<ProjectCompileOutput<T>> {
|
||||
let sources = self.paths.read_input_files()?;
|
||||
tracing::trace!("found {} sources to compile: {:?}", sources.len(), sources.keys());
|
||||
|
||||
|
@ -226,7 +231,7 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
|
|||
/// # }
|
||||
/// ```
|
||||
#[cfg(all(feature = "svm", feature = "async"))]
|
||||
pub fn svm_compile(&self, sources: Sources) -> Result<ProjectCompileOutput<Artifacts>> {
|
||||
pub fn svm_compile(&self, sources: Sources) -> Result<ProjectCompileOutput<T>> {
|
||||
project::ProjectCompiler::with_sources(self, sources)?.compile()
|
||||
}
|
||||
|
||||
|
@ -257,7 +262,7 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
|
|||
&self,
|
||||
solc: &Solc,
|
||||
sources: Sources,
|
||||
) -> Result<ProjectCompileOutput<Artifacts>> {
|
||||
) -> Result<ProjectCompileOutput<T>> {
|
||||
project::ProjectCompiler::with_sources_and_solc(
|
||||
self,
|
||||
sources,
|
||||
|
@ -325,7 +330,7 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ProjectBuilder<Artifacts: ArtifactOutput = MinimalCombinedArtifacts> {
|
||||
pub struct ProjectBuilder<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||
/// The layout of the
|
||||
paths: Option<ProjectPathsConfig>,
|
||||
/// Where to find solc
|
||||
|
@ -340,7 +345,8 @@ pub struct ProjectBuilder<Artifacts: ArtifactOutput = MinimalCombinedArtifacts>
|
|||
auto_detect: bool,
|
||||
/// Use offline mode
|
||||
offline: bool,
|
||||
artifacts: PhantomData<Artifacts>,
|
||||
/// handles all artifacts related tasks
|
||||
artifacts: T,
|
||||
/// Which error codes to ignore
|
||||
pub ignored_error_codes: Vec<u64>,
|
||||
/// All allowed paths
|
||||
|
@ -348,7 +354,24 @@ pub struct ProjectBuilder<Artifacts: ArtifactOutput = MinimalCombinedArtifacts>
|
|||
solc_jobs: Option<usize>,
|
||||
}
|
||||
|
||||
impl<Artifacts: ArtifactOutput> ProjectBuilder<Artifacts> {
|
||||
impl<T: ArtifactOutput> ProjectBuilder<T> {
|
||||
/// Create a new builder with the given artifacts handler
|
||||
pub fn new(artifacts: T) -> Self {
|
||||
Self {
|
||||
paths: None,
|
||||
solc: None,
|
||||
solc_config: None,
|
||||
cached: true,
|
||||
no_artifacts: false,
|
||||
auto_detect: true,
|
||||
offline: false,
|
||||
artifacts,
|
||||
ignored_error_codes: Vec::new(),
|
||||
allowed_paths: vec![],
|
||||
solc_jobs: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn paths(mut self, paths: ProjectPathsConfig) -> Self {
|
||||
self.paths = Some(paths);
|
||||
|
@ -454,7 +477,7 @@ impl<Artifacts: ArtifactOutput> ProjectBuilder<Artifacts> {
|
|||
}
|
||||
|
||||
/// Set arbitrary `ArtifactOutputHandler`
|
||||
pub fn artifacts<A: ArtifactOutput>(self) -> ProjectBuilder<A> {
|
||||
pub fn artifacts<A: ArtifactOutput>(self, artifacts: A) -> ProjectBuilder<A> {
|
||||
let ProjectBuilder {
|
||||
paths,
|
||||
solc,
|
||||
|
@ -476,7 +499,7 @@ impl<Artifacts: ArtifactOutput> ProjectBuilder<Artifacts> {
|
|||
no_artifacts,
|
||||
auto_detect,
|
||||
offline,
|
||||
artifacts: PhantomData::default(),
|
||||
artifacts,
|
||||
ignored_error_codes,
|
||||
allowed_paths,
|
||||
solc_jobs,
|
||||
|
@ -485,7 +508,7 @@ impl<Artifacts: ArtifactOutput> ProjectBuilder<Artifacts> {
|
|||
|
||||
/// Adds an allowed-path to the solc executable
|
||||
#[must_use]
|
||||
pub fn allowed_path<T: Into<PathBuf>>(mut self, path: T) -> Self {
|
||||
pub fn allowed_path<P: Into<PathBuf>>(mut self, path: P) -> Self {
|
||||
self.allowed_paths.push(path.into());
|
||||
self
|
||||
}
|
||||
|
@ -503,7 +526,7 @@ impl<Artifacts: ArtifactOutput> ProjectBuilder<Artifacts> {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<Project<Artifacts>> {
|
||||
pub fn build(self) -> Result<Project<T>> {
|
||||
let Self {
|
||||
paths,
|
||||
solc,
|
||||
|
@ -544,29 +567,85 @@ impl<Artifacts: ArtifactOutput> ProjectBuilder<Artifacts> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Artifacts: ArtifactOutput> Default for ProjectBuilder<Artifacts> {
|
||||
impl<T: ArtifactOutput + Default> Default for ProjectBuilder<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
paths: None,
|
||||
solc: None,
|
||||
solc_config: None,
|
||||
cached: true,
|
||||
no_artifacts: false,
|
||||
auto_detect: true,
|
||||
offline: false,
|
||||
artifacts: PhantomData::default(),
|
||||
ignored_error_codes: Vec::new(),
|
||||
allowed_paths: vec![],
|
||||
solc_jobs: None,
|
||||
}
|
||||
Self::new(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Artifacts: ArtifactOutput> ArtifactOutput for Project<Artifacts> {
|
||||
type Artifact = Artifacts::Artifact;
|
||||
impl<T: ArtifactOutput> ArtifactOutput for Project<T> {
|
||||
type Artifact = T::Artifact;
|
||||
|
||||
fn contract_to_artifact(file: &str, name: &str, contract: Contract) -> Self::Artifact {
|
||||
Artifacts::contract_to_artifact(file, name, contract)
|
||||
fn on_output(
|
||||
&self,
|
||||
contracts: &VersionedContracts,
|
||||
layout: &ProjectPathsConfig,
|
||||
) -> Result<Artifacts<Self::Artifact>> {
|
||||
self.artifacts_handler().on_output(contracts, layout)
|
||||
}
|
||||
|
||||
fn write_contract_extras(&self, contract: &Contract, file: &Path) -> Result<()> {
|
||||
self.artifacts_handler().write_contract_extras(contract, file)
|
||||
}
|
||||
|
||||
fn write_extras(
|
||||
&self,
|
||||
contracts: &VersionedContracts,
|
||||
layout: &ProjectPathsConfig,
|
||||
) -> Result<()> {
|
||||
self.artifacts_handler().write_extras(contracts, layout)
|
||||
}
|
||||
|
||||
fn output_file_name(name: impl AsRef<str>) -> PathBuf {
|
||||
T::output_file_name(name)
|
||||
}
|
||||
|
||||
fn output_file_name_versioned(name: impl AsRef<str>, version: &Version) -> PathBuf {
|
||||
T::output_file_name_versioned(name, version)
|
||||
}
|
||||
|
||||
fn output_file(contract_file: impl AsRef<Path>, name: impl AsRef<str>) -> PathBuf {
|
||||
T::output_file(contract_file, name)
|
||||
}
|
||||
|
||||
fn output_file_versioned(
|
||||
contract_file: impl AsRef<Path>,
|
||||
name: impl AsRef<str>,
|
||||
version: &Version,
|
||||
) -> PathBuf {
|
||||
T::output_file_versioned(contract_file, name, version)
|
||||
}
|
||||
|
||||
fn contract_name(file: impl AsRef<Path>) -> Option<String> {
|
||||
T::contract_name(file)
|
||||
}
|
||||
|
||||
fn output_exists(
|
||||
contract_file: impl AsRef<Path>,
|
||||
name: impl AsRef<str>,
|
||||
root: impl AsRef<Path>,
|
||||
) -> bool {
|
||||
T::output_exists(contract_file, name, root)
|
||||
}
|
||||
|
||||
fn read_cached_artifact(path: impl AsRef<Path>) -> Result<Self::Artifact> {
|
||||
T::read_cached_artifact(path)
|
||||
}
|
||||
|
||||
fn read_cached_artifacts<P, I>(files: I) -> Result<BTreeMap<PathBuf, Self::Artifact>>
|
||||
where
|
||||
I: IntoIterator<Item = P>,
|
||||
P: Into<PathBuf>,
|
||||
{
|
||||
T::read_cached_artifacts(files)
|
||||
}
|
||||
|
||||
fn contract_to_artifact(&self, file: &str, name: &str, contract: Contract) -> Self::Artifact {
|
||||
self.artifacts_handler().contract_to_artifact(file, name, contract)
|
||||
}
|
||||
|
||||
fn output_to_artifacts(&self, contracts: &VersionedContracts) -> Artifacts<Self::Artifact> {
|
||||
self.artifacts_handler().output_to_artifacts(contracts)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
//! Utilities for mocking project workspaces
|
||||
use crate::{
|
||||
artifacts::Settings,
|
||||
config::ProjectPathsConfigBuilder,
|
||||
error::{Result, SolcError},
|
||||
hh::HardhatArtifacts,
|
||||
utils::tempdir,
|
||||
ArtifactOutput, MinimalCombinedArtifacts, PathStyle, Project, ProjectCompileOutput,
|
||||
ArtifactOutput, ConfigurableArtifacts, PathStyle, Project, ProjectCompileOutput,
|
||||
ProjectPathsConfig, SolcIoError,
|
||||
};
|
||||
use fs_extra::{dir, file};
|
||||
|
@ -17,7 +18,7 @@ use tempfile::TempDir;
|
|||
/// A [`Project`] wrapper that lives in a new temporary directory
|
||||
///
|
||||
/// Once `TempProject` is dropped, the temp dir is automatically removed, see [`TempDir::drop()`]
|
||||
pub struct TempProject<T: ArtifactOutput = MinimalCombinedArtifacts> {
|
||||
pub struct TempProject<T: ArtifactOutput = ConfigurableArtifacts> {
|
||||
/// temporary workspace root
|
||||
_root: TempDir,
|
||||
/// actual project workspace with the `root` tempdir as its root
|
||||
|
@ -32,26 +33,29 @@ impl<T: ArtifactOutput> TempProject<T> {
|
|||
Ok(project)
|
||||
}
|
||||
|
||||
/// Creates a new temp project inside a tempdir with a prefixed directory
|
||||
pub fn prefixed(prefix: &str, paths: ProjectPathsConfigBuilder) -> Result<Self> {
|
||||
/// Creates a new temp project using the provided paths and artifacts handler.
|
||||
/// sets the project root to a temp dir
|
||||
pub fn with_artifacts(paths: ProjectPathsConfigBuilder, artifacts: T) -> Result<Self> {
|
||||
Self::prefixed_with_artifacts("temp-project", paths, artifacts)
|
||||
}
|
||||
|
||||
/// Creates a new temp project inside a tempdir with a prefixed directory and the given
|
||||
/// artifacts handler
|
||||
pub fn prefixed_with_artifacts(
|
||||
prefix: &str,
|
||||
paths: ProjectPathsConfigBuilder,
|
||||
artifacts: T,
|
||||
) -> Result<Self> {
|
||||
let tmp_dir = tempdir(prefix)?;
|
||||
let paths = paths.build_with_root(tmp_dir.path());
|
||||
let inner = Project::builder().artifacts().paths(paths).build()?;
|
||||
let inner = Project::builder().artifacts(artifacts).paths(paths).build()?;
|
||||
Ok(Self::create_new(tmp_dir, inner)?)
|
||||
}
|
||||
|
||||
/// Creates a new temp project for the given `PathStyle`
|
||||
pub fn with_style(prefix: &str, style: PathStyle) -> Result<Self> {
|
||||
let tmp_dir = tempdir(prefix)?;
|
||||
let paths = style.paths(tmp_dir.path())?;
|
||||
let inner = Project::builder().artifacts().paths(paths).build()?;
|
||||
Ok(Self::create_new(tmp_dir, inner)?)
|
||||
}
|
||||
|
||||
/// Creates a new temp project using the provided paths and setting the project root to a temp
|
||||
/// dir
|
||||
pub fn new(paths: ProjectPathsConfigBuilder) -> Result<Self> {
|
||||
Self::prefixed("temp-project", paths)
|
||||
/// Overwrites the settings to pass to `solc`
|
||||
pub fn with_settings(mut self, settings: impl Into<Settings>) -> Self {
|
||||
self.inner.solc_config.settings = settings.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn project(&self) -> &Project<T> {
|
||||
|
@ -158,6 +162,27 @@ impl<T: ArtifactOutput> TempProject<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ArtifactOutput + Default> TempProject<T> {
|
||||
/// Creates a new temp project inside a tempdir with a prefixed directory
|
||||
pub fn prefixed(prefix: &str, paths: ProjectPathsConfigBuilder) -> Result<Self> {
|
||||
Self::prefixed_with_artifacts(prefix, paths, T::default())
|
||||
}
|
||||
|
||||
/// Creates a new temp project for the given `PathStyle`
|
||||
pub fn with_style(prefix: &str, style: PathStyle) -> Result<Self> {
|
||||
let tmp_dir = tempdir(prefix)?;
|
||||
let paths = style.paths(tmp_dir.path())?;
|
||||
let inner = Project::builder().artifacts(T::default()).paths(paths).build()?;
|
||||
Ok(Self::create_new(tmp_dir, inner)?)
|
||||
}
|
||||
|
||||
/// Creates a new temp project using the provided paths and setting the project root to a temp
|
||||
/// dir
|
||||
pub fn new(paths: ProjectPathsConfigBuilder) -> Result<Self> {
|
||||
Self::prefixed("temp-project", paths)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ArtifactOutput> fmt::Debug for TempProject<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("TempProject").field("paths", self.paths()).finish()
|
||||
|
@ -189,18 +214,19 @@ impl TempProject<HardhatArtifacts> {
|
|||
|
||||
let paths = ProjectPathsConfig::hardhat(tmp_dir.path())?;
|
||||
|
||||
let inner = Project::builder().artifacts().paths(paths).build()?;
|
||||
let inner =
|
||||
Project::builder().artifacts(HardhatArtifacts::default()).paths(paths).build()?;
|
||||
Ok(Self::create_new(tmp_dir, inner)?)
|
||||
}
|
||||
}
|
||||
|
||||
impl TempProject<MinimalCombinedArtifacts> {
|
||||
impl TempProject<ConfigurableArtifacts> {
|
||||
/// Creates an empty new dapptools style workspace in a new temporary dir
|
||||
pub fn dapptools() -> Result<Self> {
|
||||
let tmp_dir = tempdir("tmp_dapp")?;
|
||||
let paths = ProjectPathsConfig::dapptools(tmp_dir.path())?;
|
||||
|
||||
let inner = Project::builder().artifacts().paths(paths).build()?;
|
||||
let inner = Project::builder().paths(paths).build()?;
|
||||
Ok(Self::create_new(tmp_dir, inner)?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,8 @@ use ethers_solc::{
|
|||
cache::{SolFilesCache, SOLIDITY_FILES_CACHE_FILENAME},
|
||||
project_util::*,
|
||||
remappings::Remapping,
|
||||
Graph, MinimalCombinedArtifacts, Project, ProjectCompileOutput, ProjectPathsConfig,
|
||||
ConfigurableArtifacts, ExtraOutputValues, Graph, Project, ProjectCompileOutput,
|
||||
ProjectPathsConfig,
|
||||
};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
|
@ -28,7 +29,7 @@ fn can_compile_hardhat_sample() {
|
|||
let paths = ProjectPathsConfig::builder()
|
||||
.sources(root.join("contracts"))
|
||||
.lib(root.join("node_modules"));
|
||||
let project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();
|
||||
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||
|
||||
let compiled = project.compile().unwrap();
|
||||
assert!(compiled.find("Greeter").is_some());
|
||||
|
@ -53,7 +54,7 @@ fn can_compile_hardhat_sample() {
|
|||
fn can_compile_dapp_sample() {
|
||||
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 project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();
|
||||
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||
|
||||
let compiled = project.compile().unwrap();
|
||||
assert!(compiled.find("Dapp").is_some());
|
||||
|
@ -76,9 +77,33 @@ fn can_compile_dapp_sample() {
|
|||
assert_eq!(cache, updated_cache);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_compile_configured() {
|
||||
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 handler = ConfigurableArtifacts {
|
||||
additional_values: ExtraOutputValues {
|
||||
metadata: true,
|
||||
ir: true,
|
||||
ir_optimized: true,
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let settings = handler.settings();
|
||||
let project = TempProject::with_artifacts(paths, handler).unwrap().with_settings(settings);
|
||||
let compiled = project.compile().unwrap();
|
||||
let artifact = compiled.find("Dapp").unwrap();
|
||||
assert!(artifact.metadata.is_some());
|
||||
assert!(artifact.ir.is_some());
|
||||
assert!(artifact.ir_optimized.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_compile_dapp_detect_changes_in_libs() {
|
||||
let mut project = TempProject::<MinimalCombinedArtifacts>::dapptools().unwrap();
|
||||
let mut project = TempProject::<ConfigurableArtifacts>::dapptools().unwrap();
|
||||
|
||||
let remapping = project.paths().libraries[0].join("remapping");
|
||||
project
|
||||
|
@ -151,8 +176,7 @@ fn can_compile_dapp_detect_changes_in_libs() {
|
|||
|
||||
#[test]
|
||||
fn can_compile_dapp_detect_changes_in_sources() {
|
||||
init_tracing();
|
||||
let project = TempProject::<MinimalCombinedArtifacts>::dapptools().unwrap();
|
||||
let project = TempProject::<ConfigurableArtifacts>::dapptools().unwrap();
|
||||
|
||||
let src = project
|
||||
.add_source(
|
||||
|
@ -330,7 +354,7 @@ fn can_flatten_file() {
|
|||
.sources(root.join("src"))
|
||||
.lib(root.join("lib1"))
|
||||
.lib(root.join("lib2"));
|
||||
let project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();
|
||||
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||
|
||||
let result = project.flatten(&target);
|
||||
assert!(result.is_ok());
|
||||
|
@ -346,7 +370,7 @@ fn can_flatten_file_with_external_lib() {
|
|||
let paths = ProjectPathsConfig::builder()
|
||||
.sources(root.join("contracts"))
|
||||
.lib(root.join("node_modules"));
|
||||
let project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();
|
||||
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||
|
||||
let target = root.join("contracts").join("Greeter.sol");
|
||||
|
||||
|
@ -362,7 +386,7 @@ fn can_flatten_file_with_external_lib() {
|
|||
fn can_flatten_file_in_dapp_sample() {
|
||||
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 project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();
|
||||
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||
|
||||
let target = root.join("src/Dapp.t.sol");
|
||||
|
||||
|
@ -379,7 +403,7 @@ fn can_flatten_file_in_dapp_sample() {
|
|||
fn can_flatten_file_with_duplicates() {
|
||||
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/flatten-sample");
|
||||
let paths = ProjectPathsConfig::builder().sources(root.join("contracts"));
|
||||
let project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();
|
||||
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();
|
||||
|
||||
let target = root.join("contracts/FooBar.sol");
|
||||
|
||||
|
@ -395,7 +419,7 @@ fn can_flatten_file_with_duplicates() {
|
|||
|
||||
#[test]
|
||||
fn can_detect_type_error() {
|
||||
let project = TempProject::<MinimalCombinedArtifacts>::dapptools().unwrap();
|
||||
let project = TempProject::<ConfigurableArtifacts>::dapptools().unwrap();
|
||||
|
||||
project
|
||||
.add_source(
|
||||
|
|
Loading…
Reference in New Issue