Support os-specific $padding in config:install_tree
Providing only $padding or ${padding} results in an attempt to substitute a padding of maximum system path length, while leaving room for the parts of the install path spack generates. Providing $padding-<len> or ${padding-<len>} simply substitutes padding of the specified length.
This commit is contained in:
parent
ea818caca3
commit
47b3dda1aa
@ -23,7 +23,7 @@
|
|||||||
import spack.schema.mirrors
|
import spack.schema.mirrors
|
||||||
import spack.schema.repos
|
import spack.schema.repos
|
||||||
import spack.util.spack_yaml as syaml
|
import spack.util.spack_yaml as syaml
|
||||||
from spack.util.path import canonicalize_path
|
import spack.util.path as spack_path
|
||||||
|
|
||||||
|
|
||||||
# sample config data
|
# sample config data
|
||||||
@ -272,31 +272,31 @@ def test_substitute_config_variables(mock_low_high_config):
|
|||||||
|
|
||||||
assert os.path.join(
|
assert os.path.join(
|
||||||
'/foo/bar/baz', prefix
|
'/foo/bar/baz', prefix
|
||||||
) == canonicalize_path('/foo/bar/baz/$spack')
|
) == spack_path.canonicalize_path('/foo/bar/baz/$spack')
|
||||||
|
|
||||||
assert os.path.join(
|
assert os.path.join(
|
||||||
spack.paths.prefix, 'foo/bar/baz'
|
spack.paths.prefix, 'foo/bar/baz'
|
||||||
) == canonicalize_path('$spack/foo/bar/baz/')
|
) == spack_path.canonicalize_path('$spack/foo/bar/baz/')
|
||||||
|
|
||||||
assert os.path.join(
|
assert os.path.join(
|
||||||
'/foo/bar/baz', prefix, 'foo/bar/baz'
|
'/foo/bar/baz', prefix, 'foo/bar/baz'
|
||||||
) == canonicalize_path('/foo/bar/baz/$spack/foo/bar/baz/')
|
) == spack_path.canonicalize_path('/foo/bar/baz/$spack/foo/bar/baz/')
|
||||||
|
|
||||||
assert os.path.join(
|
assert os.path.join(
|
||||||
'/foo/bar/baz', prefix
|
'/foo/bar/baz', prefix
|
||||||
) == canonicalize_path('/foo/bar/baz/${spack}')
|
) == spack_path.canonicalize_path('/foo/bar/baz/${spack}')
|
||||||
|
|
||||||
assert os.path.join(
|
assert os.path.join(
|
||||||
spack.paths.prefix, 'foo/bar/baz'
|
spack.paths.prefix, 'foo/bar/baz'
|
||||||
) == canonicalize_path('${spack}/foo/bar/baz/')
|
) == spack_path.canonicalize_path('${spack}/foo/bar/baz/')
|
||||||
|
|
||||||
assert os.path.join(
|
assert os.path.join(
|
||||||
'/foo/bar/baz', prefix, 'foo/bar/baz'
|
'/foo/bar/baz', prefix, 'foo/bar/baz'
|
||||||
) == canonicalize_path('/foo/bar/baz/${spack}/foo/bar/baz/')
|
) == spack_path.canonicalize_path('/foo/bar/baz/${spack}/foo/bar/baz/')
|
||||||
|
|
||||||
assert os.path.join(
|
assert os.path.join(
|
||||||
'/foo/bar/baz', prefix, 'foo/bar/baz'
|
'/foo/bar/baz', prefix, 'foo/bar/baz'
|
||||||
) != canonicalize_path('/foo/bar/baz/${spack/foo/bar/baz/')
|
) != spack_path.canonicalize_path('/foo/bar/baz/${spack/foo/bar/baz/')
|
||||||
|
|
||||||
|
|
||||||
packages_merge_low = {
|
packages_merge_low = {
|
||||||
@ -345,19 +345,43 @@ def test_merge_with_defaults(mock_low_high_config, write_config_file):
|
|||||||
|
|
||||||
def test_substitute_user(mock_low_high_config):
|
def test_substitute_user(mock_low_high_config):
|
||||||
user = getpass.getuser()
|
user = getpass.getuser()
|
||||||
assert '/foo/bar/' + user + '/baz' == canonicalize_path(
|
assert '/foo/bar/' + user + '/baz' == spack_path.canonicalize_path(
|
||||||
'/foo/bar/$user/baz'
|
'/foo/bar/$user/baz'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_substitute_tempdir(mock_low_high_config):
|
def test_substitute_tempdir(mock_low_high_config):
|
||||||
tempdir = tempfile.gettempdir()
|
tempdir = tempfile.gettempdir()
|
||||||
assert tempdir == canonicalize_path('$tempdir')
|
assert tempdir == spack_path.canonicalize_path('$tempdir')
|
||||||
assert tempdir + '/foo/bar/baz' == canonicalize_path(
|
assert tempdir + '/foo/bar/baz' == spack_path.canonicalize_path(
|
||||||
'$tempdir/foo/bar/baz'
|
'$tempdir/foo/bar/baz'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_substitute_padding(mock_low_high_config):
|
||||||
|
max_system_path = spack_path.get_system_path_max()
|
||||||
|
expected_length = (max_system_path -
|
||||||
|
spack_path.SPACK_MAX_INSTALL_PATH_LENGTH)
|
||||||
|
|
||||||
|
install_path = spack_path.canonicalize_path('/foo/bar/${padding}/baz')
|
||||||
|
|
||||||
|
assert spack_path.SPACK_PATH_PADDING_CHARS in install_path
|
||||||
|
assert len(install_path) == expected_length
|
||||||
|
|
||||||
|
install_path = spack_path.canonicalize_path('/foo/bar/baz/gah/$padding')
|
||||||
|
|
||||||
|
assert spack_path.SPACK_PATH_PADDING_CHARS in install_path
|
||||||
|
assert len(install_path) == expected_length
|
||||||
|
|
||||||
|
i_path = spack_path.canonicalize_path('/foo/$padding:10')
|
||||||
|
i_expect = os.path.join('/foo', spack_path.SPACK_PATH_PADDING_CHARS[:10])
|
||||||
|
assert i_path == i_expect
|
||||||
|
|
||||||
|
i_path = spack_path.canonicalize_path('/foo/${padding:20}')
|
||||||
|
i_expect = os.path.join('/foo', spack_path.SPACK_PATH_PADDING_CHARS[:20])
|
||||||
|
assert i_path == i_expect
|
||||||
|
|
||||||
|
|
||||||
def test_read_config(mock_low_high_config, write_config_file):
|
def test_read_config(mock_low_high_config, write_config_file):
|
||||||
write_config_file('config', config_low, 'low')
|
write_config_file('config', config_low, 'low')
|
||||||
assert spack.config.get('config') == config_low['config']
|
assert spack.config.get('config') == config_low['config']
|
||||||
|
@ -10,8 +10,12 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import getpass
|
import getpass
|
||||||
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
import llnl.util.tty as tty
|
||||||
|
from llnl.util.lang import memoized
|
||||||
|
|
||||||
import spack.paths
|
import spack.paths
|
||||||
|
|
||||||
|
|
||||||
@ -27,6 +31,38 @@
|
|||||||
'tempdir': tempfile.gettempdir(),
|
'tempdir': tempfile.gettempdir(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# This is intended to be longer than the part of the install path
|
||||||
|
# spack generates from the root path we give it. Included in the
|
||||||
|
# estimate:
|
||||||
|
#
|
||||||
|
# os-arch -> 30
|
||||||
|
# compiler -> 30
|
||||||
|
# package name -> 50 (longest is currently 47 characters)
|
||||||
|
# version -> 20
|
||||||
|
# hash -> 32
|
||||||
|
# buffer -> 138
|
||||||
|
# ---------------------
|
||||||
|
# total -> 300
|
||||||
|
SPACK_MAX_INSTALL_PATH_LENGTH = 300
|
||||||
|
SPACK_PATH_PADDING_CHARS = 'spack_path_placeholder'
|
||||||
|
|
||||||
|
|
||||||
|
@memoized
|
||||||
|
def get_system_path_max():
|
||||||
|
# Choose a conservative default
|
||||||
|
sys_max_path_length = 256
|
||||||
|
try:
|
||||||
|
path_max_proc = subprocess.Popen(['getconf', 'PATH_MAX', '/'],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
proc_output = str(path_max_proc.communicate()[0].decode())
|
||||||
|
sys_max_path_length = int(proc_output)
|
||||||
|
except (ValueError, subprocess.CalledProcessError, OSError):
|
||||||
|
tty.msg('Unable to find system max path length, using: {0}'.format(
|
||||||
|
sys_max_path_length))
|
||||||
|
|
||||||
|
return sys_max_path_length
|
||||||
|
|
||||||
|
|
||||||
def substitute_config_variables(path):
|
def substitute_config_variables(path):
|
||||||
"""Substitute placeholders into paths.
|
"""Substitute placeholders into paths.
|
||||||
@ -58,8 +94,45 @@ def substitute_path_variables(path):
|
|||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def _get_padding_string(length):
|
||||||
|
spack_path_padding_size = len(SPACK_PATH_PADDING_CHARS)
|
||||||
|
num_reps = int(length / (spack_path_padding_size + 1))
|
||||||
|
extra_chars = length % (spack_path_padding_size + 1)
|
||||||
|
reps_list = [SPACK_PATH_PADDING_CHARS for i in range(num_reps)]
|
||||||
|
reps_list.append(SPACK_PATH_PADDING_CHARS[:extra_chars])
|
||||||
|
return os.path.sep.join(reps_list)
|
||||||
|
|
||||||
|
|
||||||
|
def _add_computed_padding(path):
|
||||||
|
"""Subtitute in padding of os-specific length. The intent is to leave
|
||||||
|
SPACK_MAX_INSTALL_PATH_LENGTH characters available for parts of the
|
||||||
|
path generated by spack. This is to allow for not-completely-known
|
||||||
|
lengths of things like os/arch, compiler, package name, hash length,
|
||||||
|
etc.
|
||||||
|
"""
|
||||||
|
padding_regex = re.compile(r'(\$[\w\d\:]+\b|\$\{[\w\d\:]+\})')
|
||||||
|
m = padding_regex.search(path)
|
||||||
|
if m and m.group(0).strip('${}').startswith('padding'):
|
||||||
|
padding_part = m.group(0)
|
||||||
|
len_pad_part = len(m.group(0))
|
||||||
|
p_match = re.search(r'\:(\d+)', padding_part)
|
||||||
|
if p_match:
|
||||||
|
computed_padding = _get_padding_string(int(p_match.group(1)))
|
||||||
|
else:
|
||||||
|
# Take whatever has been computed/substituted so far and add some
|
||||||
|
# room
|
||||||
|
path_len = len(path) - len_pad_part + SPACK_MAX_INSTALL_PATH_LENGTH
|
||||||
|
system_max_path = get_system_path_max()
|
||||||
|
needed_pad_len = system_max_path - path_len
|
||||||
|
computed_padding = _get_padding_string(needed_pad_len)
|
||||||
|
return padding_regex.sub(computed_padding, path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
def canonicalize_path(path):
|
def canonicalize_path(path):
|
||||||
"""Same as substitute_path_variables, but also take absolute path."""
|
"""Same as substitute_path_variables, but also take absolute path."""
|
||||||
path = substitute_path_variables(path)
|
path = substitute_path_variables(path)
|
||||||
path = os.path.abspath(path)
|
path = os.path.abspath(path)
|
||||||
|
path = _add_computed_padding(path)
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
Loading…
Reference in New Issue
Block a user