feat(solc): include project paths in cache (#1097)

* feat: add project paths struct

* feat: add project paths to cache file

* chore: bump format version

* update cache creation api

* feat: add cache condition
This commit is contained in:
Matthias Seitz 2022-03-30 21:14:29 +02:00 committed by GitHub
parent 842f4d260f
commit 26b6472f9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 112 additions and 12 deletions

View File

@ -1,7 +1,7 @@
//! Support for compiling contracts //! Support for compiling contracts
use crate::{ use crate::{
artifacts::Sources, artifacts::Sources,
config::SolcConfig, config::{ProjectPaths, SolcConfig},
error::{Result, SolcError}, error::{Result, SolcError},
filter::{FilteredSource, FilteredSourceInfo, FilteredSources}, filter::{FilteredSource, FilteredSourceInfo, FilteredSources},
resolver::GraphEdges, resolver::GraphEdges,
@ -25,7 +25,7 @@ use std::{
/// `ethers-solc` uses a different format version id, but the actual format is consistent with /// `ethers-solc` uses a different format version id, but the actual format is consistent with
/// hardhat This allows ethers-solc to detect if the cache file was written by hardhat or /// hardhat This allows ethers-solc to detect if the cache file was written by hardhat or
/// `ethers-solc` /// `ethers-solc`
const ETHERS_FORMAT_VERSION: &str = "ethers-rs-sol-cache-2"; const ETHERS_FORMAT_VERSION: &str = "ethers-rs-sol-cache-3";
/// The file name of the default cache file /// The file name of the default cache file
pub const SOLIDITY_FILES_CACHE_FILENAME: &str = "solidity-files-cache.json"; pub const SOLIDITY_FILES_CACHE_FILENAME: &str = "solidity-files-cache.json";
@ -35,13 +35,15 @@ pub const SOLIDITY_FILES_CACHE_FILENAME: &str = "solidity-files-cache.json";
pub struct SolFilesCache { pub struct SolFilesCache {
#[serde(rename = "_format")] #[serde(rename = "_format")]
pub format: String, pub format: String,
/// contains all directories used for the project
pub paths: ProjectPaths,
pub files: BTreeMap<PathBuf, CacheEntry>, pub files: BTreeMap<PathBuf, CacheEntry>,
} }
impl SolFilesCache { impl SolFilesCache {
/// Create a new cache instance with the given files /// Create a new cache instance with the given files
pub fn new(files: BTreeMap<PathBuf, CacheEntry>) -> Self { pub fn new(files: BTreeMap<PathBuf, CacheEntry>, paths: ProjectPaths) -> Self {
Self { format: ETHERS_FORMAT_VERSION.to_string(), files } Self { format: ETHERS_FORMAT_VERSION.to_string(), files, paths }
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
@ -348,7 +350,18 @@ impl SolFilesCache {
impl Default for SolFilesCache { impl Default for SolFilesCache {
fn default() -> Self { fn default() -> Self {
SolFilesCache { format: ETHERS_FORMAT_VERSION.to_string(), files: Default::default() } SolFilesCache {
format: ETHERS_FORMAT_VERSION.to_string(),
files: Default::default(),
paths: Default::default(),
}
}
}
impl<'a> From<&'a ProjectPathsConfig> for SolFilesCache {
fn from(config: &'a ProjectPathsConfig) -> Self {
let paths = config.paths_relative();
SolFilesCache::new(Default::default(), paths)
} }
} }
@ -741,13 +754,26 @@ pub(crate) enum ArtifactsCache<'a, T: ArtifactOutput> {
impl<'a, T: ArtifactOutput> ArtifactsCache<'a, T> { impl<'a, T: ArtifactOutput> ArtifactsCache<'a, T> {
pub fn new(project: &'a Project<T>, edges: GraphEdges) -> Result<Self> { pub fn new(project: &'a Project<T>, edges: GraphEdges) -> Result<Self> {
/// returns the [SolFilesCache] to use
fn get_cache<T: ArtifactOutput>(project: &Project<T>) -> SolFilesCache {
// the currently configured paths
let paths = project.paths.paths_relative();
if project.cache_path().exists() {
if let Ok(cache) = SolFilesCache::read_joined(&project.paths) {
if cache.paths == paths {
// unchanged project paths
return cache
}
}
}
// new empty cache
SolFilesCache::new(Default::default(), paths)
}
let cache = if project.cached { let cache = if project.cached {
// read the cache file if it already exists // read the cache file if it already exists
let mut cache = if project.cache_path().exists() { let mut cache = get_cache(project);
SolFilesCache::read_joined(&project.paths).unwrap_or_default()
} else {
SolFilesCache::default()
};
cache.remove_missing_files(); cache.remove_missing_files();

View File

@ -10,14 +10,14 @@ use crate::{
use crate::artifacts::output_selection::ContractOutputSelection; use crate::artifacts::output_selection::ContractOutputSelection;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
collections::HashSet, collections::{BTreeSet, HashSet},
fmt::{self, Formatter}, fmt::{self, Formatter},
fs, fs,
path::{Component, Path, PathBuf}, path::{Component, Path, PathBuf},
}; };
/// Where to find all files or where to write them /// Where to find all files or where to write them
#[derive(Debug, Clone)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProjectPathsConfig { pub struct ProjectPathsConfig {
/// Project root /// Project root
pub root: PathBuf, pub root: PathBuf,
@ -60,6 +60,25 @@ impl ProjectPathsConfig {
Self::dapptools(std::env::current_dir().map_err(|err| SolcError::io(err, "."))?) Self::dapptools(std::env::current_dir().map_err(|err| SolcError::io(err, "."))?)
} }
/// Returns a new [ProjectPaths] instance that contains all directories configured for this
/// project
pub fn paths(&self) -> ProjectPaths {
ProjectPaths {
artifacts: self.artifacts.clone(),
sources: self.sources.clone(),
tests: self.tests.clone(),
libraries: self.libraries.iter().cloned().collect(),
}
}
/// Same as [Self::paths()] but strips the `root` form all paths,
/// [ProjectPaths::strip_prefix_all()]
pub fn paths_relative(&self) -> ProjectPaths {
let mut paths = self.paths();
paths.strip_prefix_all(&self.root);
paths
}
/// Creates all configured dirs and files /// Creates all configured dirs and files
pub fn create_all(&self) -> std::result::Result<(), SolcIoError> { pub fn create_all(&self) -> std::result::Result<(), SolcIoError> {
if let Some(parent) = self.cache.parent() { if let Some(parent) = self.cache.parent() {
@ -316,6 +335,61 @@ impl fmt::Display for ProjectPathsConfig {
} }
} }
/// This is a subset of [ProjectPathsConfig] that contains all relevant folders in the project
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ProjectPaths {
pub artifacts: PathBuf,
pub sources: PathBuf,
pub tests: PathBuf,
pub libraries: BTreeSet<PathBuf>,
}
impl ProjectPaths {
/// Joins the folders' location with `root`
pub fn join_all(&mut self, root: impl AsRef<Path>) -> &mut Self {
let root = root.as_ref();
self.artifacts = root.join(&self.artifacts);
self.sources = root.join(&self.sources);
self.tests = root.join(&self.tests);
let libraries = std::mem::take(&mut self.libraries);
self.libraries.extend(libraries.into_iter().map(|p| root.join(p)));
self
}
/// Removes `base` from all folders
pub fn strip_prefix_all(&mut self, base: impl AsRef<Path>) -> &mut Self {
let base = base.as_ref();
if let Ok(prefix) = self.artifacts.strip_prefix(base) {
self.artifacts = prefix.to_path_buf();
}
if let Ok(prefix) = self.sources.strip_prefix(base) {
self.sources = prefix.to_path_buf();
}
if let Ok(prefix) = self.tests.strip_prefix(base) {
self.tests = prefix.to_path_buf();
}
let libraries = std::mem::take(&mut self.libraries);
self.libraries.extend(
libraries
.into_iter()
.map(|p| p.strip_prefix(base).map(|p| p.to_path_buf()).unwrap_or(p)),
);
self
}
}
impl Default for ProjectPaths {
fn default() -> Self {
Self {
artifacts: "out".into(),
sources: "src".into(),
tests: "tests".into(),
libraries: Default::default(),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
pub enum PathStyle { pub enum PathStyle {
HardHat, HardHat,