forked from pantsbuild/pants
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(sapling split of 33f6bdd7511db229d81b82da0eb527b36187b742)
- Loading branch information
Benjy
committed
May 30, 2013
1 parent
ceb2bff
commit d3bab89
Showing
8 changed files
with
241 additions
and
263 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,24 @@ | ||
from twitter.common.lang import Compatibility | ||
from twitter.pants.cache.combined_artifact_cache import CombinedArtifactCache | ||
from twitter.pants.cache.file_based_artifact_cache import FileBasedArtifactCache | ||
from twitter.pants.cache.restful_artifact_cache import RESTfulArtifactCache | ||
|
||
|
||
def create_artifact_cache(context, artifact_root, spec): | ||
"""Returns an artifact cache for the specified spec. | ||
If config is a string, it's interpreted as a path or URL prefix to a cache root. If it's a list of | ||
strings, it returns an appropriate combined cache. | ||
""" | ||
if not spec: | ||
raise ValueError('Empty artifact cache spec') | ||
if isinstance(spec, Compatibility.string): | ||
if spec.startswith('/'): | ||
return FileBasedArtifactCache(context, artifact_root, spec) | ||
elif spec.startswith('http://') or spec.startswith('https://'): | ||
return RESTfulArtifactCache(context, artifact_root, spec) | ||
else: | ||
raise ValueError('Invalid artifact cache spec: %s' % spec) | ||
elif isinstance(spec, (list, tuple)): | ||
caches = [ create_artifact_cache(context, artifact_root, x) for x in spec ] | ||
return CombinedArtifactCache(caches) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
from twitter.pants.cache.artifact_cache import ArtifactCache | ||
|
||
|
||
class CombinedArtifactCache(ArtifactCache): | ||
"""An artifact cache that delegates to a list of other caches.""" | ||
def __init__(self, artifact_caches): | ||
if not artifact_caches: | ||
raise ValueError('Must provide at least one underlying artifact cache') | ||
context = artifact_caches[0].context | ||
artifact_root = artifact_caches[0].artifact_root | ||
if any(x.context != context or x.artifact_root != artifact_root for x in artifact_caches): | ||
raise ValueError('Combined artifact caches must all have the same artifact root.') | ||
ArtifactCache.__init__(self, context, artifact_root) | ||
self._artifact_caches = artifact_caches | ||
|
||
def insert(self, cache_key, build_artifacts): | ||
for cache in self._artifact_caches: # Insert into all. | ||
cache.insert(cache_key, build_artifacts) | ||
|
||
def has(self, cache_key): | ||
return any(cache.has(cache_key) for cache in self._artifact_caches) | ||
|
||
def use_cached_files(self, cache_key): | ||
return any(cache.use_cached_files(cache_key) for cache in self._artifact_caches) | ||
|
||
def delete(self, cache_key): | ||
for cache in self._artifact_caches: # Delete from all. | ||
cache.delete(cache_key) |
58 changes: 58 additions & 0 deletions
58
src/python/twitter/pants/cache/file_based_artifact_cache.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import os | ||
import shutil | ||
from twitter.common.dirutil import safe_mkdir, safe_rmtree | ||
from twitter.pants.cache.artifact_cache import ArtifactCache | ||
|
||
|
||
class FileBasedArtifactCache(ArtifactCache): | ||
"""An artifact cache that stores the artifacts in local files.""" | ||
def __init__(self, context, artifact_root, cache_root, copy_fn=None): | ||
""" | ||
cache_root: The locally cached files are stored under this directory. | ||
copy_fn: An optional function with the signature copy_fn(absolute_src_path, relative_dst_path) that | ||
will copy cached files into the desired destination. If unspecified, a simple file copy is used. | ||
""" | ||
ArtifactCache.__init__(self, context, artifact_root) | ||
self._cache_root = cache_root | ||
self._copy_fn = copy_fn or ( | ||
lambda src, rel_dst: shutil.copy(src, os.path.join(self.artifact_root, rel_dst))) | ||
safe_mkdir(self._cache_root) | ||
|
||
def try_insert(self, cache_key, build_artifacts): | ||
cache_dir = self._cache_dir_for_key(cache_key) | ||
safe_rmtree(cache_dir) | ||
for artifact in build_artifacts or (): | ||
rel_path = os.path.relpath(artifact, self.artifact_root) | ||
|
||
if rel_path.startswith('..'): | ||
raise self.CacheError('Artifact %s is not under artifact root %s' % (artifact, | ||
self.artifact_root)) | ||
|
||
artifact_dest = os.path.join(cache_dir, rel_path) | ||
safe_mkdir(os.path.dirname(artifact_dest)) | ||
if os.path.isdir(artifact): | ||
shutil.copytree(artifact, artifact_dest) | ||
else: | ||
shutil.copy(artifact, artifact_dest) | ||
|
||
def has(self, cache_key): | ||
return os.path.isdir(self._cache_dir_for_key(cache_key)) | ||
|
||
def use_cached_files(self, cache_key): | ||
cache_dir = self._cache_dir_for_key(cache_key) | ||
if not os.path.exists(cache_dir): | ||
return False | ||
for dir_name, _, filenames in os.walk(cache_dir): | ||
for filename in filenames: | ||
filename = os.path.join(dir_name, filename) | ||
relative_filename = os.path.relpath(filename, cache_dir) | ||
self._copy_fn(filename, relative_filename) | ||
return True | ||
|
||
def delete(self, cache_key): | ||
safe_rmtree(self._cache_dir_for_key(cache_key)) | ||
|
||
def _cache_dir_for_key(self, cache_key): | ||
# Note: it's important to use the id as well as the hash, because two different targets | ||
# may have the same hash if both have no sources, but we may still want to differentiate them. | ||
return os.path.join(self._cache_root, cache_key.id, cache_key.hash) |
Oops, something went wrong.