Move temp directory configuration to config.yaml

- Moved temp finding logic to spack.stage
- Updated stage tests
- Added tests for new path substaitution of $user, $spack, $tempdir
This commit is contained in:
Todd Gamblin 2016-10-27 22:36:59 -07:00
parent 9347f86939
commit 0da639298c
9 changed files with 330 additions and 176 deletions

View File

@ -31,13 +31,13 @@ config:
# You can use $tempdir to refer to the system default temp directory # You can use $tempdir to refer to the system default temp directory
# (as returned by tempfile.gettempdir()). # (as returned by tempfile.gettempdir()).
# #
# A value of $local indicates that Spack should run builds directly # A value of $spack/var/spack/stage indicates that Spack should run
# inside its install directory without staging them in temporary space. # builds directly inside its install directory without staging them in
# temporary space.
build_stage: build_stage:
- /usr/workspace/*/%u
- $tempdir - $tempdir
- /nfs/tmp2/%u - /nfs/tmp2/$user
- $local - $spack/var/spack/stage
# Cache directory already downloaded source tarballs and archived # Cache directory already downloaded source tarballs and archived

View File

@ -25,7 +25,6 @@
import collections import collections
import errno import errno
import fileinput import fileinput
import getpass
import glob import glob
import numbers import numbers
import os import os
@ -46,7 +45,6 @@
'can_access', 'can_access',
'change_sed_delimiter', 'change_sed_delimiter',
'copy_mode', 'copy_mode',
'expand_user',
'filter_file', 'filter_file',
'find_libraries', 'find_libraries',
'fix_darwin_install_name', 'fix_darwin_install_name',
@ -229,16 +227,6 @@ def is_exe(path):
return os.path.isfile(path) and os.access(path, os.X_OK) return os.path.isfile(path) and os.access(path, os.X_OK)
def expand_user(path):
"""Find instances of '%u' in a path and replace with the current user's
username."""
username = getpass.getuser()
if not username and '%u' in path:
tty.die("Couldn't get username to complete path '%s'" % path)
return path.replace('%u', username)
def mkdirp(*paths): def mkdirp(*paths):
"""Creates a directory, as well as parent directories if needed.""" """Creates a directory, as well as parent directories if needed."""
for path in paths: for path in paths:

View File

@ -119,27 +119,6 @@
# certifiates. e.g., curl should use the -k option. # certifiates. e.g., curl should use the -k option.
insecure = False insecure = False
# Whether to build in tmp space or directly in the stage_path.
# If this is true, then spack will make stage directories in
# a tmp filesystem, and it will symlink them into stage_path.
use_tmp_stage = True
# Locations to use for staging and building, in order of preference
# Use a %u to add a username to the stage paths here, in case this
# is a shared filesystem. Spack will use the first of these paths
# that it can create.
tmp_dirs = []
_default_tmp = tempfile.gettempdir()
_tmp_user = getpass.getuser()
_tmp_candidates = (_default_tmp, '/nfs/tmp2', '/tmp', '/var/tmp')
for path in _tmp_candidates:
# don't add a second username if it's already unique by user.
if _tmp_user not in path:
tmp_dirs.append(join_path(path, '%u', 'spack-stage'))
else:
tmp_dirs.append(join_path(path, 'spack-stage'))
# Whether spack should allow installation of unsafe versions of # Whether spack should allow installation of unsafe versions of
# software. "Unsafe" versions are ones it doesn't have a checksum # software. "Unsafe" versions are ones it doesn't have a checksum
# for. # for.

View File

@ -44,6 +44,7 @@
import spack.error import spack.error
import spack.spec import spack.spec
from spack.provider_index import ProviderIndex from spack.provider_index import ProviderIndex
from spack.util.path import canonicalize_path
from spack.util.naming import * from spack.util.naming import *
# #
@ -93,19 +94,6 @@ def __getattr__(self, name):
return getattr(self, name) return getattr(self, name)
def substitute_spack_prefix(path):
"""Replaces instances of $spack with Spack's prefix."""
return re.sub(r'^\$spack', spack.prefix, path)
def canonicalize_path(path):
"""Substitute $spack, expand user home, take abspath."""
path = substitute_spack_prefix(path)
path = os.path.expanduser(path)
path = os.path.abspath(path)
return path
class RepoPath(object): class RepoPath(object):
"""A RepoPath is a list of repos that function as one. """A RepoPath is a list of repos that function as one.

View File

@ -28,6 +28,7 @@
import hashlib import hashlib
import shutil import shutil
import tempfile import tempfile
import getpass
from urlparse import urljoin from urlparse import urljoin
import llnl.util.tty as tty import llnl.util.tty as tty
@ -41,9 +42,72 @@
import spack.fetch_strategy as fs import spack.fetch_strategy as fs
import spack.error import spack.error
from spack.version import * from spack.version import *
from spack.util.path import canonicalize_path
from spack.util.crypto import prefix_bits, bit_length from spack.util.crypto import prefix_bits, bit_length
STAGE_PREFIX = 'spack-stage-' _stage_prefix = 'spack-stage-'
def _first_accessible_path(paths):
"""Find a tmp dir that exists that we can access."""
for path in paths:
try:
# try to create the path if it doesn't exist.
path = canonicalize_path(path)
mkdirp(path)
# ensure accessible
if not can_access(path):
continue
# return it if successful.
return path
except OSError:
tty.debug('OSError while checking temporary path: %s' % path)
continue
return None
# cached temporary root
_tmp_root = None
_use_tmp_stage = True
def get_tmp_root():
global _tmp_root, _use_tmp_stage
if not _use_tmp_stage:
return None
if _tmp_root is None:
config = spack.config.get_config('config')
candidates = config['build_stage']
if isinstance(candidates, basestring):
candidates = [candidates]
path = _first_accessible_path(candidates)
if not path:
raise StageError("No accessible stage paths in %s", candidates)
# Return None to indicate we're using a local staging area.
if path == canonicalize_path(spack.stage_path):
_use_tmp_stage = False
return None
# ensure that any temp path is unique per user, so users don't
# fight over shared temporary space.
user = getpass.getuser()
if user not in path:
path = os.path.join(path, user, 'spack-stage')
else:
path = os.path.join(path, 'spack-stage')
mkdirp(path)
_tmp_root = path
return _tmp_root
class Stage(object): class Stage(object):
@ -141,9 +205,8 @@ def __init__(
# TODO : won't be the same as the temporary stage area in tmp_root # TODO : won't be the same as the temporary stage area in tmp_root
self.name = name self.name = name
if name is None: if name is None:
self.name = STAGE_PREFIX + next(tempfile._get_candidate_names()) self.name = _stage_prefix + next(tempfile._get_candidate_names())
self.mirror_path = mirror_path self.mirror_path = mirror_path
self.tmp_root = find_tmp_root()
# Try to construct here a temporary name for the stage directory # Try to construct here a temporary name for the stage directory
# If this is a named stage, then construct a named path. # If this is a named stage, then construct a named path.
@ -217,10 +280,11 @@ def _need_to_create_path(self):
# Path looks ok, but need to check the target of the link. # Path looks ok, but need to check the target of the link.
if os.path.islink(self.path): if os.path.islink(self.path):
real_path = os.path.realpath(self.path) tmp_root = get_tmp_root()
real_tmp = os.path.realpath(self.tmp_root) if tmp_root is not None:
real_path = os.path.realpath(self.path)
real_tmp = os.path.realpath(tmp_root)
if spack.use_tmp_stage:
# If we're using a tmp dir, it's a link, and it points at the # If we're using a tmp dir, it's a link, and it points at the
# right spot, then keep it. # right spot, then keep it.
if (real_path.startswith(real_tmp) and if (real_path.startswith(real_tmp) and
@ -416,11 +480,11 @@ def chdir_to_source(self):
""" """
path = self.source_path path = self.source_path
if not path: if not path:
tty.die("Attempt to chdir before expanding archive.") raise StageError("Attempt to chdir before expanding archive.")
else: else:
os.chdir(path) os.chdir(path)
if not os.listdir(path): if not os.listdir(path):
tty.die("Archive was empty for %s" % self.name) raise StageError("Archive was empty for %s" % self.name)
def restage(self): def restage(self):
"""Removes the expanded archive path if it exists, then re-expands """Removes the expanded archive path if it exists, then re-expands
@ -429,17 +493,17 @@ def restage(self):
self.fetcher.reset() self.fetcher.reset()
def create(self): def create(self):
""" """Creates the stage directory.
Creates the stage directory
If self.tmp_root evaluates to False, the stage directory is If get_tmp_root() is None, the stage directory is created
created directly under spack.stage_path, otherwise this will directly under spack.stage_path, otherwise this will attempt to
attempt to create a stage in a temporary directory and link it create a stage in a temporary directory and link it into
into spack.stage_path. spack.stage_path.
Spack will use the first writable location in spack.tmp_dirs Spack will use the first writable location in spack.tmp_dirs
to create a stage. If there is no valid location in tmp_dirs, to create a stage. If there is no valid location in tmp_dirs,
fall back to making the stage inside spack.stage_path. fall back to making the stage inside spack.stage_path.
""" """
# Create the top-level stage directory # Create the top-level stage directory
mkdirp(spack.stage_path) mkdirp(spack.stage_path)
@ -448,8 +512,10 @@ def create(self):
# If a tmp_root exists then create a directory there and then link it # 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 # in the stage area, otherwise create the stage directory in self.path
if self._need_to_create_path(): if self._need_to_create_path():
if self.tmp_root: tmp_root = get_tmp_root()
tmp_dir = tempfile.mkdtemp('', STAGE_PREFIX, self.tmp_root) if tmp_root is not None:
tmp_dir = tempfile.mkdtemp('', _stage_prefix, tmp_root)
tty.debug('link %s -> %s' % (self.path, tmp_dir))
os.symlink(tmp_dir, self.path) os.symlink(tmp_dir, self.path)
else: else:
mkdirp(self.path) mkdirp(self.path)
@ -614,25 +680,6 @@ def purge():
remove_linked_tree(stage_path) remove_linked_tree(stage_path)
def find_tmp_root():
if spack.use_tmp_stage:
for tmp in spack.tmp_dirs:
try:
# Replace %u with username
expanded = expand_user(tmp)
# try to create a directory for spack stuff
mkdirp(expanded)
# return it if successful.
return expanded
except OSError:
continue
return None
class StageError(spack.error.SpackError): class StageError(spack.error.SpackError):
""""Superclass for all errors encountered during staging.""" """"Superclass for all errors encountered during staging."""

View File

@ -24,10 +24,12 @@
############################################################################## ##############################################################################
import os import os
import shutil import shutil
import getpass
from tempfile import mkdtemp from tempfile import mkdtemp
import spack import spack
import spack.config import spack.config
from spack.util.path import canonicalize_path
from ordereddict_backport import OrderedDict from ordereddict_backport import OrderedDict
from spack.test.mock_packages_test import * from spack.test.mock_packages_test import *
@ -217,3 +219,53 @@ def test_write_to_same_priority_file(self):
# Same check again, to ensure consistency. # Same check again, to ensure consistency.
self.check_config(a_comps, *self.a_comp_specs) self.check_config(a_comps, *self.a_comp_specs)
self.check_config(b_comps, *self.b_comp_specs) self.check_config(b_comps, *self.b_comp_specs)
def check_canonical(self, var, expected):
"""ensure things are substituted properly and canonicalized."""
path = '/foo/bar/baz'
self.assertEqual(canonicalize_path(var + path),
expected + path)
self.assertEqual(canonicalize_path(path + var),
path + '/' + expected)
self.assertEqual(canonicalize_path(path + var + path),
expected + path)
def test_substitute_config_variables(self):
prefix = spack.prefix.lstrip('/')
self.assertEqual(os.path.join('/foo/bar/baz', prefix),
canonicalize_path('/foo/bar/baz/$spack'))
self.assertEqual(os.path.join(spack.prefix, 'foo/bar/baz'),
canonicalize_path('$spack/foo/bar/baz/'))
self.assertEqual(os.path.join('/foo/bar/baz', prefix, 'foo/bar/baz'),
canonicalize_path('/foo/bar/baz/$spack/foo/bar/baz/'))
self.assertEqual(os.path.join('/foo/bar/baz', prefix),
canonicalize_path('/foo/bar/baz/${spack}'))
self.assertEqual(os.path.join(spack.prefix, 'foo/bar/baz'),
canonicalize_path('${spack}/foo/bar/baz/'))
self.assertEqual(
os.path.join('/foo/bar/baz', prefix, 'foo/bar/baz'),
canonicalize_path('/foo/bar/baz/${spack}/foo/bar/baz/'))
self.assertNotEqual(
os.path.join('/foo/bar/baz', prefix, 'foo/bar/baz'),
canonicalize_path('/foo/bar/baz/${spack/foo/bar/baz/'))
def test_substitute_user(self):
user = getpass.getuser()
self.assertEqual('/foo/bar/' + user + '/baz',
canonicalize_path('/foo/bar/$user/baz'))
def test_substitute_tempdir(self):
tempdir = tempfile.gettempdir()
self.assertEqual(tempdir, canonicalize_path('$tempdir'))
self.assertEqual(tempdir + '/foo/bar/baz',
canonicalize_path('$tempdir/foo/bar/baz'))

View File

@ -180,6 +180,27 @@
externalmodule@1.0%gcc@4.5.0: external-module externalmodule@1.0%gcc@4.5.0: external-module
""" """
mock_config = """\
config:
install_tree: $spack/opt/spack
build_stage:
- $tempdir
- /nfs/tmp2/$user
- $spack/var/spack/stage
source_cache: $spack/var/spack/cache
misc_cache: ~/.spack/cache
verify_ssl: true
checksum: true
dirty: false
"""
# these are written out to mock config files.
mock_configs = {
'config.yaml': mock_config,
'compilers.yaml': mock_compiler_config,
'packages.yaml': mock_packages_config,
}
class MockPackagesTest(unittest.TestCase): class MockPackagesTest(unittest.TestCase):
@ -199,11 +220,10 @@ def initmock(self):
self.mock_user_config = os.path.join(self.temp_config, 'user') self.mock_user_config = os.path.join(self.temp_config, 'user')
mkdirp(self.mock_site_config) mkdirp(self.mock_site_config)
mkdirp(self.mock_user_config) mkdirp(self.mock_user_config)
for confs in [('compilers.yaml', mock_compiler_config), for filename, data in mock_configs.items():
('packages.yaml', mock_packages_config)]: conf_yaml = os.path.join(self.mock_site_config, filename)
conf_yaml = os.path.join(self.mock_site_config, confs[0])
with open(conf_yaml, 'w') as f: with open(conf_yaml, 'w') as f:
f.write(confs[1]) f.write(data)
# TODO: Mocking this up is kind of brittle b/c ConfigScope # TODO: Mocking this up is kind of brittle b/c ConfigScope
# TODO: constructor modifies config_scopes. Make it cleaner. # TODO: constructor modifies config_scopes. Make it cleaner.

View File

@ -27,64 +27,71 @@
""" """
import os import os
import shutil import shutil
import unittest import tempfile
from contextlib import * from contextlib import *
import spack import spack
import spack.stage
from llnl.util.filesystem import * from llnl.util.filesystem import *
from spack.stage import Stage from spack.stage import Stage
from spack.util.executable import which from spack.util.executable import which
from spack.test.mock_packages_test import *
test_files_dir = os.path.realpath(join_path(spack.stage_path, '.test')) _test_tmp_path = None
test_tmp_path = os.path.realpath(join_path(test_files_dir, 'tmp'))
archive_dir = 'test-files'
archive_name = archive_dir + '.tar.gz'
archive_dir_path = join_path(test_files_dir, archive_dir)
archive_url = 'file://' + join_path(test_files_dir, archive_name)
readme_name = 'README.txt'
test_readme = join_path(archive_dir_path, readme_name)
readme_text = "hello world!\n"
stage_name = 'spack-test-stage'
@contextmanager @contextmanager
def use_tmp(use_tmp): def use_tmp(use_tmp):
"""Allow some test code to be executed with spack.use_tmp_stage """Allow some test code to be executed such that spack will either use or
set to a certain value. Context manager makes sure it's reset not use temporary space for stages.
on failure.
""" """
old_tmp = spack.use_tmp_stage # mock up config
spack.use_tmp_stage = use_tmp path = _test_tmp_path if use_tmp else spack.stage_path
spack.config.update_config(
'config', {'build_stage': [path]}, scope='user')
yield yield
spack.use_tmp_stage = old_tmp
class StageTest(unittest.TestCase): class StageTest(MockPackagesTest):
def setUp(self): def setUp(self):
"""This sets up a mock archive to fetch, and a mock temp space for use """This sets up a mock archive to fetch, and a mock temp space for use
by the Stage class. It doesn't actually create the Stage -- that by the Stage class. It doesn't actually create the Stage -- that
is done by individual tests. is done by individual tests.
""" """
if os.path.exists(test_files_dir): global _test_tmp_path
shutil.rmtree(test_files_dir)
self.test_files_dir = tempfile.mkdtemp()
self.test_tmp_path = os.path.realpath(
os.path.join(self.test_files_dir, 'tmp'))
_test_tmp_path = self.test_tmp_path
self.archive_dir = 'test-files'
self.archive_name = self.archive_dir + '.tar.gz'
archive_dir_path = os.path.join(self.test_files_dir,
self.archive_dir)
self.archive_url = 'file://' + os.path.join(self.test_files_dir,
self.archive_name)
test_readme = join_path(archive_dir_path, 'README.txt')
self.readme_text = "hello world!\n"
self.stage_name = 'spack-test-stage'
mkdirp(test_files_dir)
mkdirp(archive_dir_path) mkdirp(archive_dir_path)
mkdirp(test_tmp_path) mkdirp(self.test_tmp_path)
with open(test_readme, 'w') as readme: with open(test_readme, 'w') as readme:
readme.write(readme_text) readme.write(self.readme_text)
with working_dir(test_files_dir): with working_dir(self.test_files_dir):
tar = which('tar') tar = which('tar', required=True)
tar('czf', archive_name, archive_dir) tar('czf', self.archive_name, self.archive_dir)
# Make spack use the test environment for tmp stuff. # Make spack use the test environment for tmp stuff.
self.old_tmp_dirs = spack.tmp_dirs self._old_tmp_root = spack.stage._tmp_root
spack.tmp_dirs = [test_tmp_path] self._old_use_tmp_stage = spack.stage._use_tmp_stage
spack.stage._tmp_root = None
spack.stage._use_tmp_stage = True
# record this since this test changes to directories that will # record this since this test changes to directories that will
# be removed. # be removed.
@ -92,13 +99,14 @@ def setUp(self):
def tearDown(self): def tearDown(self):
"""Blows away the test environment directory.""" """Blows away the test environment directory."""
shutil.rmtree(test_files_dir) shutil.rmtree(self.test_files_dir, ignore_errors=True)
# chdir back to original working dir # chdir back to original working dir
os.chdir(self.working_dir) os.chdir(self.working_dir)
# restore spack's original tmp environment # restore spack's original tmp environment
spack.tmp_dirs = self.old_tmp_dirs spack.stage._tmp_root = self._old_tmp_root
spack.stage._use_tmp_stage = self._old_use_tmp_stage
def get_stage_path(self, stage, stage_name): def get_stage_path(self, stage, stage_name):
"""Figure out where a stage should be living. This depends on """Figure out where a stage should be living. This depends on
@ -120,7 +128,7 @@ def check_setup(self, stage, stage_name):
# Ensure stage was created in the spack stage directory # Ensure stage was created in the spack stage directory
self.assertTrue(os.path.isdir(stage_path)) self.assertTrue(os.path.isdir(stage_path))
if spack.use_tmp_stage: if spack.stage.get_tmp_root():
# Check that the stage dir is really a symlink. # Check that the stage dir is really a symlink.
self.assertTrue(os.path.islink(stage_path)) self.assertTrue(os.path.islink(stage_path))
@ -131,7 +139,7 @@ def check_setup(self, stage, stage_name):
# Make sure the directory is in the place we asked it to # Make sure the directory is in the place we asked it to
# be (see setUp and tearDown) # be (see setUp and tearDown)
self.assertTrue(target.startswith(test_tmp_path)) self.assertTrue(target.startswith(self.test_tmp_path))
else: else:
# Make sure the stage path is NOT a link for a non-tmp stage # Make sure the stage path is NOT a link for a non-tmp stage
@ -139,24 +147,24 @@ def check_setup(self, stage, stage_name):
def check_fetch(self, stage, stage_name): def check_fetch(self, stage, stage_name):
stage_path = self.get_stage_path(stage, stage_name) stage_path = self.get_stage_path(stage, stage_name)
self.assertTrue(archive_name in os.listdir(stage_path)) self.assertTrue(self.archive_name in os.listdir(stage_path))
self.assertEqual(join_path(stage_path, archive_name), self.assertEqual(join_path(stage_path, self.archive_name),
stage.fetcher.archive_file) stage.fetcher.archive_file)
def check_expand_archive(self, stage, stage_name): def check_expand_archive(self, stage, stage_name):
stage_path = self.get_stage_path(stage, stage_name) stage_path = self.get_stage_path(stage, stage_name)
self.assertTrue(archive_name in os.listdir(stage_path)) self.assertTrue(self.archive_name in os.listdir(stage_path))
self.assertTrue(archive_dir in os.listdir(stage_path)) self.assertTrue(self.archive_dir in os.listdir(stage_path))
self.assertEqual( self.assertEqual(
join_path(stage_path, archive_dir), join_path(stage_path, self.archive_dir),
stage.source_path) stage.source_path)
readme = join_path(stage_path, archive_dir, readme_name) readme = join_path(stage_path, self.archive_dir, 'README.txt')
self.assertTrue(os.path.isfile(readme)) self.assertTrue(os.path.isfile(readme))
with open(readme) as file: with open(readme) as file:
self.assertEqual(readme_text, file.read()) self.assertEqual(self.readme_text, file.read())
def check_chdir(self, stage, stage_name): def check_chdir(self, stage, stage_name):
stage_path = self.get_stage_path(stage, stage_name) stage_path = self.get_stage_path(stage, stage_name)
@ -165,7 +173,7 @@ def check_chdir(self, stage, stage_name):
def check_chdir_to_source(self, stage, stage_name): def check_chdir_to_source(self, stage, stage_name):
stage_path = self.get_stage_path(stage, stage_name) stage_path = self.get_stage_path(stage, stage_name)
self.assertEqual( self.assertEqual(
join_path(os.path.realpath(stage_path), archive_dir), join_path(os.path.realpath(stage_path), self.archive_dir),
os.getcwd()) os.getcwd())
def check_destroy(self, stage, stage_name): def check_destroy(self, stage, stage_name):
@ -176,76 +184,76 @@ def check_destroy(self, stage, stage_name):
self.assertFalse(os.path.exists(stage_path)) self.assertFalse(os.path.exists(stage_path))
# tmp stage needs to remove tmp dir too. # tmp stage needs to remove tmp dir too.
if spack.use_tmp_stage: if spack.stage._use_tmp_stage:
target = os.path.realpath(stage_path) target = os.path.realpath(stage_path)
self.assertFalse(os.path.exists(target)) self.assertFalse(os.path.exists(target))
def test_setup_and_destroy_name_with_tmp(self): def test_setup_and_destroy_name_with_tmp(self):
with use_tmp(True): with use_tmp(True):
with Stage(archive_url, name=stage_name) as stage: with Stage(self.archive_url, name=self.stage_name) as stage:
self.check_setup(stage, stage_name) self.check_setup(stage, self.stage_name)
self.check_destroy(stage, stage_name) self.check_destroy(stage, self.stage_name)
def test_setup_and_destroy_name_without_tmp(self): def test_setup_and_destroy_name_without_tmp(self):
with use_tmp(False): with use_tmp(False):
with Stage(archive_url, name=stage_name) as stage: with Stage(self.archive_url, name=self.stage_name) as stage:
self.check_setup(stage, stage_name) self.check_setup(stage, self.stage_name)
self.check_destroy(stage, stage_name) self.check_destroy(stage, self.stage_name)
def test_setup_and_destroy_no_name_with_tmp(self): def test_setup_and_destroy_no_name_with_tmp(self):
with use_tmp(True): with use_tmp(True):
with Stage(archive_url) as stage: with Stage(self.archive_url) as stage:
self.check_setup(stage, None) self.check_setup(stage, None)
self.check_destroy(stage, None) self.check_destroy(stage, None)
def test_setup_and_destroy_no_name_without_tmp(self): def test_setup_and_destroy_no_name_without_tmp(self):
with use_tmp(False): with use_tmp(False):
with Stage(archive_url) as stage: with Stage(self.archive_url) as stage:
self.check_setup(stage, None) self.check_setup(stage, None)
self.check_destroy(stage, None) self.check_destroy(stage, None)
def test_chdir(self): def test_chdir(self):
with Stage(archive_url, name=stage_name) as stage: with Stage(self.archive_url, name=self.stage_name) as stage:
stage.chdir() stage.chdir()
self.check_setup(stage, stage_name) self.check_setup(stage, self.stage_name)
self.check_chdir(stage, stage_name) self.check_chdir(stage, self.stage_name)
self.check_destroy(stage, stage_name) self.check_destroy(stage, self.stage_name)
def test_fetch(self): def test_fetch(self):
with Stage(archive_url, name=stage_name) as stage: with Stage(self.archive_url, name=self.stage_name) as stage:
stage.fetch() stage.fetch()
self.check_setup(stage, stage_name) self.check_setup(stage, self.stage_name)
self.check_chdir(stage, stage_name) self.check_chdir(stage, self.stage_name)
self.check_fetch(stage, stage_name) self.check_fetch(stage, self.stage_name)
self.check_destroy(stage, stage_name) self.check_destroy(stage, self.stage_name)
def test_expand_archive(self): def test_expand_archive(self):
with Stage(archive_url, name=stage_name) as stage: with Stage(self.archive_url, name=self.stage_name) as stage:
stage.fetch() stage.fetch()
self.check_setup(stage, stage_name) self.check_setup(stage, self.stage_name)
self.check_fetch(stage, stage_name) self.check_fetch(stage, self.stage_name)
stage.expand_archive() stage.expand_archive()
self.check_expand_archive(stage, stage_name) self.check_expand_archive(stage, self.stage_name)
self.check_destroy(stage, stage_name) self.check_destroy(stage, self.stage_name)
def test_expand_archive_with_chdir(self): def test_expand_archive_with_chdir(self):
with Stage(archive_url, name=stage_name) as stage: with Stage(self.archive_url, name=self.stage_name) as stage:
stage.fetch() stage.fetch()
self.check_setup(stage, stage_name) self.check_setup(stage, self.stage_name)
self.check_fetch(stage, stage_name) self.check_fetch(stage, self.stage_name)
stage.expand_archive() stage.expand_archive()
stage.chdir_to_source() stage.chdir_to_source()
self.check_expand_archive(stage, stage_name) self.check_expand_archive(stage, self.stage_name)
self.check_chdir_to_source(stage, stage_name) self.check_chdir_to_source(stage, self.stage_name)
self.check_destroy(stage, stage_name) self.check_destroy(stage, self.stage_name)
def test_restage(self): def test_restage(self):
with Stage(archive_url, name=stage_name) as stage: with Stage(self.archive_url, name=self.stage_name) as stage:
stage.fetch() stage.fetch()
stage.expand_archive() stage.expand_archive()
stage.chdir_to_source() stage.chdir_to_source()
self.check_expand_archive(stage, stage_name) self.check_expand_archive(stage, self.stage_name)
self.check_chdir_to_source(stage, stage_name) self.check_chdir_to_source(stage, self.stage_name)
# Try to make a file in the old archive dir # Try to make a file in the old archive dir
with open('foobar', 'w') as file: with open('foobar', 'w') as file:
@ -255,40 +263,44 @@ def test_restage(self):
# Make sure the file is not there after restage. # Make sure the file is not there after restage.
stage.restage() stage.restage()
self.check_chdir(stage, stage_name) self.check_chdir(stage, self.stage_name)
self.check_fetch(stage, stage_name) self.check_fetch(stage, self.stage_name)
stage.chdir_to_source() stage.chdir_to_source()
self.check_chdir_to_source(stage, stage_name) self.check_chdir_to_source(stage, self.stage_name)
self.assertFalse('foobar' in os.listdir(stage.source_path)) self.assertFalse('foobar' in os.listdir(stage.source_path))
self.check_destroy(stage, stage_name) self.check_destroy(stage, self.stage_name)
def test_no_keep_without_exceptions(self): def test_no_keep_without_exceptions(self):
with Stage(archive_url, name=stage_name, keep=False) as stage: with Stage(self.archive_url,
name=self.stage_name, keep=False) as stage:
pass pass
self.check_destroy(stage, stage_name) self.check_destroy(stage, self.stage_name)
def test_keep_without_exceptions(self): def test_keep_without_exceptions(self):
with Stage(archive_url, name=stage_name, keep=True) as stage: with Stage(self.archive_url,
name=self.stage_name, keep=True) as stage:
pass pass
path = self.get_stage_path(stage, stage_name) path = self.get_stage_path(stage, self.stage_name)
self.assertTrue(os.path.isdir(path)) self.assertTrue(os.path.isdir(path))
def test_no_keep_with_exceptions(self): def test_no_keep_with_exceptions(self):
try: try:
with Stage(archive_url, name=stage_name, keep=False) as stage: with Stage(self.archive_url,
name=self.stage_name, keep=False) as stage:
raise Exception() raise Exception()
path = self.get_stage_path(stage, stage_name) path = self.get_stage_path(stage, self.stage_name)
self.assertTrue(os.path.isdir(path)) self.assertTrue(os.path.isdir(path))
except: except:
pass # ignore here. pass # ignore here.
def test_keep_exceptions(self): def test_keep_exceptions(self):
try: try:
with Stage(archive_url, name=stage_name, keep=True) as stage: with Stage(self.archive_url,
name=self.stage_name, keep=True) as stage:
raise Exception() raise Exception()
path = self.get_stage_path(stage, stage_name) path = self.get_stage_path(stage, self.stage_name)
self.assertTrue(os.path.isdir(path)) self.assertTrue(os.path.isdir(path))
except: except:
pass # ignore here. pass # ignore here.

View File

@ -0,0 +1,68 @@
##############################################################################
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://github.com/llnl/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License (as
# published by the Free Software Foundation) version 2.1, February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
"""Utilities for managing paths in Spack.
"""
import os
import re
import spack
import getpass
import tempfile
__all__ = [
'substitute_config_variables',
'canonicalize_path']
# Substitutions to perform
replacements = {
'spack': spack.prefix,
'user': getpass.getuser(),
'tempdir': tempfile.gettempdir(),
}
def substitute_config_variables(path):
"""Substitute placeholders into paths.
Spack allows paths in configs to have some placeholders, as follows:
- $spack The Spack instance's prefix
- $user The current user's username
- $tempdir Default temporary directory returned by tempfile.gettempdir()
"""
# Look up replacements for re.sub in the replacements dict.
def repl(match):
m = match.group(0).strip('${}')
return replacements.get(m, match.group(0))
# Replace $var or ${var}.
return re.sub(r'(\$\w+\b|\$\{\w+\})', repl, path)
def canonicalize_path(path):
"""Substitute $spack, expand user home, take abspath."""
path = substitute_config_variables(path)
path = os.path.expanduser(path)
path = os.path.abspath(path)
return path