Skip to content
Merged
2 changes: 1 addition & 1 deletion crates/artifacts/solc/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ pub struct CompactContractRefSome<'a> {
pub bin_runtime: &'a BytecodeObject,
}

impl<'a> CompactContractRefSome<'a> {
impl CompactContractRefSome<'_> {
/// Returns the individual parts of this contract.
///
/// If the values are `None`, then `Default` is returned.
Expand Down
2 changes: 1 addition & 1 deletion crates/artifacts/solc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,7 @@ impl<'de> Deserialize<'de> for LosslessMetadata {
{
struct LosslessMetadataVisitor;

impl<'de> Visitor<'de> for LosslessMetadataVisitor {
impl Visitor<'_> for LosslessMetadataVisitor {
type Value = LosslessMetadata;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand Down
6 changes: 3 additions & 3 deletions crates/artifacts/solc/src/sourcemap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ enum Token<'a> {
Regular,
}

impl<'a> fmt::Debug for Token<'a> {
impl fmt::Debug for Token<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Token::Number(s) => write!(f, "NUMBER({s:?})"),
Expand All @@ -105,7 +105,7 @@ impl<'a> fmt::Debug for Token<'a> {
}
}

impl<'a> fmt::Display for Token<'a> {
impl fmt::Display for Token<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Token::Number(_) => write!(f, "number"),
Expand Down Expand Up @@ -531,7 +531,7 @@ impl<'input> Parser<'input> {
}
}

impl<'input> Iterator for Parser<'input> {
impl Iterator for Parser<'_> {
type Item = Result<SourceElement, SyntaxError>;

fn next(&mut self) -> Option<Self::Item> {
Expand Down
136 changes: 75 additions & 61 deletions crates/compilers/src/artifact_output/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ mod hh;
pub use hh::*;

use crate::{
cache::{CachedArtifact, CompilerCache},
cache::{CachedArtifacts, CompilerCache},
output::{
contracts::VersionedContracts,
sources::{VersionedSourceFile, VersionedSourceFiles},
Expand All @@ -52,6 +52,7 @@ pub struct ArtifactId {
pub version: Version,
/// `solc` build id
pub build_id: String,
pub profile: String,
}

impl ArtifactId {
Expand Down Expand Up @@ -119,6 +120,7 @@ pub struct ArtifactFile<T> {
/// `solc` version that produced this artifact
pub version: Version,
pub build_id: String,
pub profile: String,
}

impl<T: Serialize> ArtifactFile<T> {
Expand Down Expand Up @@ -298,6 +300,7 @@ impl<T> Artifacts<T> {
source: source.clone(),
version: artifact.version.clone(),
build_id: artifact.build_id.clone(),
profile: artifact.profile.clone(),
}
.with_slashed_paths(),
&artifact.artifact,
Expand All @@ -324,6 +327,7 @@ impl<T> Artifacts<T> {
source: source.clone(),
version: artifact.version,
build_id: artifact.build_id.clone(),
profile: artifact.profile.clone(),
}
.with_slashed_paths(),
artifact.artifact,
Expand Down Expand Up @@ -642,14 +646,22 @@ pub trait ArtifactOutput {

/// Returns the file name for the contract's artifact
/// `Greeter.json`
fn output_file_name(name: &str) -> PathBuf {
format!("{name}.json").into()
}

/// Returns the file name for the contract's artifact and the given version
/// `Greeter.0.8.11.json`
fn output_file_name_versioned(name: &str, version: &Version) -> PathBuf {
format!("{}.{}.{}.{}.json", name, version.major, version.minor, version.patch).into()
fn output_file_name(
name: &str,
version: &Version,
profile: &str,
with_version: bool,
with_profile: bool,
) -> PathBuf {
let mut name = name.to_string();
if with_version {
name.push_str(&format!(".{}.{}.{}", version.major, version.minor, version.patch));
}
if with_profile {
name.push_str(&format!(".{profile}"));
}
name.push_str(".json");
name.into()
}

/// Returns the appropriate file name for the conflicting file.
Expand Down Expand Up @@ -724,24 +736,23 @@ pub trait ArtifactOutput {
/// Returns the path to the contract's artifact location based on the contract's file and name
///
/// This returns `contract.sol/contract.json` by default
fn output_file(contract_file: &Path, name: &str) -> PathBuf {
contract_file
.file_name()
.map(Path::new)
.map(|p| p.join(Self::output_file_name(name)))
.unwrap_or_else(|| Self::output_file_name(name))
}

/// Returns the path to the contract's artifact location based on the contract's file, name and
/// version
///
/// This returns `contract.sol/contract.0.8.11.json` by default
fn output_file_versioned(contract_file: &Path, name: &str, version: &Version) -> PathBuf {
fn output_file(
contract_file: &Path,
name: &str,
version: &Version,
profile: &str,
with_version: bool,
with_profile: bool,
) -> PathBuf {
contract_file
.file_name()
.map(Path::new)
.map(|p| p.join(Self::output_file_name_versioned(name, version)))
.unwrap_or_else(|| Self::output_file_name_versioned(name, version))
.map(|p| {
p.join(Self::output_file_name(name, version, profile, with_version, with_profile))
})
.unwrap_or_else(|| {
Self::output_file_name(name, version, profile, with_version, with_profile)
})
}

/// The inverse of `contract_file_name`
Expand All @@ -752,11 +763,6 @@ pub trait ArtifactOutput {
file.file_stem().and_then(|s| s.to_str().map(|s| s.to_string()))
}

/// Whether the corresponding artifact of the given contract file and name exists
fn output_exists(contract_file: &Path, name: &str, root: &Path) -> bool {
root.join(Self::output_file(contract_file, name)).exists()
}

/// Read the artifact that's stored at the given path
///
/// # Errors
Expand Down Expand Up @@ -800,28 +806,27 @@ pub trait ArtifactOutput {

/// Generates a path for an artifact based on already taken paths by either cached or compiled
/// artifacts.
#[allow(clippy::too_many_arguments)]
fn get_artifact_path(
ctx: &OutputContext<'_>,
already_taken: &HashSet<String>,
file: &Path,
name: &str,
artifacts_folder: &Path,
version: &Version,
versioned: bool,
profile: &str,
with_version: bool,
with_profile: bool,
) -> PathBuf {
// if an artifact for the contract already exists (from a previous compile job)
// we reuse the path, this will make sure that even if there are conflicting
// files (files for witch `T::output_file()` would return the same path) we use
// consistent output paths
if let Some(existing_artifact) = ctx.existing_artifact(file, name, version) {
if let Some(existing_artifact) = ctx.existing_artifact(file, name, version, profile) {
trace!("use existing artifact file {:?}", existing_artifact,);
existing_artifact.to_path_buf()
} else {
let path = if versioned {
Self::output_file_versioned(file, name, version)
} else {
Self::output_file(file, name)
};
let path = Self::output_file(file, name, version, profile, with_version, with_profile);

let path = artifacts_folder.join(path);

Expand Down Expand Up @@ -854,7 +859,9 @@ pub trait ArtifactOutput {
let mut taken_paths_lowercase = ctx
.existing_artifacts
.values()
.flat_map(|artifacts| artifacts.values().flat_map(|artifacts| artifacts.values()))
.flat_map(|artifacts| artifacts.values())
.flat_map(|artifacts| artifacts.values())
.flat_map(|artifacts| artifacts.values())
.map(|a| a.path.to_slash_lossy().to_lowercase())
.collect::<HashSet<_>>();

Expand All @@ -865,22 +872,26 @@ pub trait ArtifactOutput {
});
for file in files {
for (name, versioned_contracts) in &contracts[file] {
let unique_versions =
versioned_contracts.iter().map(|c| &c.version).collect::<HashSet<_>>();
let unique_profiles =
versioned_contracts.iter().map(|c| &c.profile).collect::<HashSet<_>>();
for contract in versioned_contracts {
non_standalone_sources.insert(file);

// track `SourceFile`s that can be mapped to contracts
let source_file = sources.find_file_and_version(file, &contract.version);

if let Some(source) = source_file {
non_standalone_sources.insert((source.id, &contract.version));
}

let artifact_path = Self::get_artifact_path(
&ctx,
&taken_paths_lowercase,
file,
name,
layout.artifacts.as_path(),
&contract.version,
versioned_contracts.len() > 1,
&contract.profile,
unique_versions.len() > 1,
unique_profiles.len() > 1,
);

taken_paths_lowercase.insert(artifact_path.to_slash_lossy().to_lowercase());
Expand All @@ -904,6 +915,7 @@ pub trait ArtifactOutput {
file: artifact_path,
version: contract.version.clone(),
build_id: contract.build_id.clone(),
profile: contract.profile.clone(),
};

artifacts
Expand All @@ -921,8 +933,10 @@ pub trait ArtifactOutput {
// any contract definition, which are not included in the `CompilerOutput` but we want to
// create Artifacts for them regardless
for (file, sources) in sources.as_ref().iter() {
let unique_versions = sources.iter().map(|s| &s.version).collect::<HashSet<_>>();
let unique_profiles = sources.iter().map(|s| &s.profile).collect::<HashSet<_>>();
for source in sources {
if !non_standalone_sources.contains(&(source.source_file.id, &source.version)) {
if !non_standalone_sources.contains(file) {
// scan the ast as a safe measure to ensure this file does not include any
// source units
// there's also no need to create a standalone artifact for source files that
Expand All @@ -945,26 +959,26 @@ pub trait ArtifactOutput {
name,
&layout.artifacts,
&source.version,
sources.len() > 1,
&source.profile,
unique_versions.len() > 1,
unique_profiles.len() > 1,
);

let entries = artifacts
taken_paths_lowercase
.insert(artifact_path.to_slash_lossy().to_lowercase());

artifacts
.entry(file.clone())
.or_default()
.entry(name.to_string())
.or_default();

if entries.iter().all(|entry| entry.version != source.version) {
taken_paths_lowercase
.insert(artifact_path.to_slash_lossy().to_lowercase());

entries.push(ArtifactFile {
.or_default()
.push(ArtifactFile {
artifact,
file: artifact_path,
version: source.version.clone(),
build_id: source.build_id.clone(),
profile: source.profile.clone(),
});
}
}
}
}
Expand Down Expand Up @@ -1015,8 +1029,7 @@ pub struct OutputContext<'a> {
/// └── inner
/// └── a.sol
/// ```
pub existing_artifacts:
BTreeMap<&'a Path, &'a BTreeMap<String, BTreeMap<Version, CachedArtifact>>>,
pub existing_artifacts: BTreeMap<&'a Path, &'a CachedArtifacts>,
}

// === impl OutputContext
Expand All @@ -1042,13 +1055,14 @@ impl<'a> OutputContext<'a> {
file: &Path,
contract: &str,
version: &Version,
profile: &str,
) -> Option<&Path> {
self.existing_artifacts.get(file).and_then(|contracts| {
contracts
.get(contract)
.and_then(|versions| versions.get(version))
.map(|a| a.path.as_path())
})
self.existing_artifacts
.get(file)
.and_then(|contracts| contracts.get(contract))
.and_then(|versions| versions.get(version))
.and_then(|profiles| profiles.get(profile))
.map(|a| a.path.as_path())
}
}

Expand Down
Loading