summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2016-10-27 22:36:59 -0700
committerTodd Gamblin <tgamblin@llnl.gov>2016-10-30 23:55:00 -0700
commit0da639298c8d2d956c76d8beb9f08c54c87cfec7 (patch)
tree3977386543ff4620d4cd6ac3b3778abdc26c2316
parent9347f86939f3dea9d8d8ea723c238cd4f8c518a6 (diff)
downloadspack-0da639298c8d2d956c76d8beb9f08c54c87cfec7.tar.gz
spack-0da639298c8d2d956c76d8beb9f08c54c87cfec7.tar.bz2
spack-0da639298c8d2d956c76d8beb9f08c54c87cfec7.tar.xz
spack-0da639298c8d2d956c76d8beb9f08c54c87cfec7.zip
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
-rw-r--r--etc/spack/defaults/config.yaml10
-rw-r--r--lib/spack/llnl/util/filesystem.py12
-rw-r--r--lib/spack/spack/__init__.py21
-rw-r--r--lib/spack/spack/repository.py14
-rw-r--r--lib/spack/spack/stage.py117
-rw-r--r--lib/spack/spack/test/config.py52
-rw-r--r--lib/spack/spack/test/mock_packages_test.py28
-rw-r--r--lib/spack/spack/test/stage.py184
-rw-r--r--lib/spack/spack/util/path.py68
9 files changed, 330 insertions, 176 deletions
diff --git a/etc/spack/defaults/config.yaml b/etc/spack/defaults/config.yaml
index 5ce60049ce..6a749b5f6d 100644
--- a/etc/spack/defaults/config.yaml
+++ b/etc/spack/defaults/config.yaml
@@ -31,13 +31,13 @@ config:
# You can use $tempdir to refer to the system default temp directory
# (as returned by tempfile.gettempdir()).
#
- # A value of $local indicates that Spack should run builds directly
- # inside its install directory without staging them in temporary space.
+ # A value of $spack/var/spack/stage indicates that Spack should run
+ # builds directly inside its install directory without staging them in
+ # temporary space.
build_stage:
- - /usr/workspace/*/%u
- $tempdir
- - /nfs/tmp2/%u
- - $local
+ - /nfs/tmp2/$user
+ - $spack/var/spack/stage
# Cache directory already downloaded source tarballs and archived
diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py
index e522fdda6d..31e09f2fe6 100644
--- a/lib/spack/llnl/util/filesystem.py
+++ b/lib/spack/llnl/util/filesystem.py
@@ -25,7 +25,6 @@
import collections
import errno
import fileinput
-import getpass
import glob
import numbers
import os
@@ -46,7 +45,6 @@ __all__ = [
'can_access',
'change_sed_delimiter',
'copy_mode',
- 'expand_user',
'filter_file',
'find_libraries',
'fix_darwin_install_name',
@@ -229,16 +227,6 @@ def is_exe(path):
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):
"""Creates a directory, as well as parent directories if needed."""
for path in paths:
diff --git a/lib/spack/spack/__init__.py b/lib/spack/spack/__init__.py
index ab03c0c848..782a9b8a9f 100644
--- a/lib/spack/spack/__init__.py
+++ b/lib/spack/spack/__init__.py
@@ -119,27 +119,6 @@ editor = Executable(os.environ.get("EDITOR", "vi"))
# certifiates. e.g., curl should use the -k option.
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
# software. "Unsafe" versions are ones it doesn't have a checksum
# for.
diff --git a/lib/spack/spack/repository.py b/lib/spack/spack/repository.py
index 4696d1d5bc..47d6df85b3 100644
--- a/lib/spack/spack/repository.py
+++ b/lib/spack/spack/repository.py
@@ -44,6 +44,7 @@ import spack
import spack.error
import spack.spec
from spack.provider_index import ProviderIndex
+from spack.util.path import canonicalize_path
from spack.util.naming import *
#
@@ -93,19 +94,6 @@ class SpackNamespace(ModuleType):
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):
"""A RepoPath is a list of repos that function as one.
diff --git a/lib/spack/spack/stage.py b/lib/spack/spack/stage.py
index 7e6b543799..ff10a38ca8 100644
--- a/lib/spack/spack/stage.py
+++ b/lib/spack/spack/stage.py
@@ -28,6 +28,7 @@ import errno
import hashlib
import shutil
import tempfile
+import getpass
from urlparse import urljoin
import llnl.util.tty as tty
@@ -41,9 +42,72 @@ import spack.config
import spack.fetch_strategy as fs
import spack.error
from spack.version import *
+from spack.util.path import canonicalize_path
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):
@@ -141,9 +205,8 @@ class Stage(object):
# 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())
+ self.name = _stage_prefix + next(tempfile._get_candidate_names())
self.mirror_path = mirror_path
- self.tmp_root = find_tmp_root()
# Try to construct here a temporary name for the stage directory
# If this is a named stage, then construct a named path.
@@ -217,10 +280,11 @@ class Stage(object):
# Path looks ok, but need to check the target of the link.
if os.path.islink(self.path):
- real_path = os.path.realpath(self.path)
- real_tmp = os.path.realpath(self.tmp_root)
+ tmp_root = get_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
# right spot, then keep it.
if (real_path.startswith(real_tmp) and
@@ -416,11 +480,11 @@ class Stage(object):
"""
path = self.source_path
if not path:
- tty.die("Attempt to chdir before expanding archive.")
+ raise StageError("Attempt to chdir before expanding archive.")
else:
os.chdir(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):
"""Removes the expanded archive path if it exists, then re-expands
@@ -429,17 +493,17 @@ class Stage(object):
self.fetcher.reset()
def create(self):
- """
- Creates the stage directory
+ """Creates the stage directory.
- If self.tmp_root evaluates to False, the stage directory is
- created directly under spack.stage_path, otherwise this will
- attempt to create a stage in a temporary directory and link it
- into spack.stage_path.
+ If get_tmp_root() is None, the stage directory is created
+ directly under spack.stage_path, otherwise this will attempt to
+ create a stage in a temporary directory and link it into
+ spack.stage_path.
Spack will use the first writable location in spack.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.
+
"""
# Create the top-level stage directory
mkdirp(spack.stage_path)
@@ -448,8 +512,10 @@ class Stage(object):
# 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)
+ tmp_root = get_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)
else:
mkdirp(self.path)
@@ -614,25 +680,6 @@ def purge():
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):
""""Superclass for all errors encountered during staging."""
diff --git a/lib/spack/spack/test/config.py b/lib/spack/spack/test/config.py
index 02f53a5261..de6cd79594 100644
--- a/lib/spack/spack/test/config.py
+++ b/lib/spack/spack/test/config.py
@@ -24,10 +24,12 @@
##############################################################################
import os
import shutil
+import getpass
from tempfile import mkdtemp
import spack
import spack.config
+from spack.util.path import canonicalize_path
from ordereddict_backport import OrderedDict
from spack.test.mock_packages_test import *
@@ -217,3 +219,53 @@ class ConfigTest(MockPackagesTest):
# Same check again, to ensure consistency.
self.check_config(a_comps, *self.a_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'))
diff --git a/lib/spack/spack/test/mock_packages_test.py b/lib/spack/spack/test/mock_packages_test.py
index cba9c81e9d..4e1f243c82 100644
--- a/lib/spack/spack/test/mock_packages_test.py
+++ b/lib/spack/spack/test/mock_packages_test.py
@@ -180,6 +180,27 @@ packages:
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):
@@ -199,11 +220,10 @@ class MockPackagesTest(unittest.TestCase):
self.mock_user_config = os.path.join(self.temp_config, 'user')
mkdirp(self.mock_site_config)
mkdirp(self.mock_user_config)
- for confs in [('compilers.yaml', mock_compiler_config),
- ('packages.yaml', mock_packages_config)]:
- conf_yaml = os.path.join(self.mock_site_config, confs[0])
+ for filename, data in mock_configs.items():
+ conf_yaml = os.path.join(self.mock_site_config, filename)
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: constructor modifies config_scopes. Make it cleaner.
diff --git a/lib/spack/spack/test/stage.py b/lib/spack/spack/test/stage.py
index ec661bfe50..a21142c2cb 100644
--- a/lib/spack/spack/test/stage.py
+++ b/lib/spack/spack/test/stage.py
@@ -27,64 +27,71 @@ Test that the Stage class works correctly.
"""
import os
import shutil
-import unittest
+import tempfile
from contextlib import *
import spack
+import spack.stage
from llnl.util.filesystem import *
from spack.stage import Stage
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 = 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'
+_test_tmp_path = None
@contextmanager
def use_tmp(use_tmp):
- """Allow some test code to be executed with spack.use_tmp_stage
- set to a certain value. Context manager makes sure it's reset
- on failure.
+ """Allow some test code to be executed such that spack will either use or
+ not use temporary space for stages.
"""
- old_tmp = spack.use_tmp_stage
- spack.use_tmp_stage = use_tmp
+ # mock up config
+ path = _test_tmp_path if use_tmp else spack.stage_path
+ spack.config.update_config(
+ 'config', {'build_stage': [path]}, scope='user')
yield
- spack.use_tmp_stage = old_tmp
-class StageTest(unittest.TestCase):
+class StageTest(MockPackagesTest):
def setUp(self):
"""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
is done by individual tests.
"""
- if os.path.exists(test_files_dir):
- shutil.rmtree(test_files_dir)
+ global _test_tmp_path
+
+ 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(test_tmp_path)
+ mkdirp(self.test_tmp_path)
with open(test_readme, 'w') as readme:
- readme.write(readme_text)
+ readme.write(self.readme_text)
- with working_dir(test_files_dir):
- tar = which('tar')
- tar('czf', archive_name, archive_dir)
+ with working_dir(self.test_files_dir):
+ tar = which('tar', required=True)
+ tar('czf', self.archive_name, self.archive_dir)
# Make spack use the test environment for tmp stuff.
- self.old_tmp_dirs = spack.tmp_dirs
- spack.tmp_dirs = [test_tmp_path]
+ self._old_tmp_root = spack.stage._tmp_root
+ 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
# be removed.
@@ -92,13 +99,14 @@ class StageTest(unittest.TestCase):
def tearDown(self):
"""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
os.chdir(self.working_dir)
# 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):
"""Figure out where a stage should be living. This depends on
@@ -120,7 +128,7 @@ class StageTest(unittest.TestCase):
# Ensure stage was created in the spack stage directory
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.
self.assertTrue(os.path.islink(stage_path))
@@ -131,7 +139,7 @@ class StageTest(unittest.TestCase):
# Make sure the directory is in the place we asked it to
# be (see setUp and tearDown)
- self.assertTrue(target.startswith(test_tmp_path))
+ self.assertTrue(target.startswith(self.test_tmp_path))
else:
# Make sure the stage path is NOT a link for a non-tmp stage
@@ -139,24 +147,24 @@ class StageTest(unittest.TestCase):
def check_fetch(self, stage, stage_name):
stage_path = self.get_stage_path(stage, stage_name)
- self.assertTrue(archive_name in os.listdir(stage_path))
- self.assertEqual(join_path(stage_path, archive_name),
+ self.assertTrue(self.archive_name in os.listdir(stage_path))
+ self.assertEqual(join_path(stage_path, self.archive_name),
stage.fetcher.archive_file)
def check_expand_archive(self, stage, stage_name):
stage_path = self.get_stage_path(stage, stage_name)
- self.assertTrue(archive_name in os.listdir(stage_path))
- self.assertTrue(archive_dir in os.listdir(stage_path))
+ self.assertTrue(self.archive_name in os.listdir(stage_path))
+ self.assertTrue(self.archive_dir in os.listdir(stage_path))
self.assertEqual(
- join_path(stage_path, archive_dir),
+ join_path(stage_path, self.archive_dir),
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))
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):
stage_path = self.get_stage_path(stage, stage_name)
@@ -165,7 +173,7 @@ class StageTest(unittest.TestCase):
def check_chdir_to_source(self, stage, stage_name):
stage_path = self.get_stage_path(stage, stage_name)
self.assertEqual(
- join_path(os.path.realpath(stage_path), archive_dir),
+ join_path(os.path.realpath(stage_path), self.archive_dir),
os.getcwd())
def check_destroy(self, stage, stage_name):
@@ -176,76 +184,76 @@ class StageTest(unittest.TestCase):
self.assertFalse(os.path.exists(stage_path))
# 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)
self.assertFalse(os.path.exists(target))
def test_setup_and_destroy_name_with_tmp(self):
with use_tmp(True):
- with Stage(archive_url, name=stage_name) as stage:
- self.check_setup(stage, stage_name)
- self.check_destroy(stage, stage_name)
+ with Stage(self.archive_url, name=self.stage_name) as stage:
+ self.check_setup(stage, self.stage_name)
+ self.check_destroy(stage, self.stage_name)
def test_setup_and_destroy_name_without_tmp(self):
with use_tmp(False):
- with Stage(archive_url, name=stage_name) as stage:
- self.check_setup(stage, stage_name)
- self.check_destroy(stage, stage_name)
+ with Stage(self.archive_url, name=self.stage_name) as stage:
+ self.check_setup(stage, self.stage_name)
+ self.check_destroy(stage, self.stage_name)
def test_setup_and_destroy_no_name_with_tmp(self):
with use_tmp(True):
- with Stage(archive_url) as stage:
+ with Stage(self.archive_url) as stage:
self.check_setup(stage, None)
self.check_destroy(stage, None)
def test_setup_and_destroy_no_name_without_tmp(self):
with use_tmp(False):
- with Stage(archive_url) as stage:
+ with Stage(self.archive_url) as stage:
self.check_setup(stage, None)
self.check_destroy(stage, None)
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()
- self.check_setup(stage, stage_name)
- self.check_chdir(stage, stage_name)
- self.check_destroy(stage, stage_name)
+ self.check_setup(stage, self.stage_name)
+ self.check_chdir(stage, self.stage_name)
+ self.check_destroy(stage, self.stage_name)
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()
- self.check_setup(stage, stage_name)
- self.check_chdir(stage, stage_name)
- self.check_fetch(stage, stage_name)
- self.check_destroy(stage, stage_name)
+ self.check_setup(stage, self.stage_name)
+ self.check_chdir(stage, self.stage_name)
+ self.check_fetch(stage, self.stage_name)
+ self.check_destroy(stage, self.stage_name)
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()
- self.check_setup(stage, stage_name)
- self.check_fetch(stage, stage_name)
+ self.check_setup(stage, self.stage_name)
+ self.check_fetch(stage, self.stage_name)
stage.expand_archive()
- self.check_expand_archive(stage, stage_name)
- self.check_destroy(stage, stage_name)
+ self.check_expand_archive(stage, self.stage_name)
+ self.check_destroy(stage, self.stage_name)
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()
- self.check_setup(stage, stage_name)
- self.check_fetch(stage, stage_name)
+ self.check_setup(stage, self.stage_name)
+ self.check_fetch(stage, self.stage_name)
stage.expand_archive()
stage.chdir_to_source()
- self.check_expand_archive(stage, stage_name)
- self.check_chdir_to_source(stage, stage_name)
- self.check_destroy(stage, stage_name)
+ self.check_expand_archive(stage, self.stage_name)
+ self.check_chdir_to_source(stage, self.stage_name)
+ self.check_destroy(stage, self.stage_name)
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.expand_archive()
stage.chdir_to_source()
- self.check_expand_archive(stage, stage_name)
- self.check_chdir_to_source(stage, stage_name)
+ self.check_expand_archive(stage, self.stage_name)
+ self.check_chdir_to_source(stage, self.stage_name)
# Try to make a file in the old archive dir
with open('foobar', 'w') as file:
@@ -255,40 +263,44 @@ class StageTest(unittest.TestCase):
# Make sure the file is not there after restage.
stage.restage()
- self.check_chdir(stage, stage_name)
- self.check_fetch(stage, stage_name)
+ self.check_chdir(stage, self.stage_name)
+ self.check_fetch(stage, self.stage_name)
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.check_destroy(stage, stage_name)
+ self.check_destroy(stage, self.stage_name)
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
- self.check_destroy(stage, stage_name)
+ self.check_destroy(stage, self.stage_name)
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
- path = self.get_stage_path(stage, stage_name)
+ path = self.get_stage_path(stage, self.stage_name)
self.assertTrue(os.path.isdir(path))
def test_no_keep_with_exceptions(self):
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()
- path = self.get_stage_path(stage, stage_name)
+ path = self.get_stage_path(stage, self.stage_name)
self.assertTrue(os.path.isdir(path))
except:
pass # ignore here.
def test_keep_exceptions(self):
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()
- path = self.get_stage_path(stage, stage_name)
+ path = self.get_stage_path(stage, self.stage_name)
self.assertTrue(os.path.isdir(path))
except:
pass # ignore here.
diff --git a/lib/spack/spack/util/path.py b/lib/spack/spack/util/path.py
new file mode 100644
index 0000000000..5332115ae9
--- /dev/null
+++ b/lib/spack/spack/util/path.py
@@ -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