commit
630ff6871d
@ -37,6 +37,7 @@
|
||||
import spack.config
|
||||
import spack.fetch_strategy as fs
|
||||
import spack.error
|
||||
from spack.version import *
|
||||
|
||||
STAGE_PREFIX = 'spack-stage-'
|
||||
|
||||
@ -51,10 +52,12 @@ class Stage(object):
|
||||
lifecycle looks like this:
|
||||
|
||||
```
|
||||
with Stage() as stage: # Context manager creates and destroys the stage directory
|
||||
with Stage() as stage: # Context manager creates and destroys the
|
||||
# stage directory
|
||||
stage.fetch() # Fetch a source archive into the stage.
|
||||
stage.expand_archive() # Expand the source archive.
|
||||
<install> # Build and install the archive. (handled by user of Stage)
|
||||
<install> # Build and install the archive. (handled by
|
||||
# user of Stage)
|
||||
```
|
||||
|
||||
When used as a context manager, the stage is automatically
|
||||
@ -71,7 +74,8 @@ class Stage(object):
|
||||
stage.create() # Explicitly create the stage directory.
|
||||
stage.fetch() # Fetch a source archive into the stage.
|
||||
stage.expand_archive() # Expand the source archive.
|
||||
<install> # Build and install the archive. (handled by user of Stage)
|
||||
<install> # Build and install the archive. (handled by
|
||||
# user of Stage)
|
||||
finally:
|
||||
stage.destroy() # Explicitly destroy the stage directory.
|
||||
```
|
||||
@ -120,13 +124,17 @@ def __init__(self, url_or_fetch_strategy,
|
||||
elif isinstance(url_or_fetch_strategy, fs.FetchStrategy):
|
||||
self.fetcher = url_or_fetch_strategy
|
||||
else:
|
||||
raise ValueError("Can't construct Stage without url or fetch strategy")
|
||||
raise ValueError(
|
||||
"Can't construct Stage without url or fetch strategy")
|
||||
self.fetcher.set_stage(self)
|
||||
self.default_fetcher = self.fetcher # self.fetcher can change with mirrors.
|
||||
self.skip_checksum_for_mirror = True # used for mirrored archives of repositories.
|
||||
# self.fetcher can change with mirrors.
|
||||
self.default_fetcher = self.fetcher
|
||||
# used for mirrored archives of repositories.
|
||||
self.skip_checksum_for_mirror = True
|
||||
|
||||
# TODO : this uses a protected member of tempfile, but seemed the only way to get a temporary name
|
||||
# TODO : besides, the temporary link name won't be the same as the temporary stage area in tmp_root
|
||||
# TODO : this uses a protected member of tempfile, but seemed the only
|
||||
# TODO : way to get a temporary name besides, the temporary link name
|
||||
# TODO : won't be the same as the temporary stage area in tmp_root
|
||||
self.name = name
|
||||
if name is None:
|
||||
self.name = STAGE_PREFIX + next(tempfile._get_candidate_names())
|
||||
@ -143,7 +151,6 @@ def __init__(self, url_or_fetch_strategy,
|
||||
# Flag to decide whether to delete the stage folder on exit or not
|
||||
self.keep = keep
|
||||
|
||||
|
||||
def __enter__(self):
|
||||
"""
|
||||
Entering a stage context will create the stage directory
|
||||
@ -154,7 +161,6 @@ def __enter__(self):
|
||||
self.create()
|
||||
return self
|
||||
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
"""
|
||||
Exiting from a stage context will delete the stage directory unless:
|
||||
@ -173,12 +179,10 @@ def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if exc_type is None and not self.keep:
|
||||
self.destroy()
|
||||
|
||||
|
||||
def _need_to_create_path(self):
|
||||
"""Makes sure nothing weird has happened since the last time we
|
||||
looked at path. Returns True if path already exists and is ok.
|
||||
Returns False if path needs to be created.
|
||||
"""
|
||||
Returns False if path needs to be created."""
|
||||
# Path doesn't exist yet. Will need to create it.
|
||||
if not os.path.exists(self.path):
|
||||
return True
|
||||
@ -196,7 +200,8 @@ def _need_to_create_path(self):
|
||||
if spack.use_tmp_stage:
|
||||
# If we're using a tmp dir, it's a link, and it points at the
|
||||
# right spot, then keep it.
|
||||
if (real_path.startswith(real_tmp) and os.path.exists(real_path)):
|
||||
if (real_path.startswith(real_tmp) and
|
||||
os.path.exists(real_path)):
|
||||
return False
|
||||
else:
|
||||
# otherwise, just unlink it and start over.
|
||||
@ -204,7 +209,8 @@ def _need_to_create_path(self):
|
||||
return True
|
||||
|
||||
else:
|
||||
# If we're not tmp mode, then it's a link and we want a directory.
|
||||
# If we're not tmp mode, then it's a link and we want a
|
||||
# directory.
|
||||
os.unlink(self.path)
|
||||
return True
|
||||
|
||||
@ -215,10 +221,12 @@ def expected_archive_files(self):
|
||||
"""Possible archive file paths."""
|
||||
paths = []
|
||||
if isinstance(self.fetcher, fs.URLFetchStrategy):
|
||||
paths.append(os.path.join(self.path, os.path.basename(self.fetcher.url)))
|
||||
paths.append(os.path.join(
|
||||
self.path, os.path.basename(self.fetcher.url)))
|
||||
|
||||
if self.mirror_path:
|
||||
paths.append(os.path.join(self.path, os.path.basename(self.mirror_path)))
|
||||
paths.append(os.path.join(
|
||||
self.path, os.path.basename(self.mirror_path)))
|
||||
|
||||
return paths
|
||||
|
||||
@ -227,10 +235,12 @@ def archive_file(self):
|
||||
"""Path to the source archive within this stage directory."""
|
||||
paths = []
|
||||
if isinstance(self.fetcher, fs.URLFetchStrategy):
|
||||
paths.append(os.path.join(self.path, os.path.basename(self.fetcher.url)))
|
||||
paths.append(os.path.join(
|
||||
self.path, os.path.basename(self.fetcher.url)))
|
||||
|
||||
if self.mirror_path:
|
||||
paths.append(os.path.join(self.path, os.path.basename(self.mirror_path)))
|
||||
paths.append(os.path.join(
|
||||
self.path, os.path.basename(self.mirror_path)))
|
||||
|
||||
for path in paths:
|
||||
if os.path.exists(path):
|
||||
@ -262,7 +272,8 @@ def source_path(self):
|
||||
return None
|
||||
|
||||
def chdir(self):
|
||||
"""Changes directory to the stage path. Or dies if it is not set up."""
|
||||
"""Changes directory to the stage path. Or dies if it is not set
|
||||
up."""
|
||||
if os.path.isdir(self.path):
|
||||
os.chdir(self.path)
|
||||
else:
|
||||
@ -306,6 +317,19 @@ def fetch(self, mirror_only=False):
|
||||
fetchers.insert(0, fs.URLFetchStrategy(url, digest))
|
||||
fetchers.insert(0, spack.cache.fetcher(self.mirror_path, digest))
|
||||
|
||||
# Look for the archive in list_url
|
||||
archive_version = spack.url.parse_version(self.default_fetcher.url)
|
||||
package_name = os.path.dirname(self.mirror_path)
|
||||
pkg = spack.repo.get(package_name)
|
||||
if pkg.list_url is not None and pkg.url is not None:
|
||||
versions = pkg.fetch_remote_versions()
|
||||
try:
|
||||
url_from_list = versions[Version(archive_version)]
|
||||
fetchers.append(fs.URLFetchStrategy(url_from_list, digest))
|
||||
except KeyError:
|
||||
tty.msg("Can not find version %s in url_list" %
|
||||
archive_version)
|
||||
|
||||
for fetcher in fetchers:
|
||||
try:
|
||||
fetcher.set_stage(self)
|
||||
@ -321,11 +345,11 @@ def fetch(self, mirror_only=False):
|
||||
self.fetcher = self.default_fetcher
|
||||
raise fs.FetchError(errMessage, None)
|
||||
|
||||
|
||||
def check(self):
|
||||
"""Check the downloaded archive against a checksum digest.
|
||||
No-op if this stage checks code out of a repository."""
|
||||
if self.fetcher is not self.default_fetcher and self.skip_checksum_for_mirror:
|
||||
if self.fetcher is not self.default_fetcher and \
|
||||
self.skip_checksum_for_mirror:
|
||||
tty.warn("Fetching from mirror without a checksum!",
|
||||
"This package is normally checked out from a version "
|
||||
"control system, but it has been archived on a spack "
|
||||
@ -335,16 +359,13 @@ def check(self):
|
||||
else:
|
||||
self.fetcher.check()
|
||||
|
||||
|
||||
def cache_local(self):
|
||||
spack.cache.store(self.fetcher, self.mirror_path)
|
||||
|
||||
|
||||
def expand_archive(self):
|
||||
"""Changes to the stage directory and attempt to expand the downloaded
|
||||
archive. Fail if the stage is not set up or if the archive is not yet
|
||||
downloaded.
|
||||
"""
|
||||
archive. Fail if the stage is not set up or if the archive is not yet
|
||||
downloaded."""
|
||||
archive_dir = self.source_path
|
||||
if not archive_dir:
|
||||
self.fetcher.expand()
|
||||
@ -386,8 +407,8 @@ def create(self):
|
||||
# Create the top-level stage directory
|
||||
mkdirp(spack.stage_path)
|
||||
remove_dead_links(spack.stage_path)
|
||||
# If a tmp_root exists then create a directory there and then link it in the stage area,
|
||||
# otherwise create the stage directory in self.path
|
||||
# If a tmp_root exists then create a directory there and then link it
|
||||
# in the stage area, otherwise create the stage directory in self.path
|
||||
if self._need_to_create_path():
|
||||
if self.tmp_root:
|
||||
tmp_dir = tempfile.mkdtemp('', STAGE_PREFIX, self.tmp_root)
|
||||
@ -409,6 +430,7 @@ def destroy(self):
|
||||
|
||||
|
||||
class ResourceStage(Stage):
|
||||
|
||||
def __init__(self, url_or_fetch_strategy, root, resource, **kwargs):
|
||||
super(ResourceStage, self).__init__(url_or_fetch_strategy, **kwargs)
|
||||
self.root_stage = root
|
||||
@ -418,12 +440,15 @@ def expand_archive(self):
|
||||
super(ResourceStage, self).expand_archive()
|
||||
root_stage = self.root_stage
|
||||
resource = self.resource
|
||||
placement = os.path.basename(self.source_path) if resource.placement is None else resource.placement
|
||||
placement = os.path.basename(self.source_path) \
|
||||
if resource.placement is None \
|
||||
else resource.placement
|
||||
if not isinstance(placement, dict):
|
||||
placement = {'': placement}
|
||||
# Make the paths in the dictionary absolute and link
|
||||
for key, value in placement.iteritems():
|
||||
target_path = join_path(root_stage.source_path, resource.destination)
|
||||
target_path = join_path(
|
||||
root_stage.source_path, resource.destination)
|
||||
destination_path = join_path(target_path, value)
|
||||
source_path = join_path(self.source_path, key)
|
||||
|
||||
@ -437,21 +462,23 @@ def expand_archive(self):
|
||||
|
||||
if not os.path.exists(destination_path):
|
||||
# Create a symlink
|
||||
tty.info('Moving resource stage\n\tsource : {stage}\n\tdestination : {destination}'.format(
|
||||
stage=source_path, destination=destination_path
|
||||
))
|
||||
tty.info('Moving resource stage\n\tsource : '
|
||||
'{stage}\n\tdestination : {destination}'.format(
|
||||
stage=source_path, destination=destination_path
|
||||
))
|
||||
shutil.move(source_path, destination_path)
|
||||
|
||||
|
||||
@pattern.composite(method_list=['fetch', 'create', 'check', 'expand_archive', 'restage', 'destroy', 'cache_local'])
|
||||
@pattern.composite(method_list=['fetch', 'create', 'check', 'expand_archive',
|
||||
'restage', 'destroy', 'cache_local'])
|
||||
class StageComposite:
|
||||
"""
|
||||
Composite for Stage type objects. The first item in this composite is considered to be the root package, and
|
||||
operations that return a value are forwarded to it.
|
||||
"""
|
||||
"""Composite for Stage type objects. The first item in this composite is
|
||||
considered to be the root package, and operations that return a value are
|
||||
forwarded to it."""
|
||||
#
|
||||
# __enter__ and __exit__ delegate to all stages in the composite.
|
||||
#
|
||||
|
||||
def __enter__(self):
|
||||
for item in self:
|
||||
item.__enter__()
|
||||
@ -496,8 +523,11 @@ def chdir(self):
|
||||
raise ChdirError("Setup failed: no such directory: " + self.path)
|
||||
|
||||
# DIY stages do nothing as context managers.
|
||||
def __enter__(self): pass
|
||||
def __exit__(self, exc_type, exc_val, exc_tb): pass
|
||||
def __enter__(self):
|
||||
pass
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
pass
|
||||
|
||||
def chdir_to_source(self):
|
||||
self.chdir()
|
||||
|
Loading…
Reference in New Issue
Block a user