diff --git a/ethers-solc/Cargo.toml b/ethers-solc/Cargo.toml index d3aaa7fe..cfe4c78e 100644 --- a/ethers-solc/Cargo.toml +++ b/ethers-solc/Cargo.toml @@ -68,7 +68,7 @@ harness = false [[test]] name = "project" path = "tests/project.rs" -required-features = ["project-util"] +required-features = ["async", "svm", "project-util"] [features] default = ["rustls"] diff --git a/ethers-solc/src/artifact_output/mod.rs b/ethers-solc/src/artifact_output/mod.rs index 1240e557..0025ae2c 100644 --- a/ethers-solc/src/artifact_output/mod.rs +++ b/ethers-solc/src/artifact_output/mod.rs @@ -18,6 +18,36 @@ use std::{ mod configurable; pub use configurable::*; +/// Represents unique artifact metadata for identifying artifacts on output +#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] +pub struct ArtifactId { + /// `artifact` cache path + pub path: PathBuf, + pub name: String, + /// Original source file path + pub source: PathBuf, + /// `solc` version that produced this artifact + pub version: Version, +} + +impl ArtifactId { + /// Returns a : slug that identifies an artifact + pub fn slug(&self) -> String { + format!("{}.json:{}", self.path.file_stem().unwrap().to_string_lossy(), self.name) + } + /// Returns a : slug that identifies an artifact + pub fn slug_versioned(&self) -> String { + format!( + "{}.{}.{}.{}.json:{}", + self.path.file_stem().unwrap().to_string_lossy(), + self.version.major, + self.version.minor, + self.version.patch, + self.name + ) + } +} + /// Represents an artifact file representing a [`crate::Contract`] #[derive(Debug, Clone, PartialEq)] pub struct ArtifactFile { @@ -162,17 +192,19 @@ impl Artifacts { /// Returns an iterator over _all_ artifacts and `` pub fn into_artifacts>( self, - ) -> impl Iterator { - self.0.into_values().flat_map(|contract_artifacts| { - contract_artifacts.into_iter().flat_map(|(_contract_name, artifacts)| { - artifacts.into_iter().filter_map(|artifact| { + ) -> impl Iterator { + self.0.into_iter().flat_map(|(file, contract_artifacts)| { + contract_artifacts.into_iter().flat_map(move |(_contract_name, artifacts)| { + let source = PathBuf::from(file.clone()); + artifacts.into_iter().filter_map(move |artifact| { O::contract_name(&artifact.file).map(|name| { ( - format!( - "{}:{}", - artifact.file.file_name().unwrap().to_string_lossy(), - name - ), + ArtifactId { + path: PathBuf::from(&artifact.file), + name, + source: source.clone(), + version: artifact.version, + }, artifact.artifact, ) }) diff --git a/ethers-solc/src/compile/output.rs b/ethers-solc/src/compile/output.rs index 2acd1079..037a0885 100644 --- a/ethers-solc/src/compile/output.rs +++ b/ethers-solc/src/compile/output.rs @@ -5,7 +5,7 @@ use crate::{ CompactContractBytecode, CompactContractRef, Contract, Error, SourceFile, SourceFiles, }, contracts::{VersionedContract, VersionedContracts}, - ArtifactOutput, Artifacts, CompilerOutput, ConfigurableArtifacts, + ArtifactId, ArtifactOutput, Artifacts, CompilerOutput, ConfigurableArtifacts, }; use semver::Version; use std::{collections::BTreeMap, fmt, path::Path}; @@ -36,12 +36,12 @@ impl ProjectCompileOutput { /// ```no_run /// use std::collections::btree_map::BTreeMap; /// use ethers_solc::ConfigurableContractArtifact; - /// use ethers_solc::Project; + /// use ethers_solc::{ArtifactId, Project}; /// /// let project = Project::builder().build().unwrap(); - /// let contracts: BTreeMap = project.compile().unwrap().into_artifacts().collect(); + /// let contracts: BTreeMap = project.compile().unwrap().into_artifacts().collect(); /// ``` - pub fn into_artifacts(self) -> impl Iterator { + pub fn into_artifacts(self) -> impl Iterator { let Self { cached_artifacts, compiled_artifacts, .. } = self; cached_artifacts.into_artifacts::().chain(compiled_artifacts.into_artifacts::()) } @@ -183,15 +183,16 @@ impl ProjectCompileOutput { /// ```no_run /// use std::collections::btree_map::BTreeMap; /// use ethers_solc::artifacts::CompactContractBytecode; - /// use ethers_solc::Project; + /// use ethers_solc::{ArtifactId, Project}; /// /// let project = Project::builder().build().unwrap(); - /// let contracts: BTreeMap = project.compile().unwrap().into_contract_bytecodes().collect(); + /// let contracts: BTreeMap = project.compile().unwrap().into_contract_bytecodes().collect(); /// ``` pub fn into_contract_bytecodes( self, - ) -> impl Iterator { - self.into_artifacts().map(|(name, artifact)| (name, artifact.into_contract_bytecode())) + ) -> impl Iterator { + self.into_artifacts() + .map(|(artifact_id, artifact)| (artifact_id, artifact.into_contract_bytecode())) } } diff --git a/ethers-solc/test-data/test-versioned-linkrefs/lib/Lib.sol b/ethers-solc/test-data/test-versioned-linkrefs/lib/Lib.sol new file mode 100644 index 00000000..0512682d --- /dev/null +++ b/ethers-solc/test-data/test-versioned-linkrefs/lib/Lib.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >0.4.0; + +contract Lib {} diff --git a/ethers-solc/test-data/test-versioned-linkrefs/src/FooV1.sol b/ethers-solc/test-data/test-versioned-linkrefs/src/FooV1.sol new file mode 100644 index 00000000..a42076c1 --- /dev/null +++ b/ethers-solc/test-data/test-versioned-linkrefs/src/FooV1.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity <=0.8.10; + +import "../lib/Lib.sol"; + +contract FooV1 {} diff --git a/ethers-solc/test-data/test-versioned-linkrefs/src/FooV2.sol b/ethers-solc/test-data/test-versioned-linkrefs/src/FooV2.sol new file mode 100644 index 00000000..1e1ce6be --- /dev/null +++ b/ethers-solc/test-data/test-versioned-linkrefs/src/FooV2.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.11; + +import "../lib/Lib.sol"; + +contract FooV2 {} diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs index 882a8975..ac8c01f7 100644 --- a/ethers-solc/tests/project.rs +++ b/ethers-solc/tests/project.rs @@ -23,6 +23,24 @@ fn init_tracing() { .init(); } +#[test] +#[ignore] +fn can_get_versioned_linkrefs() { + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/test-versioned-linkrefs"); + let paths = ProjectPathsConfig::builder() + .sources(root.join("src")) + .lib(root.join("lib")) + .build() + .unwrap(); + + let project = Project::builder().paths(paths).ephemeral().no_artifacts().build().unwrap(); + + let compiled = project.compile().unwrap(); + assert!(!compiled.has_compiler_errors()); + + // TODO: +} + #[test] fn can_compile_hardhat_sample() { let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/hardhat-sample"); @@ -304,12 +322,12 @@ fn can_compile_dapp_sample_with_cache() { assert!(compiled.find("NewContract").is_some()); assert!(!compiled.is_unchanged()); assert_eq!( - compiled.into_artifacts().map(|(name, _)| name).collect::>(), + compiled.into_artifacts().map(|(artifact_id, _)| artifact_id.name).collect::>(), HashSet::from([ - "Dapp.json:Dapp".to_string(), - "DappTest.json:DappTest".to_string(), - "DSTest.json:DSTest".to_string(), - "NewContract.json:NewContract".to_string(), + "Dapp".to_string(), + "DappTest".to_string(), + "DSTest".to_string(), + "NewContract".to_string(), ]) ); @@ -317,12 +335,12 @@ fn can_compile_dapp_sample_with_cache() { std::fs::copy(cache_testdata_dir.join("Dapp.sol"), root.join("src/Dapp.sol")).unwrap(); let compiled = project.compile().unwrap(); assert_eq!( - compiled.into_artifacts().map(|(name, _)| name).collect::>(), + compiled.into_artifacts().map(|(artifact_id, _)| artifact_id.name).collect::>(), HashSet::from([ - "DappTest.json:DappTest".to_string(), - "NewContract.json:NewContract".to_string(), - "DSTest.json:DSTest".to_string(), - "Dapp.json:Dapp".to_string(), + "DappTest".to_string(), + "NewContract".to_string(), + "DSTest".to_string(), + "Dapp".to_string(), ]) );