Repository: gakonst/ethers-rs
Pull request #630 (2021-11-27 14:15:16)

fix(solc): remove changed artifacts from the cache
## Motivation

When reading from the cache, there may be changed artifacts that need to be recompiled. 
Currently only missing files are removed from the cache metadata struct, but changed files remain.
Since we're now returning the cached artifacts as part of #623 on partial changes and writing new
files to the cache as part of #629, we also need to remove changed files from the cache. 

## Solution

After getting the changed sources, remove them from the cache metadata (`SolFilesCache`) so
they are no longer returned.

## PR Checklist

- [x] Added Tests


Patch:
diff --git a/ethers-solc/src/cache.rs b/ethers-solc/src/cache.rs
index e6815ac8..20ef24bd 100644
--- a/ethers-solc/src/cache.rs
+++ b/ethers-solc/src/cache.rs
@@ -58,6 +58,10 @@ impl SolFilesCache {
         self.files.retain(|file, _| Path::new(file).exists())
     }
 
+    pub fn remove_changed_files(&mut self, changed_files: &Sources) {
+        self.files.retain(|file, _| !changed_files.contains_key(file))
+    }
+
     /// Returns only the files that were changed from the provided sources, to save time
     /// when compiling.
     pub fn get_changed_files<'a>(
diff --git a/ethers-solc/src/lib.rs b/ethers-solc/src/lib.rs
index dd64ac67..e7cd94f1 100644
--- a/ethers-solc/src/lib.rs
+++ b/ethers-solc/src/lib.rs
@@ -251,6 +251,7 @@ impl<Artifacts: ArtifactOutput> Project<Artifacts> {
                 Some(&self.solc_config),
                 &self.paths.artifacts,
             );
+            cache.remove_changed_files(&changed_files);
 
             let cached_artifacts = if self.paths.artifacts.exists() {
                 cache.read_artifacts::<Artifacts>(&self.paths.artifacts)?


Test patch:
diff --git a/ethers-solc/test-data/cache-sample/Dapp.sol b/ethers-solc/test-data/cache-sample/Dapp.sol
new file mode 100644
index 00000000..762db2c3
--- /dev/null
+++ b/ethers-solc/test-data/cache-sample/Dapp.sol
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+pragma solidity >=0.6.6;
+
+contract Dapp {
+
+    function modified() public {}
+}
diff --git a/ethers-solc/tests/project.rs b/ethers-solc/tests/project.rs
index 6caebe3e..3157b66a 100644
--- a/ethers-solc/tests/project.rs
+++ b/ethers-solc/tests/project.rs
@@ -88,7 +88,7 @@ fn can_compile_dapp_sample_with_cache() {
 
     let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
     let orig_root = manifest_dir.join("test-data/dapp-sample");
-    let new_file = manifest_dir.join("test-data/cache-sample/NewContract.sol");
+    let cache_testdata_dir = manifest_dir.join("test-data/cache-sample/");
     copy_dir_all(orig_root, &tmp_dir).unwrap();
     let paths = ProjectPathsConfig::builder()
         .cache(cache)
@@ -117,11 +117,34 @@ fn can_compile_dapp_sample_with_cache() {
     assert!(!compiled.is_unchanged());
 
     // new file is compiled even with partial cache
-    std::fs::copy(new_file, root.join("src/NewContract.sol")).unwrap();
+    std::fs::copy(cache_testdata_dir.join("NewContract.sol"), root.join("src/NewContract.sol"))
+        .unwrap();
     let compiled = project.compile().unwrap();
     assert!(compiled.find("Dapp").is_some());
     assert!(compiled.find("NewContract").is_some());
     assert!(!compiled.is_unchanged());
+    assert_eq!(
+        compiled.into_artifacts().map(|(name, _)| name).collect::<Vec<_>>(),
+        vec![
+            r#""Dapp.json":Dapp"#,
+            r#""DappTest.json":DappTest"#,
+            r#""DSTest.json":DSTest"#,
+            "NewContract"
+        ]
+    );
+
+    // old cached artifact is not taken from the 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::<Vec<_>>(),
+        vec![
+            r#""DappTest.json":DappTest"#,
+            r#""NewContract.json":NewContract"#,
+            r#""DSTest.json":DSTest"#,
+            "Dapp"
+        ]
+    );
 
     // deleted artifact is not taken from the cache
     std::fs::remove_file(&project.paths.sources.join("Dapp.sol")).unwrap();