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
# (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

View File

@ -25,7 +25,6 @@
import collections
import errno
import fileinput
import getpass
import glob
import numbers
import os
@ -46,7 +45,6 @@
'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:

View File

@ -119,27 +119,6 @@
# 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.

View File

@ -44,6 +44,7 @@
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 @@ def __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):
"""A RepoPath is a list of repos that function as one.

View File

@ -28,6 +28,7 @@
import hashlib
import shutil
import tempfile
import getpass
from urlparse import urljoin
import llnl.util.tty as tty
@ -41,9 +42,72 @@
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 @@ def __init__(
# 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 @@ def _need_to_create_path(self):
# 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 @@ def chdir_to_source(self):
"""
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 @@ def restage(self):
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 @@ def create(self):
# 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."""

View File

@ -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 @@ def test_write_to_same_priority_file(self):
# 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'))

View File

@ -180,6 +180,27 @@
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 @@ def initmock(self):
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.

View File

@ -27,64 +27,71 @@
"""
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 @@ def setUp(self):
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 @@ def check_setup(self, stage, stage_name):
# 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 @@ def check_setup(self, stage, stage_name):
# 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 @@ def check_setup(self, stage, stage_name):
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 @@ def check_chdir(self, stage, stage_name):
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 @@ def check_destroy(self, stage, stage_name):
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 @@ def test_restage(self):
# 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.

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