Features: Improve Spec format strings (#10556)

* Update spec format to simpler syntax, maintain backwards compatibility
* Switch to new spec.format method throughout internals
* update package files for new format strings
* documentation and minor code cleanup. removed nonsensical variant sigils
This commit is contained in:
Greg Becker 2019-04-17 18:21:40 -07:00 committed by Todd Gamblin
parent 49334f006d
commit f242f5f8a9
42 changed files with 568 additions and 180 deletions

View File

@ -39,7 +39,7 @@ default path uses the full 32 characters.
Secondly, it is Secondly, it is
also possible to modify the entire installation scheme. By default also possible to modify the entire installation scheme. By default
Spack uses Spack uses
``${ARCHITECTURE}/${COMPILERNAME}-${COMPILERVER}/${PACKAGE}-${VERSION}-${HASH}`` ``{architecture}/{compiler.name}-{compiler.version}/{name}-{version}-{hash}``
where the tokens that are available for use in this directive are the where the tokens that are available for use in this directive are the
same as those understood by the ``Spec.format`` method. Using this parameter it same as those understood by the ``Spec.format`` method. Using this parameter it
is possible to use a different package layout or reduce the depth of is possible to use a different package layout or reduce the depth of
@ -48,7 +48,7 @@ the installation paths. For example
.. code-block:: yaml .. code-block:: yaml
config: config:
install_path_scheme: '${PACKAGE}/${VERSION}/${HASH:7}' install_path_scheme: '{name}/{version}/{hash:7}'
would install packages into sub-directories using only the package would install packages into sub-directories using only the package
name, version and a hash length of 7 characters. name, version and a hash length of 7 characters.

View File

@ -459,7 +459,7 @@ account all scopes. For example, to see the fully merged
install_tree: $spack/opt/spack install_tree: $spack/opt/spack
template_dirs: template_dirs:
- $spack/templates - $spack/templates
directory_layout: ${ARCHITECTURE}/${COMPILERNAME}-${COMPILERVER}/${PACKAGE}-${VERSION}-${HASH} directory_layout: {architecture}/{compiler.name}-{compiler.version}/{name}-{version}-{hash}
module_roots: module_roots:
tcl: $spack/share/spack/modules tcl: $spack/share/spack/modules
lmod: $spack/share/spack/lmod lmod: $spack/share/spack/lmod
@ -510,7 +510,7 @@ down the problem:
./my-scope/config.yaml:2 install_tree: /path/to/some/tree ./my-scope/config.yaml:2 install_tree: /path/to/some/tree
/home/myuser/spack/etc/spack/defaults/config.yaml:23 template_dirs: /home/myuser/spack/etc/spack/defaults/config.yaml:23 template_dirs:
/home/myuser/spack/etc/spack/defaults/config.yaml:24 - $spack/templates /home/myuser/spack/etc/spack/defaults/config.yaml:24 - $spack/templates
/home/myuser/spack/etc/spack/defaults/config.yaml:28 directory_layout: ${ARCHITECTURE}/${COMPILERNAME}-${COMPILERVER}/${PACKAGE}-${VERSION}-${HASH} /home/myuser/spack/etc/spack/defaults/config.yaml:28 directory_layout: {architecture}/{compiler.name}-{compiler.version}/{name}-{version}-{hash}
/home/myuser/spack/etc/spack/defaults/config.yaml:32 module_roots: /home/myuser/spack/etc/spack/defaults/config.yaml:32 module_roots:
/home/myuser/spack/etc/spack/defaults/config.yaml:33 tcl: $spack/share/spack/modules /home/myuser/spack/etc/spack/defaults/config.yaml:33 tcl: $spack/share/spack/modules
/home/myuser/spack/etc/spack/defaults/config.yaml:34 lmod: $spack/share/spack/lmod /home/myuser/spack/etc/spack/defaults/config.yaml:34 lmod: $spack/share/spack/lmod

View File

@ -535,10 +535,10 @@ most likely via the ``+blas`` variant specification.
modules: modules:
tcl: tcl:
naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}-${COMPILERVER}' naming_scheme: '{name}/{version}-{compiler.name}-{compiler.version}'
all: all:
conflict: conflict:
- '${PACKAGE}' - '{name}'
- 'intel/14.0.1' - 'intel/14.0.1'
will create module files that will conflict with ``intel/14.0.1`` and with the will create module files that will conflict with ``intel/14.0.1`` and with the

View File

@ -646,14 +646,14 @@ modules that refer to different flavors of the same library/application:
modules: modules:
tcl: tcl:
hash_length: 0 hash_length: 0
naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}-${COMPILERVER}' naming_scheme: '{name}/{version}-{compiler.name}-{compiler.version}'
whitelist: whitelist:
- gcc - gcc
blacklist: blacklist:
- '%gcc@5.4.0' - '%gcc@5.4.0'
all: all:
conflict: conflict:
- '${PACKAGE}' - '{name}'
suffixes: suffixes:
'^openblas': openblas '^openblas': openblas
'^netlib-lapack': netlib '^netlib-lapack': netlib
@ -713,14 +713,14 @@ is installed. You can achieve this with Spack by adding an
modules: modules:
tcl: tcl:
hash_length: 0 hash_length: 0
naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}-${COMPILERVER}' naming_scheme: '{name}/{version}-{compiler.name}-{compiler.version}'
whitelist: whitelist:
- gcc - gcc
blacklist: blacklist:
- '%gcc@5.4.0' - '%gcc@5.4.0'
all: all:
conflict: conflict:
- '${PACKAGE}' - '{name}'
suffixes: suffixes:
'^openblas': openblas '^openblas': openblas
'^netlib-lapack': netlib '^netlib-lapack': netlib
@ -728,7 +728,7 @@ is installed. You can achieve this with Spack by adding an
environment_blacklist: ['CPATH', 'LIBRARY_PATH'] environment_blacklist: ['CPATH', 'LIBRARY_PATH']
environment: environment:
set: set:
'${PACKAGE}_ROOT': '${PREFIX}' '{name}_ROOT': '{prefix}'
netlib-scalapack: netlib-scalapack:
suffixes: suffixes:
'^openmpi': openmpi '^openmpi': openmpi
@ -737,8 +737,9 @@ is installed. You can achieve this with Spack by adding an
Under the hood Spack uses the :meth:`~spack.spec.Spec.format` API to substitute Under the hood Spack uses the :meth:`~spack.spec.Spec.format` API to substitute
tokens in either environment variable names or values. There are two caveats though: tokens in either environment variable names or values. There are two caveats though:
- The set of allowed tokens in variable names is restricted to ``PACKAGE``, - The set of allowed tokens in variable names is restricted to
``VERSION``, ``COMPILER``, ``COMPILERNAME``, ``COMPILERVER``, ``ARCHITECTURE`` ``name``, ``version``, ``compiler``, ``compiler.name``,
``compiler.version``, ``architecture``
- Any token expanded in a variable name is made uppercase, but other than that - Any token expanded in a variable name is made uppercase, but other than that
case sensitivity is preserved case sensitivity is preserved
@ -784,14 +785,14 @@ etc. in the ``gcc`` module file and apply other custom modifications to the
modules: modules:
tcl: tcl:
hash_length: 0 hash_length: 0
naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}-${COMPILERVER}' naming_scheme: '{name}/{version}-{compiler.name}-{compiler.version}'
whitelist: whitelist:
- gcc - gcc
blacklist: blacklist:
- '%gcc@5.4.0' - '%gcc@5.4.0'
all: all:
conflict: conflict:
- '${PACKAGE}' - '{name}'
suffixes: suffixes:
'^openblas': openblas '^openblas': openblas
'^netlib-lapack': netlib '^netlib-lapack': netlib
@ -799,7 +800,7 @@ etc. in the ``gcc`` module file and apply other custom modifications to the
environment_blacklist: ['CPATH', 'LIBRARY_PATH'] environment_blacklist: ['CPATH', 'LIBRARY_PATH']
environment: environment:
set: set:
'${PACKAGE}_ROOT': '${PREFIX}' '{name}_ROOT': '{prefix}'
gcc: gcc:
environment: environment:
set: set:
@ -896,14 +897,14 @@ directive and assigning it the value ``direct``:
tcl: tcl:
verbose: True verbose: True
hash_length: 0 hash_length: 0
naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}-${COMPILERVER}' naming_scheme: '{name}/{version}-{compiler.name}-{compiler.version}'
whitelist: whitelist:
- gcc - gcc
blacklist: blacklist:
- '%gcc@5.4.0' - '%gcc@5.4.0'
all: all:
conflict: conflict:
- '${PACKAGE}' - '{name}'
suffixes: suffixes:
'^openblas': openblas '^openblas': openblas
'^netlib-lapack': netlib '^netlib-lapack': netlib
@ -911,7 +912,7 @@ directive and assigning it the value ``direct``:
environment_blacklist: ['CPATH', 'LIBRARY_PATH'] environment_blacklist: ['CPATH', 'LIBRARY_PATH']
environment: environment:
set: set:
'${PACKAGE}_ROOT': '${PREFIX}' '{name}_ROOT': '{prefix}'
gcc: gcc:
environment: environment:
set: set:
@ -1089,7 +1090,7 @@ After these modifications your configuration file should look like:
environment_blacklist: ['CPATH', 'LIBRARY_PATH'] environment_blacklist: ['CPATH', 'LIBRARY_PATH']
environment: environment:
set: set:
'${PACKAGE}_ROOT': '${PREFIX}' '{name}_ROOT': '{prefix}'
gcc: gcc:
environment: environment:
set: set:
@ -1298,7 +1299,7 @@ Coming back to our example, let's add ``lapack`` to the hierarchy and remove any
environment_blacklist: ['CPATH', 'LIBRARY_PATH'] environment_blacklist: ['CPATH', 'LIBRARY_PATH']
environment: environment:
set: set:
'${PACKAGE}_ROOT': '${PREFIX}' '{name}_ROOT': '{prefix}'
gcc: gcc:
environment: environment:
set: set:
@ -1534,7 +1535,7 @@ it's ``netlib-scalapack``:
environment_blacklist: ['CPATH', 'LIBRARY_PATH'] environment_blacklist: ['CPATH', 'LIBRARY_PATH']
environment: environment:
set: set:
'${PACKAGE}_ROOT': '${PREFIX}' '{name}_ROOT': '{prefix}'
gcc: gcc:
environment: environment:
set: set:

View File

@ -541,9 +541,9 @@ spec format strings, as shown in the example below.
.. code-block:: yaml .. code-block:: yaml
projections: projections:
zlib: ${PACKAGE}-${VERSION} zlib: {name}-{version}
^mpi: ${PACKAGE}-${VERSION}/${DEP:mpi:PACKAGE}-${DEP:mpi:VERSION}-${COMPILERNAME}-${COMPILERVER} ^mpi: {name}-{version}/{^mpi.name}-{^mpi.version}-{compiler.name}-{compiler.version}
all: ${PACKAGE}-${VERSION}/${COMPILERNAME}-${COMPILERVER} all: {name}-{version}/{compiler.name}-{compiler.version}
The entries in the projections configuration file must all be either The entries in the projections configuration file must all be either
specs or the keyword ``all``. For each spec, the projection used will specs or the keyword ``all``. For each spec, the projection used will

View File

@ -336,7 +336,7 @@ def __init__(self, plat=None, os=None, target=None):
self.platform = plat self.platform = plat
if plat and os: if plat and os:
os = self.platform.operating_system(os) os = self.platform.operating_system(os)
self.platform_os = os self.os = os
if plat and target: if plat and target:
target = self.platform.target(target) target = self.platform.target(target)
self.target = target self.target = target
@ -349,16 +349,16 @@ def __init__(self, plat=None, os=None, target=None):
def concrete(self): def concrete(self):
return all((self.platform is not None, return all((self.platform is not None,
isinstance(self.platform, Platform), isinstance(self.platform, Platform),
self.platform_os is not None, self.os is not None,
isinstance(self.platform_os, OperatingSystem), isinstance(self.os, OperatingSystem),
self.target is not None, isinstance(self.target, Target))) self.target is not None, isinstance(self.target, Target)))
def __str__(self): def __str__(self):
if self.platform or self.platform_os or self.target: if self.platform or self.os or self.target:
if self.platform.name == 'darwin': if self.platform.name == 'darwin':
os_name = self.platform_os.name if self.platform_os else "None" os_name = self.os.name if self.os else "None"
else: else:
os_name = str(self.platform_os) os_name = str(self.os)
return (str(self.platform) + "-" + return (str(self.platform) + "-" +
os_name + "-" + str(self.target)) os_name + "-" + str(self.target))
@ -371,7 +371,7 @@ def __contains__(self, string):
# TODO: make this unnecessary: don't include an empty arch on *every* spec. # TODO: make this unnecessary: don't include an empty arch on *every* spec.
def __nonzero__(self): def __nonzero__(self):
return (self.platform is not None or return (self.platform is not None or
self.platform_os is not None or self.os is not None or
self.target is not None) self.target is not None)
__bool__ = __nonzero__ __bool__ = __nonzero__
@ -380,21 +380,21 @@ def _cmp_key(self):
platform = self.platform.name platform = self.platform.name
else: else:
platform = self.platform platform = self.platform
if isinstance(self.platform_os, OperatingSystem): if isinstance(self.os, OperatingSystem):
platform_os = self.platform_os.name os = self.os.name
else: else:
platform_os = self.platform_os os = self.os
if isinstance(self.target, Target): if isinstance(self.target, Target):
target = self.target.name target = self.target.name
else: else:
target = self.target target = self.target
return (platform, platform_os, target) return (platform, os, target)
def to_dict(self): def to_dict(self):
str_or_none = lambda v: str(v) if v else None str_or_none = lambda v: str(v) if v else None
d = syaml_dict([ d = syaml_dict([
('platform', str_or_none(self.platform)), ('platform', str_or_none(self.platform)),
('platform_os', str_or_none(self.platform_os)), ('platform_os', str_or_none(self.os)),
('target', str_or_none(self.target))]) ('target', str_or_none(self.target))])
return syaml_dict([('arch', d)]) return syaml_dict([('arch', d)])
@ -430,13 +430,13 @@ def arch_for_spec(arch_spec):
assert(arch_spec.concrete) assert(arch_spec.concrete)
arch_plat = get_platform(arch_spec.platform) arch_plat = get_platform(arch_spec.platform)
if not (arch_plat.operating_system(arch_spec.platform_os) and if not (arch_plat.operating_system(arch_spec.os) and
arch_plat.target(arch_spec.target)): arch_plat.target(arch_spec.target)):
raise ValueError( raise ValueError(
"Can't recreate arch for spec %s on current arch %s; " "Can't recreate arch for spec %s on current arch %s; "
"spec architecture is too different" % (arch_spec, sys_type())) "spec architecture is too different" % (arch_spec, sys_type()))
return Arch(arch_plat, arch_spec.platform_os, arch_spec.target) return Arch(arch_plat, arch_spec.os, arch_spec.target)
@memoized @memoized

View File

@ -381,7 +381,7 @@ def set_build_environment_variables(pkg, env, dirty):
if spack.config.get('config:debug'): if spack.config.get('config:debug'):
env.set(SPACK_DEBUG, 'TRUE') env.set(SPACK_DEBUG, 'TRUE')
env.set(SPACK_SHORT_SPEC, pkg.spec.short_spec) env.set(SPACK_SHORT_SPEC, pkg.spec.short_spec)
env.set(SPACK_DEBUG_LOG_ID, pkg.spec.format('${PACKAGE}-${HASH:7}')) env.set(SPACK_DEBUG_LOG_ID, pkg.spec.format('{name}-{hash:7}'))
env.set(SPACK_DEBUG_LOG_DIR, spack.main.spack_working_dir) env.set(SPACK_DEBUG_LOG_DIR, spack.main.spack_working_dir)
# Find ccache binary and hand it to build environment # Find ccache binary and hand it to build environment

View File

@ -184,10 +184,11 @@ def disambiguate_spec(spec, env):
tty.die("Spec '%s' matches no installed packages." % spec) tty.die("Spec '%s' matches no installed packages." % spec)
elif len(matching_specs) > 1: elif len(matching_specs) > 1:
format_string = '{name}{@version}{%compiler}{arch=architecture}'
args = ["%s matches multiple packages." % spec, args = ["%s matches multiple packages." % spec,
"Matching packages:"] "Matching packages:"]
args += [colorize(" @K{%s} " % s.dag_hash(7)) + args += [colorize(" @K{%s} " % s.dag_hash(7)) +
s.cformat('$_$@$%@$=') for s in matching_specs] s.cformat(format_string) for s in matching_specs]
args += ["Use a more specific spec."] args += ["Use a more specific spec."]
tty.die(*args) tty.die(*args)
@ -263,15 +264,15 @@ def get_arg(name, default=None):
hashes = True hashes = True
hlen = None hlen = None
nfmt = '{fullpackage}' if namespace else '{package}' nfmt = '{namespace}{name}' if namespace else '{name}'
ffmt = '' ffmt = ''
if full_compiler or flags: if full_compiler or flags:
ffmt += '$%' ffmt += '{%compiler.name}'
if full_compiler: if full_compiler:
ffmt += '@' ffmt += '{@compiler.version}'
ffmt += '+' ffmt += ' {compiler_flags}'
vfmt = '$+' if variants else '' vfmt = '{variants}' if variants else ''
format_string = '$%s$@%s%s' % (nfmt, ffmt, vfmt) format_string = nfmt + '{@version}' + ffmt + vfmt
# Make a dict with specs keyed by architecture and compiler. # Make a dict with specs keyed by architecture and compiler.
index = index_by(specs, ('architecture', 'compiler')) index = index_by(specs, ('architecture', 'compiler'))
@ -329,7 +330,7 @@ def fmt(s):
if hashes: if hashes:
string += gray_hash(s, hlen) + ' ' string += gray_hash(s, hlen) + ' '
string += s.cformat( string += s.cformat(
'$%s$@%s' % (nfmt, vfmt), transform=transform) nfmt + '{@version}' + vfmt, transform=transform)
return string return string
if not flags and not full_compiler: if not flags and not full_compiler:

View File

@ -32,7 +32,7 @@ def arch(parser, args):
if args.platform: if args.platform:
print(arch.platform) print(arch.platform)
elif args.operating_system: elif args.operating_system:
print(arch.platform_os) print(arch.os)
elif args.target: elif args.target:
print(arch.target) print(arch.target)
else: else:

View File

@ -42,7 +42,8 @@ def dependencies(parser, args):
env = ev.get_env(args, 'dependencies') env = ev.get_env(args, 'dependencies')
spec = spack.cmd.disambiguate_spec(specs[0], env) spec = spack.cmd.disambiguate_spec(specs[0], env)
tty.msg("Dependencies of %s" % spec.format('$_$@$%@$/', color=True)) format_string = '{name}{@version}{%compiler}{/hash:7}'
tty.msg("Dependencies of %s" % spec.format(format_string, color=True))
deps = spack.store.db.installed_relatives( deps = spack.store.db.installed_relatives(
spec, 'children', args.transitive) spec, 'children', args.transitive)
if deps: if deps:

View File

@ -85,7 +85,8 @@ def dependents(parser, args):
env = ev.get_env(args, 'dependents') env = ev.get_env(args, 'dependents')
spec = spack.cmd.disambiguate_spec(specs[0], env) spec = spack.cmd.disambiguate_spec(specs[0], env)
tty.msg("Dependents of %s" % spec.cformat('$_$@$%@$/')) format_string = '{name}{@version}{%compiler}{/hash:7}'
tty.msg("Dependents of %s" % spec.cformat(format_string))
deps = spack.store.db.installed_relatives( deps = spack.store.db.installed_relatives(
spec, 'parents', args.transitive) spec, 'parents', args.transitive)
if deps: if deps:

View File

@ -162,7 +162,7 @@ def mirror_create(args):
# If nothing is passed, use all packages. # If nothing is passed, use all packages.
if not specs: if not specs:
specs = [Spec(n) for n in spack.repo.all_package_names()] specs = [Spec(n) for n in spack.repo.all_package_names()]
specs.sort(key=lambda s: s.format("$_$@").lower()) specs.sort(key=lambda s: s.format("{name}{@version}").lower())
# If the user asked for dependencies, traverse spec DAG get them. # If the user asked for dependencies, traverse spec DAG get them.
if args.dependencies: if args.dependencies:
@ -204,7 +204,7 @@ def mirror_create(args):
" %-4d failed to fetch." % e) " %-4d failed to fetch." % e)
if error: if error:
tty.error("Failed downloads:") tty.error("Failed downloads:")
colify(s.cformat("$_$@") for s in error) colify(s.cformat("{name}{@version}") for s in error)
def mirror(parser, args): def mirror(parser, args):

View File

@ -344,7 +344,9 @@ def modules_cmd(parser, args, module_type, callbacks=callbacks):
except MultipleSpecsMatch: except MultipleSpecsMatch:
msg = "the constraint '{query}' matches multiple packages:\n" msg = "the constraint '{query}' matches multiple packages:\n"
for s in specs: for s in specs:
msg += '\t' + s.cformat(format_string='$/ $_$@$+$%@+$+$=') + '\n' spec_fmt = '{hash:7} {name}{@version}{%compiler}'
spec_fmt += '{compiler_flags}{variants}{arch=architecture}'
msg += '\t' + s.cformat(spec_fmt) + '\n'
tty.error(msg.format(query=args.constraint)) tty.error(msg.format(query=args.constraint))
tty.die('In this context exactly **one** match is needed: please specify your constraints better.') # NOQA: ignore=E501 tty.die('In this context exactly **one** match is needed: please specify your constraints better.') # NOQA: ignore=E501

View File

@ -42,11 +42,12 @@ def setup_parser(subparser):
def spec(parser, args): def spec(parser, args):
name_fmt = '$.' if args.namespaces else '$_' name_fmt = '{namespace}.{name}' if args.namespaces else '{name}'
fmt = '{@version}{%compiler}{compiler_flags}{variants}{arch=architecture}'
install_status_fn = spack.spec.Spec.install_status install_status_fn = spack.spec.Spec.install_status
kwargs = { kwargs = {
'cover': args.cover, 'cover': args.cover,
'format': name_fmt + '$@$%@+$+$=', 'format': name_fmt + fmt,
'hashlen': None if args.very_long else 7, 'hashlen': None if args.very_long else 7,
'show_types': args.types, 'show_types': args.types,
'status_fn': install_status_fn if args.install_status else None 'status_fn': install_status_fn if args.install_status else None

View File

@ -260,7 +260,8 @@ def get_uninstall_list(args, specs, env):
if i > 0: if i > 0:
print() print()
tty.info("Will not uninstall %s" % spec.cformat("$_$@$%@$/"), spec_format = '{name}{@version}{%compiler}{/hash:7}'
tty.info("Will not uninstall %s" % spec.cformat(spec_format),
format='*r') format='*r')
dependents = active_dpts.get(spec) dependents = active_dpts.get(spec)

View File

@ -70,10 +70,11 @@ def squash(matching_specs):
matching_in_view = [ms for ms in matching_specs if ms in view_specs] matching_in_view = [ms for ms in matching_specs if ms in view_specs]
if len(matching_in_view) > 1: if len(matching_in_view) > 1:
spec_format = '{name}{@version}{%compiler}{arch=architecture}'
args = ["Spec matches multiple packages.", args = ["Spec matches multiple packages.",
"Matching packages:"] "Matching packages:"]
args += [colorize(" @K{%s} " % s.dag_hash(7)) + args += [colorize(" @K{%s} " % s.dag_hash(7)) +
s.cformat('$_$@$%@$=') for s in matching_in_view] s.cformat(spec_format) for s in matching_in_view]
args += ["Use a more specific spec."] args += ["Use a more specific spec."]
tty.die(*args) tty.die(*args)

View File

@ -307,7 +307,7 @@ def get_compilers(config, cspec=None, arch_spec=None):
# If an arch spec is given, confirm that this compiler # If an arch spec is given, confirm that this compiler
# is for the given operating system # is for the given operating system
os = items.get('operating_system', None) os = items.get('operating_system', None)
if arch_spec and os != arch_spec.platform_os: if arch_spec and os != arch_spec.os:
continue continue
# If an arch spec is given, confirm that this compiler # If an arch spec is given, confirm that this compiler
@ -333,7 +333,7 @@ def compiler_for_spec(compiler_spec, arch_spec):
compilers = compilers_for_spec(compiler_spec, arch_spec=arch_spec) compilers = compilers_for_spec(compiler_spec, arch_spec=arch_spec)
if len(compilers) < 1: if len(compilers) < 1:
raise NoCompilerForSpecError(compiler_spec, arch_spec.platform_os) raise NoCompilerForSpecError(compiler_spec, arch_spec.os)
if len(compilers) > 1: if len(compilers) > 1:
raise CompilerDuplicateError(compiler_spec, arch_spec) raise CompilerDuplicateError(compiler_spec, arch_spec)
return compilers[0] return compilers[0]

View File

@ -285,7 +285,7 @@ def concretize_compiler(self, spec):
""" """
# Pass on concretizing the compiler if the target or operating system # Pass on concretizing the compiler if the target or operating system
# is not yet determined # is not yet determined
if not (spec.architecture.platform_os and spec.architecture.target): if not (spec.architecture.os and spec.architecture.target):
# We haven't changed, but other changes need to happen before we # We haven't changed, but other changes need to happen before we
# continue. `return True` here to force concretization to keep # continue. `return True` here to force concretization to keep
# running. # running.
@ -371,7 +371,7 @@ def concretize_compiler_flags(self, spec):
""" """
# Pass on concretizing the compiler flags if the target or operating # Pass on concretizing the compiler flags if the target or operating
# system is not set. # system is not set.
if not (spec.architecture.platform_os and spec.architecture.target): if not (spec.architecture.os and spec.architecture.target):
# We haven't changed, but other changes need to happen before we # We haven't changed, but other changes need to happen before we
# continue. `return True` here to force concretization to keep # continue. `return True` here to force concretization to keep
# running. # running.
@ -471,7 +471,7 @@ def __init__(self, arch, available_os_targets):
" for operating system %s and target %s." " for operating system %s and target %s."
"\nIf previous installations have succeeded, the" "\nIf previous installations have succeeded, the"
" operating system may have been updated." % " operating system may have been updated." %
(arch.platform_os, arch.target)) (arch.os, arch.target))
available_os_target_strs = list() available_os_target_strs = list()
for os, t in available_os_targets: for os, t in available_os_targets:
@ -494,7 +494,7 @@ def __init__(self, compiler_spec, arch=None):
err_msg = "No compilers with spec {0} found".format(compiler_spec) err_msg = "No compilers with spec {0} found".format(compiler_spec)
if arch: if arch:
err_msg += " for operating system {0} and target {1}.".format( err_msg += " for operating system {0} and target {1}.".format(
arch.platform_os, arch.target arch.os, arch.target
) )
super(UnavailableCompilerVersionError, self).__init__( super(UnavailableCompilerVersionError, self).__init__(

View File

@ -384,7 +384,8 @@ def _assign_dependencies(self, hash_key, installs, data):
if not child: if not child:
msg = ("Missing dependency not in database: " msg = ("Missing dependency not in database: "
"%s needs %s-%s" % ( "%s needs %s-%s" % (
spec.cformat('$_$/'), dname, dhash[:7])) spec.cformat('{name}{/hash:7}'),
dname, dhash[:7]))
if self._fail_when_missing_deps: if self._fail_when_missing_deps:
raise MissingDependenciesError(msg) raise MissingDependenciesError(msg)
tty.warn(msg) tty.warn(msg)

View File

@ -175,15 +175,15 @@ def __init__(self, root, **kwargs):
super(YamlDirectoryLayout, self).__init__(root) super(YamlDirectoryLayout, self).__init__(root)
self.hash_len = kwargs.get('hash_len') self.hash_len = kwargs.get('hash_len')
self.path_scheme = kwargs.get('path_scheme') or ( self.path_scheme = kwargs.get('path_scheme') or (
"${ARCHITECTURE}/" "{architecture}/"
"${COMPILERNAME}-${COMPILERVER}/" "{compiler.name}-{compiler.version}/"
"${PACKAGE}-${VERSION}-${HASH}") "{name}-{version}-{hash}")
if self.hash_len is not None: if self.hash_len is not None:
if re.search(r'\${HASH:\d+}', self.path_scheme): if re.search(r'{hash:\d+}', self.path_scheme):
raise InvalidDirectoryLayoutParametersError( raise InvalidDirectoryLayoutParametersError(
"Conflicting options for installation layout hash length") "Conflicting options for installation layout hash length")
self.path_scheme = self.path_scheme.replace( self.path_scheme = self.path_scheme.replace(
"${HASH}", "${HASH:%d}" % self.hash_len) "{hash}", "{hash:%d}" % self.hash_len)
# If any of these paths change, downstream databases may not be able to # If any of these paths change, downstream databases may not be able to
# locate files in older upstream databases # locate files in older upstream databases

View File

@ -557,7 +557,8 @@ def print_status(self, *specs, **kwargs):
specs = index[(architecture, compiler)] specs = index[(architecture, compiler)]
specs.sort() specs.sort()
format_string = '$_$@$%@+$+' format_string = '{name}{@version}'
format_string += '{%compiler}{compiler_flags}{variants}'
abbreviated = [s.cformat(format_string) for s in specs] abbreviated = [s.cformat(format_string) for s in specs]
# Print one spec per line along with prefix path # Print one spec per line along with prefix path

View File

@ -210,7 +210,9 @@ def create(path, specs, **kwargs):
def add_single_spec(spec, mirror_root, categories, **kwargs): def add_single_spec(spec, mirror_root, categories, **kwargs):
tty.msg("Adding package {pkg} to mirror".format(pkg=spec.format("$_$@"))) tty.msg("Adding package {pkg} to mirror".format(
pkg=spec.format("{name}{@version}")
))
try: try:
spec.package.do_fetch() spec.package.do_fetch()
spec.package.do_clean() spec.package.do_clean()
@ -220,7 +222,8 @@ def add_single_spec(spec, mirror_root, categories, **kwargs):
sys.excepthook(*sys.exc_info()) sys.excepthook(*sys.exc_info())
else: else:
tty.warn( tty.warn(
"Error while fetching %s" % spec.cformat('$_$@'), e.message) "Error while fetching %s" % spec.cformat('{name}{@version}'),
e.message)
categories['error'].append(spec) categories['error'].append(spec)

View File

@ -59,12 +59,12 @@
#: Valid tokens for naming scheme and env variable names #: Valid tokens for naming scheme and env variable names
_valid_tokens = ( _valid_tokens = (
'PACKAGE', 'name',
'VERSION', 'version',
'COMPILER', 'compiler',
'COMPILERNAME', 'compiler.name',
'COMPILERVER', 'compiler.version',
'ARCHITECTURE' 'architecture'
) )
@ -80,8 +80,9 @@ def _check_tokens_are_valid(format_string, message):
tokens are found tokens are found
""" """
named_tokens = re.findall(r'\${(\w*)}', format_string) named_tokens = re.findall(r'{(\w*)}', format_string)
invalid_tokens = [x for x in named_tokens if x not in _valid_tokens] invalid_tokens = [x for x in named_tokens
if x.lower() not in _valid_tokens]
if invalid_tokens: if invalid_tokens:
msg = message msg = message
msg += ' [{0}]. '.format(', '.join(invalid_tokens)) msg += ' [{0}]. '.format(', '.join(invalid_tokens))
@ -294,7 +295,7 @@ def naming_scheme(self):
"""Naming scheme suitable for non-hierarchical layouts""" """Naming scheme suitable for non-hierarchical layouts"""
scheme = self.module.configuration.get( scheme = self.module.configuration.get(
'naming_scheme', 'naming_scheme',
'${PACKAGE}-${VERSION}-${COMPILERNAME}-${COMPILERVER}' '{name}-{version}-{compiler.name}-{compiler.version}'
) )
# Ensure the named tokens we are expanding are allowed, see # Ensure the named tokens we are expanding are allowed, see
@ -523,7 +524,7 @@ def short_description(self):
value = re.sub(r'"', "'", value) value = re.sub(r'"', "'", value)
return value return value
# Otherwise the short description is just the package + version # Otherwise the short description is just the package + version
return self.spec.format("$_ $@") return self.spec.format("{name} {@version}")
@tengine.context_property @tengine.context_property
def long_description(self): def long_description(self):

View File

@ -232,7 +232,7 @@ def use_name(self):
to console to use it. to console to use it.
""" """
# Package name and version # Package name and version
base = os.path.join("${PACKAGE}", "${VERSION}") base = os.path.join("{name}", "{version}")
name_parts = [self.spec.format(base)] name_parts = [self.spec.format(base)]
# The remaining elements are filename suffixes # The remaining elements are filename suffixes
name_parts.extend(self.conf.suffixes) name_parts.extend(self.conf.suffixes)

View File

@ -946,7 +946,7 @@ def do_fetch(self, mirror_only=False):
checksum = spack.config.get('config:checksum') checksum = spack.config.get('config:checksum')
if checksum and self.version not in self.versions: if checksum and self.version not in self.versions:
tty.warn("There is no checksum on file to fetch %s safely." % tty.warn("There is no checksum on file to fetch %s safely." %
self.spec.cformat('$_$@')) self.spec.cformat('{name}{@version}'))
# Ask the user whether to skip the checksum if we're # Ask the user whether to skip the checksum if we're
# interactive, but just fail if non-interactive. # interactive, but just fail if non-interactive.
@ -960,7 +960,7 @@ def do_fetch(self, mirror_only=False):
if not ignore_checksum: if not ignore_checksum:
raise FetchError("Will not fetch %s" % raise FetchError("Will not fetch %s" %
self.spec.format('$_$@'), ck_msg) self.spec.format('{name}{@version}'), ck_msg)
self.stage.create() self.stage.create()
self.stage.fetch(mirror_only) self.stage.fetch(mirror_only)

View File

@ -96,6 +96,7 @@
from llnl.util.tty.color import cwrite, colorize, cescape, get_color_when from llnl.util.tty.color import cwrite, colorize, cescape, get_color_when
import llnl.util.tty as tty import llnl.util.tty as tty
import spack.paths
import spack.architecture import spack.architecture
import spack.compiler import spack.compiler
import spack.compilers as compilers import spack.compilers as compilers
@ -187,6 +188,10 @@
#: Max integer helps avoid passing too large a value to cyaml. #: Max integer helps avoid passing too large a value to cyaml.
maxint = 2 ** (ctypes.sizeof(ctypes.c_int) * 8 - 1) - 1 maxint = 2 ** (ctypes.sizeof(ctypes.c_int) * 8 - 1) - 1
default_format = '{name}{@version}'
default_format += '{%compiler.name}{@compiler.version}{compiler_flags}'
default_format += '{variants}{arch=architecture}'
def colorize_spec(spec): def colorize_spec(spec):
"""Returns a spec colorized according to the colors specified in """Returns a spec colorized according to the colors specified in
@ -221,7 +226,7 @@ class ArchSpec(object):
def __init__(self, *args): def __init__(self, *args):
to_attr_string = lambda s: str(s) if s and s != "None" else None to_attr_string = lambda s: str(s) if s and s != "None" else None
self.platform, self.platform_os, self.target = (None, None, None) self.platform, self.os, self.target = (None, None, None)
if len(args) == 1: if len(args) == 1:
spec_like = args[0] spec_like = args[0]
@ -231,13 +236,13 @@ def __init__(self, *args):
spec_fields = spec_like.split("-") spec_fields = spec_like.split("-")
if len(spec_fields) == 3: if len(spec_fields) == 3:
self.platform, self.platform_os, self.target = tuple( self.platform, self.os, self.target = tuple(
to_attr_string(f) for f in spec_fields) to_attr_string(f) for f in spec_fields)
else: else:
raise ValueError("%s is an invalid arch spec" % spec_like) raise ValueError("%s is an invalid arch spec" % spec_like)
elif len(args) == 3: elif len(args) == 3:
self.platform = to_attr_string(args[0]) self.platform = to_attr_string(args[0])
self.platform_os = to_attr_string(args[1]) self.os = to_attr_string(args[1])
self.target = to_attr_string(args[2]) self.target = to_attr_string(args[2])
elif len(args) != 0: elif len(args) != 0:
raise TypeError("Can't make arch spec from %s" % args) raise TypeError("Can't make arch spec from %s" % args)
@ -248,11 +253,11 @@ def _autospec(self, spec_like):
return ArchSpec(spec_like) return ArchSpec(spec_like)
def _cmp_key(self): def _cmp_key(self):
return (self.platform, self.platform_os, self.target) return (self.platform, self.os, self.target)
def _dup(self, other): def _dup(self, other):
self.platform = other.platform self.platform = other.platform
self.platform_os = other.platform_os self.os = other.os
self.target = other.target self.target = other.target
@property @property
@ -269,11 +274,11 @@ def platform(self, value):
self._platform = value self._platform = value
@property @property
def platform_os(self): def os(self):
return self._platform_os return self._os
@platform_os.setter @os.setter
def platform_os(self, value): def os(self, value):
""" The OS of the architecture spec will update the platform field """ The OS of the architecture spec will update the platform field
if the OS is set to one of the reserved OS types so that the if the OS is set to one of the reserved OS types so that the
default OS type can be resolved. Since the reserved OS default OS type can be resolved. Since the reserved OS
@ -295,7 +300,7 @@ def platform_os(self, value):
spec_platform = spack.architecture.get_platform(self.platform) spec_platform = spack.architecture.get_platform(self.platform)
value = str(spec_platform.operating_system(value)) value = str(spec_platform.operating_system(value))
self._platform_os = value self._os = value
@property @property
def target(self): def target(self):
@ -369,13 +374,13 @@ def to_cmp_dict(self):
"""Returns a dictionary that can be used for field comparison.""" """Returns a dictionary that can be used for field comparison."""
return dict([ return dict([
('platform', self.platform), ('platform', self.platform),
('platform_os', self.platform_os), ('os', self.os),
('target', self.target)]) ('target', self.target)])
def to_dict(self): def to_dict(self):
d = syaml_dict([ d = syaml_dict([
('platform', self.platform), ('platform', self.platform),
('platform_os', self.platform_os), ('platform_os', self.os),
('target', self.target)]) ('target', self.target)])
return syaml_dict([('arch', d)]) return syaml_dict([('arch', d)])
@ -397,10 +402,13 @@ def from_dict(d):
return ArchSpec('spack09', 'unknown', d['arch']) return ArchSpec('spack09', 'unknown', d['arch'])
d = d['arch'] d = d['arch']
if 'platform_os' in d:
return ArchSpec(d['platform'], d['platform_os'], d['target']) return ArchSpec(d['platform'], d['platform_os'], d['target'])
else:
return ArchSpec(d['platform'], d['os'], d['target'])
def __str__(self): def __str__(self):
return "%s-%s-%s" % (self.platform, self.platform_os, self.target) return "%s-%s-%s" % (self.platform, self.os, self.target)
def __repr__(self): def __repr__(self):
return str(self) return str(self)
@ -835,10 +843,10 @@ def __get__(self, instance, cls):
# properties defined and no default handler, or that all callbacks # properties defined and no default handler, or that all callbacks
# raised AttributeError. In this case, we raise AttributeError with an # raised AttributeError. In this case, we raise AttributeError with an
# appropriate message. # appropriate message.
fmt = '\'{name}\' package has no relevant attribute \'{query}\'\n' # NOQA: ignore=E501 fmt = '\'{name}\' package has no relevant attribute \'{query}\'\n'
fmt += '\tspec : \'{spec}\'\n' fmt += '\tspec : \'{spec}\'\n'
fmt += '\tqueried as : \'{spec.last_query.name}\'\n' fmt += '\tqueried as : \'{spec.last_query.name}\'\n'
fmt += '\textra parameters : \'{spec.last_query.extra_parameters}\'\n' # NOQA: ignore=E501 fmt += '\textra parameters : \'{spec.last_query.extra_parameters}\'\n'
message = fmt.format( message = fmt.format(
name=pkg.name, name=pkg.name,
query=self.attribute_name, query=self.attribute_name,
@ -1004,11 +1012,11 @@ def _add_flag(self, name, value):
if name == 'arch' or name == 'architecture': if name == 'arch' or name == 'architecture':
parts = tuple(value.split('-')) parts = tuple(value.split('-'))
plat, os, tgt = parts if len(parts) == 3 else (None, None, value) plat, os, tgt = parts if len(parts) == 3 else (None, None, value)
self._set_architecture(platform=plat, platform_os=os, target=tgt) self._set_architecture(platform=plat, os=os, target=tgt)
elif name == 'platform': elif name == 'platform':
self._set_architecture(platform=value) self._set_architecture(platform=value)
elif name == 'os' or name == 'operating_system': elif name == 'os' or name == 'operating_system':
self._set_architecture(platform_os=value) self._set_architecture(os=value)
elif name == 'target': elif name == 'target':
self._set_architecture(target=value) self._set_architecture(target=value)
elif name in valid_flags: elif name in valid_flags:
@ -1026,7 +1034,7 @@ def _add_flag(self, name, value):
def _set_architecture(self, **kwargs): def _set_architecture(self, **kwargs):
"""Called by the parser to set the architecture.""" """Called by the parser to set the architecture."""
arch_attrs = ['platform', 'platform_os', 'target'] arch_attrs = ['platform', 'os', 'target']
if self.architecture and self.architecture.concrete: if self.architecture and self.architecture.concrete:
raise DuplicateArchitectureError( raise DuplicateArchitectureError(
"Spec for '%s' cannot have two architectures." % self.name) "Spec for '%s' cannot have two architectures." % self.name)
@ -1258,12 +1266,16 @@ def return_val(dspec):
def short_spec(self): def short_spec(self):
"""Returns a version of the spec with the dependencies hashed """Returns a version of the spec with the dependencies hashed
instead of completely enumerated.""" instead of completely enumerated."""
return self.format('$_$@$%@$+$=$/') spec_format = '{name}{@version}{%compiler}'
spec_format += '{variants}{arch=architecture}{/hash:7}'
return self.format(spec_format)
@property @property
def cshort_spec(self): def cshort_spec(self):
"""Returns an auto-colorized version of ``self.short_spec``.""" """Returns an auto-colorized version of ``self.short_spec``."""
return self.cformat('$_$@$%@$+$=$/') spec_format = '{name}{@version}{%compiler}'
spec_format += '{variants}{arch=architecture}{/hash:7}'
return self.cformat(spec_format)
@property @property
def prefix(self): def prefix(self):
@ -2434,8 +2446,8 @@ def constrain(self, other, deps=True):
if sarch.platform is not None and oarch.platform is not None: if sarch.platform is not None and oarch.platform is not None:
if sarch.platform != oarch.platform: if sarch.platform != oarch.platform:
raise UnsatisfiableArchitectureSpecError(sarch, oarch) raise UnsatisfiableArchitectureSpecError(sarch, oarch)
if sarch.platform_os is not None and oarch.platform_os is not None: if sarch.os is not None and oarch.os is not None:
if sarch.platform_os != oarch.platform_os: if sarch.os != oarch.os:
raise UnsatisfiableArchitectureSpecError(sarch, oarch) raise UnsatisfiableArchitectureSpecError(sarch, oarch)
if sarch.target is not None and oarch.target is not None: if sarch.target is not None and oarch.target is not None:
if sarch.target != oarch.target: if sarch.target != oarch.target:
@ -2460,8 +2472,8 @@ def constrain(self, other, deps=True):
else: else:
if sarch.platform is None or oarch.platform is None: if sarch.platform is None or oarch.platform is None:
self.architecture.platform = sarch.platform or oarch.platform self.architecture.platform = sarch.platform or oarch.platform
if sarch.platform_os is None or oarch.platform_os is None: if sarch.os is None or oarch.os is None:
sarch.platform_os = sarch.platform_os or oarch.platform_os sarch.os = sarch.os or oarch.os
if sarch.target is None or oarch.target is None: if sarch.target is None or oarch.target is None:
sarch.target = sarch.target or oarch.target sarch.target = sarch.target or oarch.target
changed |= (str(self.architecture) != old) changed |= (str(self.architecture) != old)
@ -3010,10 +3022,245 @@ def _cmp_key(self):
def colorized(self): def colorized(self):
return colorize_spec(self) return colorize_spec(self)
def format(self, format_string='$_$@$%@+$+$=', **kwargs): def format(self, format_string=default_format, **kwargs):
"""Prints out particular pieces of a spec, depending on what is r"""Prints out particular pieces of a spec, depending on what is
in the format string. in the format string.
Using the ``{attribute}`` syntax, any field of the spec can be
selected. Those attributes can be recursive. For example,
``s.format({compiler.version})`` will print the version of the
compiler.
Commonly used attributes of the Spec for format strings include::
name
version
compiler
compiler.name
compiler.version
compiler_flags
variants
architecture
architecture.platform
architecture.os
architecture.target
prefix
Some additional special-case properties can be added::
hash[:len] The DAG hash with optional length argument
spack_root The spack root directory
spack_install The spack install directory
The ``^`` sigil can be used to access dependencies by name.
``s.format({^mpi.name})`` will print the name of the MPI
implementation in the spec.
The ``@``, ``%``, ``arch=``, and ``/`` sigils
can be used to include the sigil with the printed
string. These sigils may only be used with the appropriate
attributes, listed below::
@ ``{@version}``, ``{@compiler.version}``
% ``{%compiler}``, ``{%compiler.name}``
arch= ``{arch=architecture}``
/ ``{/hash}``, ``{/hash:7}``, etc
The ``@`` sigil may also be used for any other property named
``version``. Sigils printed with the attribute string are only
printed if the attribute string is non-empty, and are colored
according to the color of the attribute.
Sigils are not used for printing variants. Variants listed by
name naturally print with their sigil. For example,
``spec.format('{variants.debug}')`` would print either
``+debug`` or ``~debug`` depending on the name of the
variant. Non-boolean variants print as ``name=value``. To
print variant names or values independently, use
``spec.format('{variants.<name>.name}')`` or
``spec.format('{variants.<name>.value}')``.
Spec format strings use ``\`` as the escape character. Use
``\{`` and ``\}`` for literal braces, and ``\\`` for the
literal ``\`` character. Also use ``\$`` for the literal ``$``
to differentiate from previous, deprecated format string
syntax.
The previous format strings are deprecated. They can still be
accessed by the ``old_format`` method. The ``format`` method
will call ``old_format`` if the character ``$`` appears
unescaped in the format string.
Args:
format_string (str): string containing the format to be expanded
Keyword Args:
color (bool): True if returned string is colored
transform (dict): maps full-string formats to a callable \
that accepts a string and returns another one
"""
# If we have an unescaped $ sigil, use the deprecated format strings
if re.search(r'[^\\]*\$', format_string):
return self.old_format(format_string, **kwargs)
color = kwargs.get('color', False)
transform = kwargs.get('transform', {})
out = StringIO()
def write(s, c=None):
f = cescape(s)
if c is not None:
f = color_formats[c] + f + '@.'
cwrite(f, stream=out, color=color)
def write_attribute(spec, attribute, color):
if attribute.startswith('^'):
attribute = attribute[1:]
dep, attribute = attribute.split('.', 1)
current = self[dep]
if attribute == '':
raise SpecFormatStringError(
'Format string attributes must be non-empty')
attribute = attribute.lower()
current = spec
sig = ''
if attribute[0] in '@%/':
# color sigils that are inside braces
sig = attribute[0]
attribute = attribute[1:]
elif attribute.startswith('arch='):
sig = ' arch=' # include space as separator
attribute = attribute[5:]
parts = attribute.split('.')
assert parts
# check that the sigil is valid for the attribute.
if sig == '@' and parts[-1] not in ('versions', 'version'):
raise SpecFormatSigilError(sig, 'versions', attribute)
elif sig == '%' and attribute not in ('compiler', 'compiler.name'):
raise SpecFormatSigilError(sig, 'compilers', attribute)
elif sig == '/' and not re.match(r'hash(:\d+)?$', attribute):
raise SpecFormatSigilError(sig, 'DAG hashes', attribute)
elif sig == ' arch=' and attribute not in ('architecture', 'arch'):
raise SpecFormatSigilError(sig, 'the architecture', attribute)
# find the morph function for our attribute
morph = transform.get(attribute, lambda s, x: x)
# Special cases for non-spec attributes and hashes.
# These must be the only non-dep component of the format attribute
if attribute == 'spack_root':
write(morph(spec, spack.paths.spack_root))
return
elif attribute == 'spack_install':
write(morph(spec, spack.store.layout.root))
return
elif re.match(r'hash(:\d)?', attribute):
col = '#'
if ':' in attribute:
_, length = attribute.split(':')
write(sig + morph(spec, spec.dag_hash(int(length))), col)
else:
write(sig + morph(spec, spec.dag_hash()), col)
return
# Iterate over components using getattr to get next element
for idx, part in enumerate(parts):
if not part:
raise SpecFormatStringError(
'Format string attributes must be non-empty'
)
if part.startswith('_'):
raise SpecFormatStringError(
'Attempted to format private attribute'
)
else:
if isinstance(current, VariantMap):
# subscript instead of getattr for variant names
current = current[part]
else:
# aliases
if part == 'arch':
part = 'architecture'
elif part == 'version':
# Version requires concrete spec, versions does not
# when concrete, they print the same thing
part = 'versions'
try:
current = getattr(current, part)
except AttributeError:
parent = '.'.join(parts[:idx])
m = 'Attempted to format attribute %s.' % attribute
m += 'Spec.%s has no attribute %s' % (parent, part)
raise SpecFormatStringError(m)
if isinstance(current, VersionList):
if current == _any_version:
# We don't print empty version lists
return
if callable(current):
raise SpecFormatStringError(
'Attempted to format callable object'
)
if not current:
# We're not printing anything
return
# Set color codes for various attributes
col = None
if 'variants' in parts:
col = '+'
elif 'architecture' in parts:
col = '='
elif 'compiler' in parts or 'compiler_flags' in parts:
col = '%'
elif 'version' in parts:
col = '@'
# Finally, write the ouptut
write(sig + morph(spec, str(current)), col)
attribute = ''
in_attribute = False
escape = False
for c in format_string:
if escape:
out.write(c)
escape = False
elif c == '\\':
escape = True
elif in_attribute:
if c == '}':
write_attribute(self, attribute, color)
attribute = ''
in_attribute = False
else:
attribute += c
else:
if c == '}':
raise SpecFormatStringError(
'Encountered closing } before opening {'
)
elif c == '{':
in_attribute = True
else:
out.write(c)
if in_attribute:
raise SpecFormatStringError(
'Format string terminated while reading attribute.'
'Missing terminating }.'
)
return out.getvalue()
def old_format(self, format_string='$_$@$%@+$+$=', **kwargs):
"""
The format strings you can provide are:: The format strings you can provide are::
$_ Package name $_ Package name
@ -3085,6 +3332,7 @@ def format(self, format_string='$_$@$%@+$+$=', **kwargs):
TODO: allow, e.g., ``$6#`` to customize short hash length TODO: allow, e.g., ``$6#`` to customize short hash length
TODO: allow, e.g., ``$//`` for full hash. TODO: allow, e.g., ``$//`` for full hash.
""" """
color = kwargs.get('color', False) color = kwargs.get('color', False)
# Dictionary of transformations for named tokens # Dictionary of transformations for named tokens
@ -3218,7 +3466,7 @@ def write(s, c=None):
platform = str(self.architecture.platform) platform = str(self.architecture.platform)
write(fmt % transform(self, platform), '=') write(fmt % transform(self, platform), '=')
elif named_str == "OS": elif named_str == "OS":
operating_sys = str(self.architecture.platform_os) operating_sys = str(self.architecture.os)
write(fmt % transform(self, operating_sys), '=') write(fmt % transform(self, operating_sys), '=')
elif named_str == "TARGET": elif named_str == "TARGET":
target = str(self.architecture.target) target = str(self.architecture.target)
@ -3302,7 +3550,7 @@ def tree(self, **kwargs):
status_fn = kwargs.pop('status_fn', False) status_fn = kwargs.pop('status_fn', False)
cover = kwargs.pop('cover', 'nodes') cover = kwargs.pop('cover', 'nodes')
indent = kwargs.pop('indent', 0) indent = kwargs.pop('indent', 0)
fmt = kwargs.pop('format', '$_$@$%@+$+$=') fmt = kwargs.pop('format', default_format)
prefix = kwargs.pop('prefix', None) prefix = kwargs.pop('prefix', None)
show_types = kwargs.pop('show_types', False) show_types = kwargs.pop('show_types', False)
deptypes = kwargs.pop('deptypes', 'all') deptypes = kwargs.pop('deptypes', 'all')
@ -3516,7 +3764,7 @@ def do_parse(self):
for spec in specs: for spec in specs:
for s in spec.traverse(): for s in spec.traverse():
if s.architecture and not s.architecture.platform and \ if s.architecture and not s.architecture.platform and \
(s.architecture.platform_os or s.architecture.target): (s.architecture.os or s.architecture.target):
s._set_architecture(platform=platform_default) s._set_architecture(platform=platform_default)
return specs return specs
@ -3877,7 +4125,9 @@ def __init__(self, provided, required):
class AmbiguousHashError(SpecError): class AmbiguousHashError(SpecError):
def __init__(self, msg, *specs): def __init__(self, msg, *specs):
specs_str = '\n ' + '\n '.join(spec.format('$.$@$%@+$+$=$/') spec_fmt = '{namespace}.{name}{@version}{%compiler}{compiler_flags}'
spec_fmt += '{variants}{arch=architecture}{/hash:7}'
specs_str = '\n ' + '\n '.join(spec.format(spec_fmt)
for spec in specs) for spec in specs)
super(AmbiguousHashError, self).__init__(msg + specs_str) super(AmbiguousHashError, self).__init__(msg + specs_str)
@ -3904,6 +4154,18 @@ def __init__(self, spec, addition):
% (addition, spec)) % (addition, spec))
class SpecFormatStringError(SpecError):
"""Called for errors in Spec format strings."""
class SpecFormatSigilError(SpecFormatStringError):
"""Called for mismatched sigils and attributes in format strings"""
def __init__(self, sigil, requirement, used):
msg = 'The sigil %s may only be used for %s.' % (sigil, requirement)
msg += ' It was used with the attribute %s.' % used
super(SpecFormatSigilError, self).__init__(msg)
class ConflictsInSpecError(SpecError, RuntimeError): class ConflictsInSpecError(SpecError, RuntimeError):
def __init__(self, spec, matches): def __init__(self, spec, matches):
message = 'Conflicts in concretized spec "{0}"\n'.format( message = 'Conflicts in concretized spec "{0}"\n'.format(

View File

@ -1,3 +1,4 @@
# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other # Copyright 2013-2019 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details. # Spack Project Developers. See the top-level COPYRIGHT file for details.
# #
@ -21,7 +22,7 @@
def test_dict_functions_for_architecture(): def test_dict_functions_for_architecture():
arch = spack.architecture.Arch() arch = spack.architecture.Arch()
arch.platform = spack.architecture.platform() arch.platform = spack.architecture.platform()
arch.platform_os = arch.platform.operating_system('default_os') arch.os = arch.platform.operating_system('default_os')
arch.target = arch.platform.target('default_target') arch.target = arch.platform.target('default_target')
new_arch = spack.architecture.Arch.from_dict(arch.to_dict()) new_arch = spack.architecture.Arch.from_dict(arch.to_dict())
@ -29,11 +30,11 @@ def test_dict_functions_for_architecture():
assert arch == new_arch assert arch == new_arch
assert isinstance(arch, spack.architecture.Arch) assert isinstance(arch, spack.architecture.Arch)
assert isinstance(arch.platform, spack.architecture.Platform) assert isinstance(arch.platform, spack.architecture.Platform)
assert isinstance(arch.platform_os, spack.architecture.OperatingSystem) assert isinstance(arch.os, spack.architecture.OperatingSystem)
assert isinstance(arch.target, spack.architecture.Target) assert isinstance(arch.target, spack.architecture.Target)
assert isinstance(new_arch, spack.architecture.Arch) assert isinstance(new_arch, spack.architecture.Arch)
assert isinstance(new_arch.platform, spack.architecture.Platform) assert isinstance(new_arch.platform, spack.architecture.Platform)
assert isinstance(new_arch.platform_os, spack.architecture.OperatingSystem) assert isinstance(new_arch.os, spack.architecture.OperatingSystem)
assert isinstance(new_arch.target, spack.architecture.Target) assert isinstance(new_arch.target, spack.architecture.Target)
@ -67,7 +68,7 @@ def test_boolness():
assert arch assert arch
arch = spack.architecture.Arch() arch = spack.architecture.Arch()
arch.platform_os = plat_os arch.os = plat_os
assert arch assert arch
arch = spack.architecture.Arch() arch = spack.architecture.Arch()
@ -86,7 +87,7 @@ def test_user_front_end_input(config):
frontend_spec = Spec('libelf os=frontend target=frontend') frontend_spec = Spec('libelf os=frontend target=frontend')
frontend_spec.concretize() frontend_spec.concretize()
assert frontend_os == frontend_spec.architecture.platform_os assert frontend_os == frontend_spec.architecture.os
assert frontend_target == frontend_spec.architecture.target assert frontend_target == frontend_spec.architecture.target
@ -101,7 +102,7 @@ def test_user_back_end_input(config):
backend_spec = Spec("libelf os=backend target=backend") backend_spec = Spec("libelf os=backend target=backend")
backend_spec.concretize() backend_spec.concretize()
assert backend_os == backend_spec.architecture.platform_os assert backend_os == backend_spec.architecture.os
assert backend_target == backend_spec.architecture.target assert backend_target == backend_spec.architecture.target
@ -113,7 +114,7 @@ def test_user_defaults(config):
default_spec = Spec("libelf") # default is no args default_spec = Spec("libelf") # default is no args
default_spec.concretize() default_spec.concretize()
assert default_os == default_spec.architecture.platform_os assert default_os == default_spec.architecture.os
assert default_target == default_spec.architecture.target assert default_target == default_spec.architecture.target
@ -133,7 +134,7 @@ def test_user_input_combination(config):
spec = Spec("libelf os=%s target=%s" % (o, t)) spec = Spec("libelf os=%s target=%s" % (o, t))
spec.concretize() spec.concretize()
results.append( results.append(
spec.architecture.platform_os == str(platform.operating_system(o)) spec.architecture.os == str(platform.operating_system(o))
) )
results.append( results.append(
spec.architecture.target == str(platform.target(t)) spec.architecture.target == str(platform.target(t))

View File

@ -45,7 +45,7 @@ def test_view_projections(
viewpath = str(tmpdir.mkdir('view_{0}'.format(cmd))) viewpath = str(tmpdir.mkdir('view_{0}'.format(cmd)))
view_projection = { view_projection = {
'projections': { 'projections': {
'all': '${PACKAGE}-${VERSION}' 'all': '{name}-{version}'
} }
} }
projection_file = create_projection_file(tmpdir, view_projection) projection_file = create_projection_file(tmpdir, view_projection)
@ -65,8 +65,8 @@ def test_view_multiple_projections(
viewpath = str(tmpdir.mkdir('view')) viewpath = str(tmpdir.mkdir('view'))
view_projection = s_yaml.syaml_dict( view_projection = s_yaml.syaml_dict(
[('extendee', '${PACKAGE}-${COMPILERNAME}'), [('extendee', '{name}-{compiler.name}'),
('all', '${PACKAGE}-${VERSION}')] ('all', '{name}-{version}')]
) )
projection_file = create_projection_file(tmpdir, view_projection) projection_file = create_projection_file(tmpdir, view_projection)
@ -87,8 +87,8 @@ def test_view_multiple_projections_all_first(
viewpath = str(tmpdir.mkdir('view')) viewpath = str(tmpdir.mkdir('view'))
view_projection = s_yaml.syaml_dict( view_projection = s_yaml.syaml_dict(
[('all', '${PACKAGE}-${VERSION}'), [('all', '{name}-{version}'),
('extendee', '${PACKAGE}-${COMPILERNAME}')] ('extendee', '{name}-{compiler.name}')]
) )
projection_file = create_projection_file(tmpdir, view_projection) projection_file = create_projection_file(tmpdir, view_projection)
@ -145,7 +145,7 @@ def test_view_extension_projection(
install('extension2@1.0') install('extension2@1.0')
viewpath = str(tmpdir.mkdir('view')) viewpath = str(tmpdir.mkdir('view'))
view_projection = {'all': '${PACKAGE}-${VERSION}'} view_projection = {'all': '{name}-{version}'}
projection_file = create_projection_file(tmpdir, view_projection) projection_file = create_projection_file(tmpdir, view_projection)
view('symlink', viewpath, '--projection-file={0}'.format(projection_file), view('symlink', viewpath, '--projection-file={0}'.format(projection_file),
'extension1@1.0') 'extension1@1.0')

View File

@ -13,7 +13,7 @@ lmod:
- CMAKE_PREFIX_PATH - CMAKE_PREFIX_PATH
environment: environment:
set: set:
'${PACKAGE}_ROOT': '${PREFIX}' '{name}_ROOT': '{prefix}'
'platform=test target=x86_64': 'platform=test target=x86_64':
environment: environment:

View File

@ -7,7 +7,7 @@ tcl:
- CMAKE_PREFIX_PATH - CMAKE_PREFIX_PATH
environment: environment:
set: set:
'${PACKAGE}_ROOT': '${PREFIX}' '{name}_ROOT': '{prefix}'
'platform=test target=x86_64': 'platform=test target=x86_64':
environment: environment:

View File

@ -1,8 +1,8 @@
enable: enable:
- tcl - tcl
tcl: tcl:
naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}' naming_scheme: '{name}/{version}-{compiler.name}'
all: all:
conflict: conflict:
- '${PACKAGE}' - '{name}'
- 'intel/14.0.1' - 'intel/14.0.1'

View File

@ -1,5 +1,5 @@
enable: enable:
- tcl - tcl
tcl: tcl:
# ${OPTIONS} is not allowed in the naming scheme, see #2884 # {variants} is not allowed in the naming scheme, see #2884
naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}-${OPTIONS}' naming_scheme: '{name}/{version}-{compiler.name}-{variants}'

View File

@ -7,12 +7,12 @@ tcl:
- CMAKE_PREFIX_PATH - CMAKE_PREFIX_PATH
environment: environment:
set: set:
'${PACKAGE}_ROOT_${PREFIX}': '${PREFIX}' '{name}_ROOT_{prefix}': '{prefix}'
'platform=test target=x86_64': 'platform=test target=x86_64':
environment: environment:
set: set:
FOO_${OPTIONS}: 'foo' FOO_{variants}: 'foo'
unset: unset:
- BAR - BAR

View File

@ -1,7 +1,7 @@
enable: enable:
- tcl - tcl
tcl: tcl:
naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}' naming_scheme: '{name}/{version}-{compiler.name}'
all: all:
conflict: conflict:
- '${PACKAGE}/${COMPILERNAME}' - '{name}/{compiler.name}'

View File

@ -41,9 +41,9 @@ def test_yaml_directory_layout_parameters(
layout_default = YamlDirectoryLayout(str(tmpdir)) layout_default = YamlDirectoryLayout(str(tmpdir))
path_default = layout_default.relative_path_for_spec(spec) path_default = layout_default.relative_path_for_spec(spec)
assert(path_default == spec.format( assert(path_default == spec.format(
"${ARCHITECTURE}/" "{architecture}/"
"${COMPILERNAME}-${COMPILERVER}/" "{compiler.name}-{compiler.version}/"
"${PACKAGE}-${VERSION}-${HASH}")) "{name}-{version}-{hash}"))
# Test hash_length parameter works correctly # Test hash_length parameter works correctly
layout_10 = YamlDirectoryLayout(str(tmpdir), hash_len=10) layout_10 = YamlDirectoryLayout(str(tmpdir), hash_len=10)
@ -56,7 +56,7 @@ def test_yaml_directory_layout_parameters(
# Test path_scheme # Test path_scheme
arch, compiler, package7 = path_7.split('/') arch, compiler, package7 = path_7.split('/')
scheme_package7 = "${PACKAGE}-${VERSION}-${HASH:7}" scheme_package7 = "{name}-{version}-{hash:7}"
layout_package7 = YamlDirectoryLayout(str(tmpdir), layout_package7 = YamlDirectoryLayout(str(tmpdir),
path_scheme=scheme_package7) path_scheme=scheme_package7)
path_package7 = layout_package7.relative_path_for_spec(spec) path_package7 = layout_package7.relative_path_for_spec(spec)
@ -64,7 +64,7 @@ def test_yaml_directory_layout_parameters(
assert(package7 == path_package7) assert(package7 == path_package7)
# Test separation of architecture # Test separation of architecture
arch_scheme_package = "${PLATFORM}/${TARGET}/${OS}/${PACKAGE}/${VERSION}/${HASH:7}" # NOQA: ignore=E501 arch_scheme_package = "{architecture.platform}/{architecture.target}/{architecture.os}/{name}/{version}/{hash:7}" # NOQA: ignore=E501
layout_arch_package = YamlDirectoryLayout(str(tmpdir), layout_arch_package = YamlDirectoryLayout(str(tmpdir),
path_scheme=arch_scheme_package) path_scheme=arch_scheme_package)
arch_path_package = layout_arch_package.relative_path_for_spec(spec) arch_path_package = layout_arch_package.relative_path_for_spec(spec)

View File

@ -65,16 +65,16 @@ def test_dynamic_dot_graph_mpileaks(mock_packages):
dot = stream.getvalue() dot = stream.getvalue()
mpileaks_hash, mpileaks_lbl = s.dag_hash(), s.format('$_$/') mpileaks_hash, mpileaks_lbl = s.dag_hash(), s.format('{name}{/hash:7}')
mpi_hash, mpi_lbl = s['mpi'].dag_hash(), s['mpi'].format('$_$/') mpi_hash, mpi_lbl = s['mpi'].dag_hash(), s['mpi'].format('{name}{/hash:7}')
callpath_hash, callpath_lbl = ( callpath_hash, callpath_lbl = (
s['callpath'].dag_hash(), s['callpath'].format('$_$/')) s['callpath'].dag_hash(), s['callpath'].format('{name}{/hash:7}'))
dyninst_hash, dyninst_lbl = ( dyninst_hash, dyninst_lbl = (
s['dyninst'].dag_hash(), s['dyninst'].format('$_$/')) s['dyninst'].dag_hash(), s['dyninst'].format('{name}{/hash:7}'))
libdwarf_hash, libdwarf_lbl = ( libdwarf_hash, libdwarf_lbl = (
s['libdwarf'].dag_hash(), s['libdwarf'].format('$_$/')) s['libdwarf'].dag_hash(), s['libdwarf'].format('{name}{/hash:7}'))
libelf_hash, libelf_lbl = ( libelf_hash, libelf_lbl = (
s['libelf'].dag_hash(), s['libelf'].format('$_$/')) s['libelf'].dag_hash(), s['libelf'].format('{name}{/hash:7}'))
assert ' "%s" [label="%s"]\n' % (mpileaks_hash, mpileaks_lbl) in dot assert ' "%s" [label="%s"]\n' % (mpileaks_hash, mpileaks_lbl) in dot
assert ' "%s" [label="%s"]\n' % (callpath_hash, callpath_lbl) in dot assert ' "%s" [label="%s"]\n' % (callpath_hash, callpath_lbl) in dot

View File

@ -151,7 +151,7 @@ def test_naming_scheme(self, factory, module_configuration):
# Test we read the expected configuration for the naming scheme # Test we read the expected configuration for the naming scheme
writer, _ = factory('mpileaks') writer, _ = factory('mpileaks')
expected = '${PACKAGE}/${VERSION}-${COMPILERNAME}' expected = '{name}/{version}-{compiler.name}'
assert writer.conf.naming_scheme == expected assert writer.conf.naming_scheme == expected

View File

@ -8,6 +8,7 @@
from spack.spec import Spec, UnsatisfiableSpecError, SpecError from spack.spec import Spec, UnsatisfiableSpecError, SpecError
from spack.spec import substitute_abstract_variants, parse_anonymous_spec from spack.spec import substitute_abstract_variants, parse_anonymous_spec
from spack.spec import SpecFormatSigilError, SpecFormatStringError
from spack.variant import InvalidVariantValueError from spack.variant import InvalidVariantValueError
from spack.variant import MultipleValuesInExclusiveVariantError from spack.variant import MultipleValuesInExclusiveVariantError
@ -736,28 +737,133 @@ def test_exceptional_paths_for_constructor(self):
Spec('libelf foo') Spec('libelf foo')
def test_spec_formatting(self): def test_spec_formatting(self):
spec = Spec("multivalue_variant cflags=-O2")
spec.concretize()
# Since the default is the full spec see if the string rep of
# spec is the same as the output of spec.format()
# ignoring whitespace (though should we?) and ignoring dependencies
spec_string = str(spec)
idx = spec_string.index(' ^')
assert spec_string[:idx] == spec.format().strip()
# Testing named strings ie {string} and whether we get
# the correct component
# Mixed case intentional to test both
package_segments = [("{NAME}", "name"),
("{VERSION}", "versions"),
("{compiler}", "compiler"),
("{compiler_flags}", "compiler_flags"),
("{variants}", "variants"),
("{architecture}", "architecture")]
sigil_package_segments = [("{@VERSIONS}", '@' + str(spec.version)),
("{%compiler}", '%' + str(spec.compiler)),
("{arch=architecture}",
' arch=' + str(spec.architecture))]
compiler_segments = [("{compiler.name}", "name"),
("{compiler.version}", "versions")]
sigil_compiler_segments = [("{%compiler.name}",
'%' + spec.compiler.name),
("{@compiler.version}",
'@' + str(spec.compiler.version))]
architecture_segments = [("{architecture.platform}", "platform"),
("{architecture.os}", "os"),
("{architecture.target}", "target")]
other_segments = [('{spack_root}', spack.paths.spack_root),
('{spack_install}', spack.store.layout.root),
('{hash:7}', spec.dag_hash(7)),
('{/hash}', '/' + spec.dag_hash())]
for named_str, prop in package_segments:
expected = getattr(spec, prop, "")
actual = spec.format(named_str)
assert str(expected) == actual
for named_str, expected in sigil_package_segments:
actual = spec.format(named_str)
assert expected == actual
compiler = spec.compiler
for named_str, prop in compiler_segments:
expected = getattr(compiler, prop, "")
actual = spec.format(named_str)
assert str(expected) == actual
for named_str, expected in sigil_compiler_segments:
actual = spec.format(named_str)
assert expected == actual
arch = spec.architecture
for named_str, prop in architecture_segments:
expected = getattr(arch, prop, "")
actual = spec.format(named_str)
assert str(expected) == actual
for named_str, expected in other_segments:
actual = spec.format(named_str)
assert expected == actual
def test_spec_formatting_escapes(self):
spec = Spec('multivalue_variant cflags=-O2')
spec.concretize()
sigil_mismatches = [
'{@name}',
'{@version.concrete}',
'{%compiler.version}',
'{/hashd}',
'{arch=architecture.os}'
]
for fmt_str in sigil_mismatches:
with pytest.raises(SpecFormatSigilError):
spec.format(fmt_str)
bad_formats = [
'{}',
'name}',
'\{name}', # NOQA: ignore=W605
'{name',
'{name\}', # NOQA: ignore=W605
'{_concrete}',
'{dag_hash}',
'{foo}',
'{+variants.debug}'
]
for fmt_str in bad_formats:
with pytest.raises(SpecFormatStringError):
spec.format(fmt_str)
def test_spec_deprecated_formatting(self):
spec = Spec("libelf cflags=-O2") spec = Spec("libelf cflags=-O2")
spec.concretize() spec.concretize()
# Since the default is the full spec see if the string rep of # Since the default is the full spec see if the string rep of
# spec is the same as the output of spec.format() # spec is the same as the output of spec.format()
# ignoring whitespace (though should we?) # ignoring whitespace (though should we?)
assert str(spec) == spec.format().strip() assert str(spec) == spec.format('$_$@$%@+$+$=').strip()
# Testing named strings ie ${STRING} and whether we get # Testing named strings ie {string} and whether we get
# the correct component # the correct component
# Mixed case intentional for testing both
package_segments = [("${PACKAGE}", "name"), package_segments = [("${PACKAGE}", "name"),
("${VERSION}", "versions"), ("${VERSION}", "versions"),
("${COMPILER}", "compiler"), ("${compiler}", "compiler"),
("${COMPILERFLAGS}", "compiler_flags"), ("${compilerflags}", "compiler_flags"),
("${OPTIONS}", "variants"), ("${options}", "variants"),
("${ARCHITECTURE}", "architecture")] ("${architecture}", "architecture")]
compiler_segments = [("${COMPILERNAME}", "name"), compiler_segments = [("${compilername}", "name"),
("${COMPILERVER}", "versions")] ("${compilerver}", "versions")]
architecture_segments = [("${PLATFORM}", "platform"), architecture_segments = [("${PLATFORM}", "platform"),
("${OS}", "platform_os"), ("${OS}", "os"),
("${TARGET}", "target")] ("${TARGET}", "target")]
for named_str, prop in package_segments: for named_str, prop in package_segments:

View File

@ -203,8 +203,8 @@ def test_canonicalize(self):
"x ^y~f+e~d+c~b+a@4,2:3,1%intel@4,3,2,1") "x ^y~f+e~d+c~b+a@4,2:3,1%intel@4,3,2,1")
self.check_parse( self.check_parse(
"x arch=test-redhat6-None " "x arch=test-redhat6-None"
" ^y arch=test-None-x86_64 " " ^y arch=test-None-x86_64"
" ^z arch=linux-None-None", " ^z arch=linux-None-None",
"x os=fe " "x os=fe "
@ -212,11 +212,11 @@ def test_canonicalize(self):
"^z platform=linux") "^z platform=linux")
self.check_parse( self.check_parse(
"x arch=test-debian6-x86_64 " "x arch=test-debian6-x86_64"
" ^y arch=test-debian6-x86_64", " ^y arch=test-debian6-x86_64",
"x os=default_os target=default_target " "x os=default_os target=default_target"
"^y os=default_os target=default_target") " ^y os=default_os target=default_target")
self.check_parse("x ^y", "x@: ^y@:") self.check_parse("x ^y", "x@: ^y@:")

View File

@ -397,7 +397,7 @@ def write_rpath_specs(self):
the compiler used to build the executable.""" the compiler used to build the executable."""
if not self.spec_dir: if not self.spec_dir:
tty.warn('Could not install specs for {0}.'.format( tty.warn('Could not install specs for {0}.'.format(
self.spec.format('$_$@'))) self.spec.format('{name}{@version}')))
return return
gcc = self.spec['gcc'].command gcc = self.spec['gcc'].command

View File

@ -67,7 +67,9 @@ def patch(self):
add_extra_files(self, self.common, self.assets) add_extra_files(self, self.common, self.assets)
# Emit openfoam version immediately, if we resolved the wrong version # Emit openfoam version immediately, if we resolved the wrong version
# it takes a very long time to rebuild! # it takes a very long time to rebuild!
tty.info('Build for ' + self.spec['openfoam'].format('$_$@$%@+$+')) tty.info('Build for ' + self.spec['openfoam'].format(
'{name}{@version}{%compiler}{compiler_flags}{variants}'
))
def configure(self, spec, prefix): def configure(self, spec, prefix):
"""Generate spack-config.sh file.""" """Generate spack-config.sh file."""

View File

@ -42,7 +42,9 @@ def patch(self):
add_extra_files(self, self.common, self.assets) add_extra_files(self, self.common, self.assets)
# Emit openfoam version immediately, if we resolved the wrong version # Emit openfoam version immediately, if we resolved the wrong version
# it takes a very long time to rebuild! # it takes a very long time to rebuild!
tty.info('Build for ' + self.spec['openfoam'].format('$_$@$%@+$+')) tty.info('Build for ' + self.spec['openfoam'].format(
'{name}{@version}{%compiler}{compiler_flags}{variants}'
))
def configure(self, spec, prefix): def configure(self, spec, prefix):
"""Generate spack-config.sh file.""" """Generate spack-config.sh file."""