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:
parent
842f4d260f
commit
26b6472f9b
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue