diff --git a/lib/spack/docs/config_yaml.rst b/lib/spack/docs/config_yaml.rst index 03e9060d457f2aee4894828cc71eaf36d53232e2..ad2ff736afe0f7aa160428e3f3650f979b800065 100644 --- a/lib/spack/docs/config_yaml.rst +++ b/lib/spack/docs/config_yaml.rst @@ -39,7 +39,7 @@ default path uses the full 32 characters. Secondly, it is also possible to modify the entire installation scheme. By default 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 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 @@ -48,7 +48,7 @@ the installation paths. For example .. code-block:: yaml 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 name, version and a hash length of 7 characters. diff --git a/lib/spack/docs/configuration.rst b/lib/spack/docs/configuration.rst index 6b1621d39123ca75bdb143d9855737feebaffe67..4ce08d067e3f48c1c25b058d6bf64d1d42e78fd8 100644 --- a/lib/spack/docs/configuration.rst +++ b/lib/spack/docs/configuration.rst @@ -459,7 +459,7 @@ account all scopes. For example, to see the fully merged install_tree: $spack/opt/spack template_dirs: - $spack/templates - directory_layout: ${ARCHITECTURE}/${COMPILERNAME}-${COMPILERVER}/${PACKAGE}-${VERSION}-${HASH} + directory_layout: {architecture}/{compiler.name}-{compiler.version}/{name}-{version}-{hash} module_roots: tcl: $spack/share/spack/modules lmod: $spack/share/spack/lmod @@ -510,7 +510,7 @@ down the problem: ./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: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:33 tcl: $spack/share/spack/modules /home/myuser/spack/etc/spack/defaults/config.yaml:34 lmod: $spack/share/spack/lmod diff --git a/lib/spack/docs/module_file_support.rst b/lib/spack/docs/module_file_support.rst index 31fe90279017e1a633666504b4a7cb9144b42a5d..2335fc73bbaf8335134584ed8992c7afe1cb79eb 100644 --- a/lib/spack/docs/module_file_support.rst +++ b/lib/spack/docs/module_file_support.rst @@ -535,10 +535,10 @@ most likely via the ``+blas`` variant specification. modules: tcl: - naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}-${COMPILERVER}' + naming_scheme: '{name}/{version}-{compiler.name}-{compiler.version}' all: conflict: - - '${PACKAGE}' + - '{name}' - 'intel/14.0.1' will create module files that will conflict with ``intel/14.0.1`` and with the diff --git a/lib/spack/docs/tutorial_modules.rst b/lib/spack/docs/tutorial_modules.rst index 3ae1a040699a15bc8a51f8fd3965d826874fe403..8487a03759abfee0898f16963c33ec2fc534fe7e 100644 --- a/lib/spack/docs/tutorial_modules.rst +++ b/lib/spack/docs/tutorial_modules.rst @@ -646,14 +646,14 @@ modules that refer to different flavors of the same library/application: modules: tcl: hash_length: 0 - naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}-${COMPILERVER}' + naming_scheme: '{name}/{version}-{compiler.name}-{compiler.version}' whitelist: - gcc blacklist: - '%gcc@5.4.0' all: conflict: - - '${PACKAGE}' + - '{name}' suffixes: '^openblas': openblas '^netlib-lapack': netlib @@ -713,14 +713,14 @@ is installed. You can achieve this with Spack by adding an modules: tcl: hash_length: 0 - naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}-${COMPILERVER}' + naming_scheme: '{name}/{version}-{compiler.name}-{compiler.version}' whitelist: - gcc blacklist: - '%gcc@5.4.0' all: conflict: - - '${PACKAGE}' + - '{name}' suffixes: '^openblas': openblas '^netlib-lapack': netlib @@ -728,7 +728,7 @@ is installed. You can achieve this with Spack by adding an environment_blacklist: ['CPATH', 'LIBRARY_PATH'] environment: set: - '${PACKAGE}_ROOT': '${PREFIX}' + '{name}_ROOT': '{prefix}' netlib-scalapack: suffixes: '^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 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``, - ``VERSION``, ``COMPILER``, ``COMPILERNAME``, ``COMPILERVER``, ``ARCHITECTURE`` +- The set of allowed tokens in variable names is restricted to + ``name``, ``version``, ``compiler``, ``compiler.name``, + ``compiler.version``, ``architecture`` - Any token expanded in a variable name is made uppercase, but other than that case sensitivity is preserved @@ -784,14 +785,14 @@ etc. in the ``gcc`` module file and apply other custom modifications to the modules: tcl: hash_length: 0 - naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}-${COMPILERVER}' + naming_scheme: '{name}/{version}-{compiler.name}-{compiler.version}' whitelist: - gcc blacklist: - '%gcc@5.4.0' all: conflict: - - '${PACKAGE}' + - '{name}' suffixes: '^openblas': openblas '^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: set: - '${PACKAGE}_ROOT': '${PREFIX}' + '{name}_ROOT': '{prefix}' gcc: environment: set: @@ -896,14 +897,14 @@ directive and assigning it the value ``direct``: tcl: verbose: True hash_length: 0 - naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}-${COMPILERVER}' + naming_scheme: '{name}/{version}-{compiler.name}-{compiler.version}' whitelist: - gcc blacklist: - '%gcc@5.4.0' all: conflict: - - '${PACKAGE}' + - '{name}' suffixes: '^openblas': openblas '^netlib-lapack': netlib @@ -911,7 +912,7 @@ directive and assigning it the value ``direct``: environment_blacklist: ['CPATH', 'LIBRARY_PATH'] environment: set: - '${PACKAGE}_ROOT': '${PREFIX}' + '{name}_ROOT': '{prefix}' gcc: environment: set: @@ -1089,7 +1090,7 @@ After these modifications your configuration file should look like: environment_blacklist: ['CPATH', 'LIBRARY_PATH'] environment: set: - '${PACKAGE}_ROOT': '${PREFIX}' + '{name}_ROOT': '{prefix}' gcc: environment: 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: set: - '${PACKAGE}_ROOT': '${PREFIX}' + '{name}_ROOT': '{prefix}' gcc: environment: set: @@ -1534,7 +1535,7 @@ it's ``netlib-scalapack``: environment_blacklist: ['CPATH', 'LIBRARY_PATH'] environment: set: - '${PACKAGE}_ROOT': '${PREFIX}' + '{name}_ROOT': '{prefix}' gcc: environment: set: diff --git a/lib/spack/docs/workflows.rst b/lib/spack/docs/workflows.rst index 371fce35a26a647f5c78da7d1a52df32066b1570..454cb15ecc6a95af54a0847ab06b0f6f0cc89d2d 100644 --- a/lib/spack/docs/workflows.rst +++ b/lib/spack/docs/workflows.rst @@ -541,9 +541,9 @@ spec format strings, as shown in the example below. .. code-block:: yaml projections: - zlib: ${PACKAGE}-${VERSION} - ^mpi: ${PACKAGE}-${VERSION}/${DEP:mpi:PACKAGE}-${DEP:mpi:VERSION}-${COMPILERNAME}-${COMPILERVER} - all: ${PACKAGE}-${VERSION}/${COMPILERNAME}-${COMPILERVER} + zlib: {name}-{version} + ^mpi: {name}-{version}/{^mpi.name}-{^mpi.version}-{compiler.name}-{compiler.version} + all: {name}-{version}/{compiler.name}-{compiler.version} The entries in the projections configuration file must all be either specs or the keyword ``all``. For each spec, the projection used will diff --git a/lib/spack/spack/architecture.py b/lib/spack/spack/architecture.py index b37ae18266f181bde2da9ea4225abc52fa424833..eefe5287ae2651f4360b8b52ea1e682df42ee44b 100644 --- a/lib/spack/spack/architecture.py +++ b/lib/spack/spack/architecture.py @@ -336,7 +336,7 @@ def __init__(self, plat=None, os=None, target=None): self.platform = plat if plat and os: os = self.platform.operating_system(os) - self.platform_os = os + self.os = os if plat and target: target = self.platform.target(target) self.target = target @@ -349,16 +349,16 @@ def __init__(self, plat=None, os=None, target=None): def concrete(self): return all((self.platform is not None, isinstance(self.platform, Platform), - self.platform_os is not None, - isinstance(self.platform_os, OperatingSystem), + self.os is not None, + isinstance(self.os, OperatingSystem), self.target is not None, isinstance(self.target, Target))) 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': - os_name = self.platform_os.name if self.platform_os else "None" + os_name = self.os.name if self.os else "None" else: - os_name = str(self.platform_os) + os_name = str(self.os) return (str(self.platform) + "-" + 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. def __nonzero__(self): 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) __bool__ = __nonzero__ @@ -380,21 +380,21 @@ def _cmp_key(self): platform = self.platform.name else: platform = self.platform - if isinstance(self.platform_os, OperatingSystem): - platform_os = self.platform_os.name + if isinstance(self.os, OperatingSystem): + os = self.os.name else: - platform_os = self.platform_os + os = self.os if isinstance(self.target, Target): target = self.target.name else: target = self.target - return (platform, platform_os, target) + return (platform, os, target) def to_dict(self): str_or_none = lambda v: str(v) if v else None d = syaml_dict([ ('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))]) return syaml_dict([('arch', d)]) @@ -430,13 +430,13 @@ def arch_for_spec(arch_spec): assert(arch_spec.concrete) 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)): raise ValueError( "Can't recreate arch for spec %s on current arch %s; " "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 diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py index 614188a281fce6c63d33ad16d22d5d3837ddbc77..3d826937ba075ba23af430f734595575a2da89f8 100644 --- a/lib/spack/spack/build_environment.py +++ b/lib/spack/spack/build_environment.py @@ -381,7 +381,7 @@ def set_build_environment_variables(pkg, env, dirty): if spack.config.get('config:debug'): env.set(SPACK_DEBUG, 'TRUE') 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) # Find ccache binary and hand it to build environment diff --git a/lib/spack/spack/cmd/__init__.py b/lib/spack/spack/cmd/__init__.py index 9d003eb656c2cdab47306245f9d9c8411af639d7..d1efb99e62e670ff900572a3f1c0561ca9104028 100644 --- a/lib/spack/spack/cmd/__init__.py +++ b/lib/spack/spack/cmd/__init__.py @@ -184,10 +184,11 @@ def disambiguate_spec(spec, env): tty.die("Spec '%s' matches no installed packages." % spec) elif len(matching_specs) > 1: + format_string = '{name}{@version}{%compiler}{arch=architecture}' args = ["%s matches multiple packages." % spec, "Matching packages:"] 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."] tty.die(*args) @@ -263,15 +264,15 @@ def get_arg(name, default=None): hashes = True hlen = None - nfmt = '{fullpackage}' if namespace else '{package}' + nfmt = '{namespace}{name}' if namespace else '{name}' ffmt = '' if full_compiler or flags: - ffmt += '$%' + ffmt += '{%compiler.name}' if full_compiler: - ffmt += '@' - ffmt += '+' - vfmt = '$+' if variants else '' - format_string = '$%s$@%s%s' % (nfmt, ffmt, vfmt) + ffmt += '{@compiler.version}' + ffmt += ' {compiler_flags}' + vfmt = '{variants}' if variants else '' + format_string = nfmt + '{@version}' + ffmt + vfmt # Make a dict with specs keyed by architecture and compiler. index = index_by(specs, ('architecture', 'compiler')) @@ -329,7 +330,7 @@ def fmt(s): if hashes: string += gray_hash(s, hlen) + ' ' string += s.cformat( - '$%s$@%s' % (nfmt, vfmt), transform=transform) + nfmt + '{@version}' + vfmt, transform=transform) return string if not flags and not full_compiler: diff --git a/lib/spack/spack/cmd/arch.py b/lib/spack/spack/cmd/arch.py index 96de71e48f7ae4312a9c45b768fc000fcc859be2..f3208ec5c0b70c95b315eb34e8e2dcca888a637c 100644 --- a/lib/spack/spack/cmd/arch.py +++ b/lib/spack/spack/cmd/arch.py @@ -32,7 +32,7 @@ def arch(parser, args): if args.platform: print(arch.platform) elif args.operating_system: - print(arch.platform_os) + print(arch.os) elif args.target: print(arch.target) else: diff --git a/lib/spack/spack/cmd/dependencies.py b/lib/spack/spack/cmd/dependencies.py index 2dde325ab257e67255177b608e9e7eb62923ae78..31817783b2883b69f5454419902eae6a7c5ca744 100644 --- a/lib/spack/spack/cmd/dependencies.py +++ b/lib/spack/spack/cmd/dependencies.py @@ -42,7 +42,8 @@ def dependencies(parser, args): env = ev.get_env(args, 'dependencies') 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( spec, 'children', args.transitive) if deps: diff --git a/lib/spack/spack/cmd/dependents.py b/lib/spack/spack/cmd/dependents.py index 51bf1fa656c621f1d6be801cd732f1fd97d205dd..368dae8f55d8871bf9106f9b1f4153387e97b5a9 100644 --- a/lib/spack/spack/cmd/dependents.py +++ b/lib/spack/spack/cmd/dependents.py @@ -85,7 +85,8 @@ def dependents(parser, args): env = ev.get_env(args, 'dependents') 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( spec, 'parents', args.transitive) if deps: diff --git a/lib/spack/spack/cmd/mirror.py b/lib/spack/spack/cmd/mirror.py index 1b5167e8165a1c9ef5bcfca4026724f42bfd64e8..698b78adb50863416a96b5b839a7668b04986693 100644 --- a/lib/spack/spack/cmd/mirror.py +++ b/lib/spack/spack/cmd/mirror.py @@ -162,7 +162,7 @@ def mirror_create(args): # If nothing is passed, use all packages. if not specs: 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 args.dependencies: @@ -204,7 +204,7 @@ def mirror_create(args): " %-4d failed to fetch." % e) if error: tty.error("Failed downloads:") - colify(s.cformat("$_$@") for s in error) + colify(s.cformat("{name}{@version}") for s in error) def mirror(parser, args): diff --git a/lib/spack/spack/cmd/modules/__init__.py b/lib/spack/spack/cmd/modules/__init__.py index e3f2e8099ba2eb6b066ba4696f305d0e9fcaacca..7fadd73ee0739b1337206131e975c9d37116cb4d 100644 --- a/lib/spack/spack/cmd/modules/__init__.py +++ b/lib/spack/spack/cmd/modules/__init__.py @@ -344,7 +344,9 @@ def modules_cmd(parser, args, module_type, callbacks=callbacks): except MultipleSpecsMatch: msg = "the constraint '{query}' matches multiple packages:\n" 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.die('In this context exactly **one** match is needed: please specify your constraints better.') # NOQA: ignore=E501 diff --git a/lib/spack/spack/cmd/spec.py b/lib/spack/spack/cmd/spec.py index f60e5f12835ab5ddb99091167c8fbe04cac9d1b2..64a4a7edc9db83fd97a62e6da6c74887e1929593 100644 --- a/lib/spack/spack/cmd/spec.py +++ b/lib/spack/spack/cmd/spec.py @@ -42,11 +42,12 @@ def setup_parser(subparser): 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 kwargs = { 'cover': args.cover, - 'format': name_fmt + '$@$%@+$+$=', + 'format': name_fmt + fmt, 'hashlen': None if args.very_long else 7, 'show_types': args.types, 'status_fn': install_status_fn if args.install_status else None diff --git a/lib/spack/spack/cmd/uninstall.py b/lib/spack/spack/cmd/uninstall.py index cd89f5aba8bb0b09155463198e98b4cfdefc9136..532e0c0de413abaf3cb4bcfef8f21d1ddd1072f0 100644 --- a/lib/spack/spack/cmd/uninstall.py +++ b/lib/spack/spack/cmd/uninstall.py @@ -260,7 +260,8 @@ def get_uninstall_list(args, specs, env): if i > 0: 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') dependents = active_dpts.get(spec) diff --git a/lib/spack/spack/cmd/view.py b/lib/spack/spack/cmd/view.py index 6ef455770fdcb3639ee586bb555f927c2c5f6985..5480c28c6d8939341294a57c7ae0ac47349ef3dc 100644 --- a/lib/spack/spack/cmd/view.py +++ b/lib/spack/spack/cmd/view.py @@ -70,10 +70,11 @@ def squash(matching_specs): matching_in_view = [ms for ms in matching_specs if ms in view_specs] if len(matching_in_view) > 1: + spec_format = '{name}{@version}{%compiler}{arch=architecture}' args = ["Spec matches multiple packages.", "Matching packages:"] 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."] tty.die(*args) diff --git a/lib/spack/spack/compilers/__init__.py b/lib/spack/spack/compilers/__init__.py index b2be6d84fd1172bf2ff8a222639c0a9c4c6b8b02..b7b26e289093c386be3a72593348ff9b53ae36a0 100644 --- a/lib/spack/spack/compilers/__init__.py +++ b/lib/spack/spack/compilers/__init__.py @@ -307,7 +307,7 @@ def get_compilers(config, cspec=None, arch_spec=None): # If an arch spec is given, confirm that this compiler # is for the given operating system os = items.get('operating_system', None) - if arch_spec and os != arch_spec.platform_os: + if arch_spec and os != arch_spec.os: continue # 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) if len(compilers) < 1: - raise NoCompilerForSpecError(compiler_spec, arch_spec.platform_os) + raise NoCompilerForSpecError(compiler_spec, arch_spec.os) if len(compilers) > 1: raise CompilerDuplicateError(compiler_spec, arch_spec) return compilers[0] diff --git a/lib/spack/spack/concretize.py b/lib/spack/spack/concretize.py index 01f32b8c9597aaedb1f70ad0b7005615a3ec0ac7..a4d01af9961c6a25a23804abca0ce0ec105db36e 100644 --- a/lib/spack/spack/concretize.py +++ b/lib/spack/spack/concretize.py @@ -285,7 +285,7 @@ def concretize_compiler(self, spec): """ # Pass on concretizing the compiler if the target or operating system # 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 # continue. `return True` here to force concretization to keep # running. @@ -371,7 +371,7 @@ def concretize_compiler_flags(self, spec): """ # Pass on concretizing the compiler flags if the target or operating # 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 # continue. `return True` here to force concretization to keep # running. @@ -471,7 +471,7 @@ def __init__(self, arch, available_os_targets): " for operating system %s and target %s." "\nIf previous installations have succeeded, the" " operating system may have been updated." % - (arch.platform_os, arch.target)) + (arch.os, arch.target)) available_os_target_strs = list() 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) if arch: err_msg += " for operating system {0} and target {1}.".format( - arch.platform_os, arch.target + arch.os, arch.target ) super(UnavailableCompilerVersionError, self).__init__( diff --git a/lib/spack/spack/database.py b/lib/spack/spack/database.py index ded79cbd153dda8cad2c5f46a8bcb82e9749ae89..9781a7ed7583389325de27ed1efbb16c84994805 100644 --- a/lib/spack/spack/database.py +++ b/lib/spack/spack/database.py @@ -384,7 +384,8 @@ def _assign_dependencies(self, hash_key, installs, data): if not child: msg = ("Missing dependency not in database: " "%s needs %s-%s" % ( - spec.cformat('$_$/'), dname, dhash[:7])) + spec.cformat('{name}{/hash:7}'), + dname, dhash[:7])) if self._fail_when_missing_deps: raise MissingDependenciesError(msg) tty.warn(msg) diff --git a/lib/spack/spack/directory_layout.py b/lib/spack/spack/directory_layout.py index 56b5cdeec69f185a3dcc40a17cfb76b1b392b122..b90daaecadf5d19e0b86f51c2f63e7b94d8dff19 100644 --- a/lib/spack/spack/directory_layout.py +++ b/lib/spack/spack/directory_layout.py @@ -175,15 +175,15 @@ def __init__(self, root, **kwargs): super(YamlDirectoryLayout, self).__init__(root) self.hash_len = kwargs.get('hash_len') self.path_scheme = kwargs.get('path_scheme') or ( - "${ARCHITECTURE}/" - "${COMPILERNAME}-${COMPILERVER}/" - "${PACKAGE}-${VERSION}-${HASH}") + "{architecture}/" + "{compiler.name}-{compiler.version}/" + "{name}-{version}-{hash}") 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( "Conflicting options for installation layout hash length") 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 # locate files in older upstream databases diff --git a/lib/spack/spack/filesystem_view.py b/lib/spack/spack/filesystem_view.py index abb6eb4c241e5c7032a723aed13a3c08c35a6a69..4a6e45172bf6425c8944536b35c25acab3ec87e5 100644 --- a/lib/spack/spack/filesystem_view.py +++ b/lib/spack/spack/filesystem_view.py @@ -557,7 +557,8 @@ def print_status(self, *specs, **kwargs): specs = index[(architecture, compiler)] specs.sort() - format_string = '$_$@$%@+$+' + format_string = '{name}{@version}' + format_string += '{%compiler}{compiler_flags}{variants}' abbreviated = [s.cformat(format_string) for s in specs] # Print one spec per line along with prefix path diff --git a/lib/spack/spack/mirror.py b/lib/spack/spack/mirror.py index 06868853cf6b9d73610417c685a3f223c492ff7c..f8e795ed56cbd286769d6afb3f3c34a724546f68 100644 --- a/lib/spack/spack/mirror.py +++ b/lib/spack/spack/mirror.py @@ -210,7 +210,9 @@ def create(path, specs, **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: spec.package.do_fetch() spec.package.do_clean() @@ -220,7 +222,8 @@ def add_single_spec(spec, mirror_root, categories, **kwargs): sys.excepthook(*sys.exc_info()) else: tty.warn( - "Error while fetching %s" % spec.cformat('$_$@'), e.message) + "Error while fetching %s" % spec.cformat('{name}{@version}'), + e.message) categories['error'].append(spec) diff --git a/lib/spack/spack/modules/common.py b/lib/spack/spack/modules/common.py index 748013ade423badefcf504d31c8cb64b9da23786..a372f3dd01c16af40a65cd4e28f2914b54ef4b4f 100644 --- a/lib/spack/spack/modules/common.py +++ b/lib/spack/spack/modules/common.py @@ -59,12 +59,12 @@ #: Valid tokens for naming scheme and env variable names _valid_tokens = ( - 'PACKAGE', - 'VERSION', - 'COMPILER', - 'COMPILERNAME', - 'COMPILERVER', - 'ARCHITECTURE' + 'name', + 'version', + 'compiler', + 'compiler.name', + 'compiler.version', + 'architecture' ) @@ -80,8 +80,9 @@ def _check_tokens_are_valid(format_string, message): tokens are found """ - named_tokens = re.findall(r'\${(\w*)}', format_string) - invalid_tokens = [x for x in named_tokens if x not in _valid_tokens] + named_tokens = re.findall(r'{(\w*)}', format_string) + invalid_tokens = [x for x in named_tokens + if x.lower() not in _valid_tokens] if invalid_tokens: msg = message msg += ' [{0}]. '.format(', '.join(invalid_tokens)) @@ -294,7 +295,7 @@ def naming_scheme(self): """Naming scheme suitable for non-hierarchical layouts""" scheme = self.module.configuration.get( 'naming_scheme', - '${PACKAGE}-${VERSION}-${COMPILERNAME}-${COMPILERVER}' + '{name}-{version}-{compiler.name}-{compiler.version}' ) # Ensure the named tokens we are expanding are allowed, see @@ -523,7 +524,7 @@ def short_description(self): value = re.sub(r'"', "'", value) return value # Otherwise the short description is just the package + version - return self.spec.format("$_ $@") + return self.spec.format("{name} {@version}") @tengine.context_property def long_description(self): diff --git a/lib/spack/spack/modules/lmod.py b/lib/spack/spack/modules/lmod.py index 90d5440ba35f3ebba9390a1e9e73f942388247a0..a381e08ca9e32303ca962b98a4743280ce47299b 100644 --- a/lib/spack/spack/modules/lmod.py +++ b/lib/spack/spack/modules/lmod.py @@ -232,7 +232,7 @@ def use_name(self): to console to use it. """ # Package name and version - base = os.path.join("${PACKAGE}", "${VERSION}") + base = os.path.join("{name}", "{version}") name_parts = [self.spec.format(base)] # The remaining elements are filename suffixes name_parts.extend(self.conf.suffixes) diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index b54a8abb747a13f2197281b85c6fa58c3b60bbb3..4b81afe84e645e2626b849d5fde7b0271919c5ab 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -946,7 +946,7 @@ def do_fetch(self, mirror_only=False): checksum = spack.config.get('config:checksum') if checksum and self.version not in self.versions: 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 # interactive, but just fail if non-interactive. @@ -960,7 +960,7 @@ def do_fetch(self, mirror_only=False): if not ignore_checksum: raise FetchError("Will not fetch %s" % - self.spec.format('$_$@'), ck_msg) + self.spec.format('{name}{@version}'), ck_msg) self.stage.create() self.stage.fetch(mirror_only) diff --git a/lib/spack/spack/spec.py b/lib/spack/spack/spec.py index 67e0990a1988785bbb7f5990ea8269c9a4268cf6..6afdc92f24d3dda6863f99cbfce833835db94f7b 100644 --- a/lib/spack/spack/spec.py +++ b/lib/spack/spack/spec.py @@ -96,6 +96,7 @@ from llnl.util.tty.color import cwrite, colorize, cescape, get_color_when import llnl.util.tty as tty +import spack.paths import spack.architecture import spack.compiler import spack.compilers as compilers @@ -187,6 +188,10 @@ #: Max integer helps avoid passing too large a value to cyaml. 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): """Returns a spec colorized according to the colors specified in @@ -221,7 +226,7 @@ class ArchSpec(object): def __init__(self, *args): 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: spec_like = args[0] @@ -231,13 +236,13 @@ def __init__(self, *args): spec_fields = spec_like.split("-") 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) else: raise ValueError("%s is an invalid arch spec" % spec_like) elif len(args) == 3: 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]) elif len(args) != 0: raise TypeError("Can't make arch spec from %s" % args) @@ -248,11 +253,11 @@ def _autospec(self, spec_like): return ArchSpec(spec_like) def _cmp_key(self): - return (self.platform, self.platform_os, self.target) + return (self.platform, self.os, self.target) def _dup(self, other): self.platform = other.platform - self.platform_os = other.platform_os + self.os = other.os self.target = other.target @property @@ -269,11 +274,11 @@ def platform(self, value): self._platform = value @property - def platform_os(self): - return self._platform_os + def os(self): + return self._os - @platform_os.setter - def platform_os(self, value): + @os.setter + def os(self, value): """ 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 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) value = str(spec_platform.operating_system(value)) - self._platform_os = value + self._os = value @property def target(self): @@ -369,13 +374,13 @@ def to_cmp_dict(self): """Returns a dictionary that can be used for field comparison.""" return dict([ ('platform', self.platform), - ('platform_os', self.platform_os), + ('os', self.os), ('target', self.target)]) def to_dict(self): d = syaml_dict([ ('platform', self.platform), - ('platform_os', self.platform_os), + ('platform_os', self.os), ('target', self.target)]) return syaml_dict([('arch', d)]) @@ -397,10 +402,13 @@ def from_dict(d): return ArchSpec('spack09', 'unknown', d['arch']) d = d['arch'] - return ArchSpec(d['platform'], d['platform_os'], d['target']) + if 'platform_os' in d: + return ArchSpec(d['platform'], d['platform_os'], d['target']) + else: + return ArchSpec(d['platform'], d['os'], d['target']) 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): return str(self) @@ -835,10 +843,10 @@ def __get__(self, instance, cls): # properties defined and no default handler, or that all callbacks # raised AttributeError. In this case, we raise AttributeError with an # 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 += '\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( name=pkg.name, query=self.attribute_name, @@ -1004,11 +1012,11 @@ def _add_flag(self, name, value): if name == 'arch' or name == 'architecture': parts = tuple(value.split('-')) 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': self._set_architecture(platform=value) elif name == 'os' or name == 'operating_system': - self._set_architecture(platform_os=value) + self._set_architecture(os=value) elif name == 'target': self._set_architecture(target=value) elif name in valid_flags: @@ -1026,7 +1034,7 @@ def _add_flag(self, name, value): def _set_architecture(self, **kwargs): """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: raise DuplicateArchitectureError( "Spec for '%s' cannot have two architectures." % self.name) @@ -1258,12 +1266,16 @@ def return_val(dspec): def short_spec(self): """Returns a version of the spec with the dependencies hashed 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 def cshort_spec(self): """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 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 != oarch.platform: raise UnsatisfiableArchitectureSpecError(sarch, oarch) - if sarch.platform_os is not None and oarch.platform_os is not None: - if sarch.platform_os != oarch.platform_os: + if sarch.os is not None and oarch.os is not None: + if sarch.os != oarch.os: raise UnsatisfiableArchitectureSpecError(sarch, oarch) if sarch.target is not None and oarch.target is not None: if sarch.target != oarch.target: @@ -2460,8 +2472,8 @@ def constrain(self, other, deps=True): else: if sarch.platform is None or oarch.platform is None: self.architecture.platform = sarch.platform or oarch.platform - if sarch.platform_os is None or oarch.platform_os is None: - sarch.platform_os = sarch.platform_os or oarch.platform_os + if sarch.os is None or oarch.os is None: + sarch.os = sarch.os or oarch.os if sarch.target is None or oarch.target is None: sarch.target = sarch.target or oarch.target changed |= (str(self.architecture) != old) @@ -3010,10 +3022,245 @@ def _cmp_key(self): def colorized(self): return colorize_spec(self) - def format(self, format_string='$_$@$%@+$+$=', **kwargs): - """Prints out particular pieces of a spec, depending on what is + def format(self, format_string=default_format, **kwargs): + r"""Prints out particular pieces of a spec, depending on what is 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:: $_ 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., ``$//`` for full hash. """ + color = kwargs.get('color', False) # Dictionary of transformations for named tokens @@ -3218,7 +3466,7 @@ def write(s, c=None): platform = str(self.architecture.platform) write(fmt % transform(self, platform), '=') elif named_str == "OS": - operating_sys = str(self.architecture.platform_os) + operating_sys = str(self.architecture.os) write(fmt % transform(self, operating_sys), '=') elif named_str == "TARGET": target = str(self.architecture.target) @@ -3302,7 +3550,7 @@ def tree(self, **kwargs): status_fn = kwargs.pop('status_fn', False) cover = kwargs.pop('cover', 'nodes') indent = kwargs.pop('indent', 0) - fmt = kwargs.pop('format', '$_$@$%@+$+$=') + fmt = kwargs.pop('format', default_format) prefix = kwargs.pop('prefix', None) show_types = kwargs.pop('show_types', False) deptypes = kwargs.pop('deptypes', 'all') @@ -3516,7 +3764,7 @@ def do_parse(self): for spec in specs: for s in spec.traverse(): 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) return specs @@ -3877,7 +4125,9 @@ def __init__(self, provided, required): class AmbiguousHashError(SpecError): 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) super(AmbiguousHashError, self).__init__(msg + specs_str) @@ -3904,6 +4154,18 @@ def __init__(self, spec, addition): % (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): def __init__(self, spec, matches): message = 'Conflicts in concretized spec "{0}"\n'.format( diff --git a/lib/spack/spack/test/architecture.py b/lib/spack/spack/test/architecture.py index 152f3a590945ef7557cea22308369ad94fc5e77e..d06fad6b37dcaa7f61feec88e6d666b246242d35 100644 --- a/lib/spack/spack/test/architecture.py +++ b/lib/spack/spack/test/architecture.py @@ -1,3 +1,4 @@ + # Copyright 2013-2019 Lawrence Livermore National Security, LLC and other # Spack Project Developers. See the top-level COPYRIGHT file for details. # @@ -21,7 +22,7 @@ def test_dict_functions_for_architecture(): arch = spack.architecture.Arch() 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') new_arch = spack.architecture.Arch.from_dict(arch.to_dict()) @@ -29,26 +30,26 @@ def test_dict_functions_for_architecture(): assert arch == new_arch assert isinstance(arch, spack.architecture.Arch) 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(new_arch, spack.architecture.Arch) 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) def test_platform(): - output_platform_class = spack.architecture.real_platform() - if os.environ.get('CRAYPE_VERSION') is not None: - my_platform_class = Cray() - elif os.path.exists('/bgsys'): - my_platform_class = Bgq() - elif 'Linux' in py_platform.system(): - my_platform_class = Linux() - elif 'Darwin' in py_platform.system(): - my_platform_class = Darwin() + output_platform_class = spack.architecture.real_platform() + if os.environ.get('CRAYPE_VERSION') is not None: + my_platform_class = Cray() + elif os.path.exists('/bgsys'): + my_platform_class = Bgq() + elif 'Linux' in py_platform.system(): + my_platform_class = Linux() + elif 'Darwin' in py_platform.system(): + my_platform_class = Darwin() - assert str(output_platform_class) == str(my_platform_class) + assert str(output_platform_class) == str(my_platform_class) def test_boolness(): @@ -67,7 +68,7 @@ def test_boolness(): assert arch arch = spack.architecture.Arch() - arch.platform_os = plat_os + arch.os = plat_os assert 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.concretize() - assert frontend_os == frontend_spec.architecture.platform_os + assert frontend_os == frontend_spec.architecture.os 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.concretize() - assert backend_os == backend_spec.architecture.platform_os + assert backend_os == backend_spec.architecture.os 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.concretize() - assert default_os == default_spec.architecture.platform_os + assert default_os == default_spec.architecture.os 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.concretize() results.append( - spec.architecture.platform_os == str(platform.operating_system(o)) + spec.architecture.os == str(platform.operating_system(o)) ) results.append( spec.architecture.target == str(platform.target(t)) diff --git a/lib/spack/spack/test/cmd/view.py b/lib/spack/spack/test/cmd/view.py index ea4b0cc4f5c1196655073418915525e924e6ed34..f7375d8903388f8c6bb3749a4b61521de31f66d6 100644 --- a/lib/spack/spack/test/cmd/view.py +++ b/lib/spack/spack/test/cmd/view.py @@ -45,7 +45,7 @@ def test_view_projections( viewpath = str(tmpdir.mkdir('view_{0}'.format(cmd))) view_projection = { 'projections': { - 'all': '${PACKAGE}-${VERSION}' + 'all': '{name}-{version}' } } projection_file = create_projection_file(tmpdir, view_projection) @@ -65,8 +65,8 @@ def test_view_multiple_projections( viewpath = str(tmpdir.mkdir('view')) view_projection = s_yaml.syaml_dict( - [('extendee', '${PACKAGE}-${COMPILERNAME}'), - ('all', '${PACKAGE}-${VERSION}')] + [('extendee', '{name}-{compiler.name}'), + ('all', '{name}-{version}')] ) projection_file = create_projection_file(tmpdir, view_projection) @@ -87,8 +87,8 @@ def test_view_multiple_projections_all_first( viewpath = str(tmpdir.mkdir('view')) view_projection = s_yaml.syaml_dict( - [('all', '${PACKAGE}-${VERSION}'), - ('extendee', '${PACKAGE}-${COMPILERNAME}')] + [('all', '{name}-{version}'), + ('extendee', '{name}-{compiler.name}')] ) projection_file = create_projection_file(tmpdir, view_projection) @@ -145,7 +145,7 @@ def test_view_extension_projection( install('extension2@1.0') viewpath = str(tmpdir.mkdir('view')) - view_projection = {'all': '${PACKAGE}-${VERSION}'} + view_projection = {'all': '{name}-{version}'} projection_file = create_projection_file(tmpdir, view_projection) view('symlink', viewpath, '--projection-file={0}'.format(projection_file), 'extension1@1.0') diff --git a/lib/spack/spack/test/data/modules/lmod/alter_environment.yaml b/lib/spack/spack/test/data/modules/lmod/alter_environment.yaml index a963af1ff8a720b2f6b56053347f4fab7269734c..caa01560f1af906efc68a22354ea884f3dd12266 100644 --- a/lib/spack/spack/test/data/modules/lmod/alter_environment.yaml +++ b/lib/spack/spack/test/data/modules/lmod/alter_environment.yaml @@ -13,7 +13,7 @@ lmod: - CMAKE_PREFIX_PATH environment: set: - '${PACKAGE}_ROOT': '${PREFIX}' + '{name}_ROOT': '{prefix}' 'platform=test target=x86_64': environment: diff --git a/lib/spack/spack/test/data/modules/tcl/alter_environment.yaml b/lib/spack/spack/test/data/modules/tcl/alter_environment.yaml index c9f00c636001621f516a92558c0d2bf266047934..a55ad94b0ee6ca459e6b1fc939869832e9f44aa1 100644 --- a/lib/spack/spack/test/data/modules/tcl/alter_environment.yaml +++ b/lib/spack/spack/test/data/modules/tcl/alter_environment.yaml @@ -7,7 +7,7 @@ tcl: - CMAKE_PREFIX_PATH environment: set: - '${PACKAGE}_ROOT': '${PREFIX}' + '{name}_ROOT': '{prefix}' 'platform=test target=x86_64': environment: diff --git a/lib/spack/spack/test/data/modules/tcl/conflicts.yaml b/lib/spack/spack/test/data/modules/tcl/conflicts.yaml index 4d0eb8d526b4c136d494eac3ce4f5d52e3fb63a6..66494b1ce1957817eeed2a24732e5a2dddbafa6d 100644 --- a/lib/spack/spack/test/data/modules/tcl/conflicts.yaml +++ b/lib/spack/spack/test/data/modules/tcl/conflicts.yaml @@ -1,8 +1,8 @@ enable: - tcl tcl: - naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}' + naming_scheme: '{name}/{version}-{compiler.name}' all: conflict: - - '${PACKAGE}' + - '{name}' - 'intel/14.0.1' diff --git a/lib/spack/spack/test/data/modules/tcl/invalid_naming_scheme.yaml b/lib/spack/spack/test/data/modules/tcl/invalid_naming_scheme.yaml index 2f72ba4aab1bdb7a66e4ab50eb202f0bacf1bd3d..f523f0bbef02c76d0d4731ff7ba1be15e4c9a8e9 100644 --- a/lib/spack/spack/test/data/modules/tcl/invalid_naming_scheme.yaml +++ b/lib/spack/spack/test/data/modules/tcl/invalid_naming_scheme.yaml @@ -1,5 +1,5 @@ enable: - tcl tcl: - # ${OPTIONS} is not allowed in the naming scheme, see #2884 - naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}-${OPTIONS}' + # {variants} is not allowed in the naming scheme, see #2884 + naming_scheme: '{name}/{version}-{compiler.name}-{variants}' diff --git a/lib/spack/spack/test/data/modules/tcl/invalid_token_in_env_var_name.yaml b/lib/spack/spack/test/data/modules/tcl/invalid_token_in_env_var_name.yaml index 297028531bd6fefe863f6bf6ee0f9587af36a3f6..d536c994fa7612b3b5f3ebbe1c968d5d14a52110 100644 --- a/lib/spack/spack/test/data/modules/tcl/invalid_token_in_env_var_name.yaml +++ b/lib/spack/spack/test/data/modules/tcl/invalid_token_in_env_var_name.yaml @@ -7,12 +7,12 @@ tcl: - CMAKE_PREFIX_PATH environment: set: - '${PACKAGE}_ROOT_${PREFIX}': '${PREFIX}' + '{name}_ROOT_{prefix}': '{prefix}' 'platform=test target=x86_64': environment: set: - FOO_${OPTIONS}: 'foo' + FOO_{variants}: 'foo' unset: - BAR diff --git a/lib/spack/spack/test/data/modules/tcl/wrong_conflicts.yaml b/lib/spack/spack/test/data/modules/tcl/wrong_conflicts.yaml index 0568605248b5ff17ce8fefc56ae0ba3b13ad82e9..a4bd97257be2be101e1098813cb22e323218d3bb 100644 --- a/lib/spack/spack/test/data/modules/tcl/wrong_conflicts.yaml +++ b/lib/spack/spack/test/data/modules/tcl/wrong_conflicts.yaml @@ -1,7 +1,7 @@ enable: - tcl tcl: - naming_scheme: '${PACKAGE}/${VERSION}-${COMPILERNAME}' + naming_scheme: '{name}/{version}-{compiler.name}' all: conflict: - - '${PACKAGE}/${COMPILERNAME}' + - '{name}/{compiler.name}' diff --git a/lib/spack/spack/test/directory_layout.py b/lib/spack/spack/test/directory_layout.py index 7e8cda611f86c6494e36d0322773030342a5f82c..0b6b31bef04786523d3cc9e22090230857fc64ae 100644 --- a/lib/spack/spack/test/directory_layout.py +++ b/lib/spack/spack/test/directory_layout.py @@ -41,9 +41,9 @@ def test_yaml_directory_layout_parameters( layout_default = YamlDirectoryLayout(str(tmpdir)) path_default = layout_default.relative_path_for_spec(spec) assert(path_default == spec.format( - "${ARCHITECTURE}/" - "${COMPILERNAME}-${COMPILERVER}/" - "${PACKAGE}-${VERSION}-${HASH}")) + "{architecture}/" + "{compiler.name}-{compiler.version}/" + "{name}-{version}-{hash}")) # Test hash_length parameter works correctly layout_10 = YamlDirectoryLayout(str(tmpdir), hash_len=10) @@ -56,7 +56,7 @@ def test_yaml_directory_layout_parameters( # Test path_scheme arch, compiler, package7 = path_7.split('/') - scheme_package7 = "${PACKAGE}-${VERSION}-${HASH:7}" + scheme_package7 = "{name}-{version}-{hash:7}" layout_package7 = YamlDirectoryLayout(str(tmpdir), path_scheme=scheme_package7) path_package7 = layout_package7.relative_path_for_spec(spec) @@ -64,7 +64,7 @@ def test_yaml_directory_layout_parameters( assert(package7 == path_package7) # 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), path_scheme=arch_scheme_package) arch_path_package = layout_arch_package.relative_path_for_spec(spec) diff --git a/lib/spack/spack/test/graph.py b/lib/spack/spack/test/graph.py index c0731a7257a6e7ae11bd4d1c5a2520072fafd449..a29779e3ca42acf2417252e48750d4eb25d5bcd9 100644 --- a/lib/spack/spack/test/graph.py +++ b/lib/spack/spack/test/graph.py @@ -65,16 +65,16 @@ def test_dynamic_dot_graph_mpileaks(mock_packages): dot = stream.getvalue() - mpileaks_hash, mpileaks_lbl = s.dag_hash(), s.format('$_$/') - mpi_hash, mpi_lbl = s['mpi'].dag_hash(), s['mpi'].format('$_$/') + mpileaks_hash, mpileaks_lbl = s.dag_hash(), s.format('{name}{/hash:7}') + mpi_hash, mpi_lbl = s['mpi'].dag_hash(), s['mpi'].format('{name}{/hash:7}') 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 = ( - s['dyninst'].dag_hash(), s['dyninst'].format('$_$/')) + s['dyninst'].dag_hash(), s['dyninst'].format('{name}{/hash:7}')) 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 = ( - 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' % (callpath_hash, callpath_lbl) in dot diff --git a/lib/spack/spack/test/modules/tcl.py b/lib/spack/spack/test/modules/tcl.py index 852f553f9f9de693ce2c24152fd299c189af8421..f240fbec35fd82e2e783d9c7b964ed216d6e55ad 100644 --- a/lib/spack/spack/test/modules/tcl.py +++ b/lib/spack/spack/test/modules/tcl.py @@ -151,7 +151,7 @@ def test_naming_scheme(self, factory, module_configuration): # Test we read the expected configuration for the naming scheme writer, _ = factory('mpileaks') - expected = '${PACKAGE}/${VERSION}-${COMPILERNAME}' + expected = '{name}/{version}-{compiler.name}' assert writer.conf.naming_scheme == expected diff --git a/lib/spack/spack/test/spec_semantics.py b/lib/spack/spack/test/spec_semantics.py index bec16fc7ad5527f777b01b476589ab44ff405b22..f65cc44ff566597732b980c787225ffe37bf2f6b 100644 --- a/lib/spack/spack/test/spec_semantics.py +++ b/lib/spack/spack/test/spec_semantics.py @@ -8,6 +8,7 @@ from spack.spec import Spec, UnsatisfiableSpecError, SpecError 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 MultipleValuesInExclusiveVariantError @@ -736,28 +737,133 @@ def test_exceptional_paths_for_constructor(self): Spec('libelf foo') 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.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?) - 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 + # Mixed case intentional for testing both package_segments = [("${PACKAGE}", "name"), ("${VERSION}", "versions"), - ("${COMPILER}", "compiler"), - ("${COMPILERFLAGS}", "compiler_flags"), - ("${OPTIONS}", "variants"), - ("${ARCHITECTURE}", "architecture")] + ("${compiler}", "compiler"), + ("${compilerflags}", "compiler_flags"), + ("${options}", "variants"), + ("${architecture}", "architecture")] - compiler_segments = [("${COMPILERNAME}", "name"), - ("${COMPILERVER}", "versions")] + compiler_segments = [("${compilername}", "name"), + ("${compilerver}", "versions")] architecture_segments = [("${PLATFORM}", "platform"), - ("${OS}", "platform_os"), + ("${OS}", "os"), ("${TARGET}", "target")] for named_str, prop in package_segments: diff --git a/lib/spack/spack/test/spec_syntax.py b/lib/spack/spack/test/spec_syntax.py index e4797b5c1d65299b845323c478566c4006f5b051..8ad987fbd6dcf007572d9b548dc99433789287e3 100644 --- a/lib/spack/spack/test/spec_syntax.py +++ b/lib/spack/spack/test/spec_syntax.py @@ -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") self.check_parse( - "x arch=test-redhat6-None " - " ^y arch=test-None-x86_64 " + "x arch=test-redhat6-None" + " ^y arch=test-None-x86_64" " ^z arch=linux-None-None", "x os=fe " @@ -212,11 +212,11 @@ def test_canonicalize(self): "^z platform=linux") self.check_parse( - "x arch=test-debian6-x86_64 " + "x arch=test-debian6-x86_64" " ^y arch=test-debian6-x86_64", - "x os=default_os target=default_target " - "^y os=default_os target=default_target") + "x os=default_os target=default_target" + " ^y os=default_os target=default_target") self.check_parse("x ^y", "x@: ^y@:") diff --git a/var/spack/repos/builtin/packages/gcc/package.py b/var/spack/repos/builtin/packages/gcc/package.py index 9753a0bb93463002b397510ba4629097aa873c95..0d4a9ace481a92bde91ad38dfcff4b6b04de0a15 100644 --- a/var/spack/repos/builtin/packages/gcc/package.py +++ b/var/spack/repos/builtin/packages/gcc/package.py @@ -397,7 +397,7 @@ def write_rpath_specs(self): the compiler used to build the executable.""" if not self.spec_dir: tty.warn('Could not install specs for {0}.'.format( - self.spec.format('$_$@'))) + self.spec.format('{name}{@version}'))) return gcc = self.spec['gcc'].command diff --git a/var/spack/repos/builtin/packages/of-adios-write/package.py b/var/spack/repos/builtin/packages/of-adios-write/package.py index 541219da0ef584cba633b31cc8f7ff8ba89179ea..f2265cc97437f0510378feca2bf91d19a81ab761 100644 --- a/var/spack/repos/builtin/packages/of-adios-write/package.py +++ b/var/spack/repos/builtin/packages/of-adios-write/package.py @@ -67,7 +67,9 @@ def patch(self): add_extra_files(self, self.common, self.assets) # Emit openfoam version immediately, if we resolved the wrong version # 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): """Generate spack-config.sh file.""" diff --git a/var/spack/repos/builtin/packages/of-precice/package.py b/var/spack/repos/builtin/packages/of-precice/package.py index 36ed6be5be43b0c57f39f4bd5ccd7aa3714f630f..82e486dbfcd0abc7b421ac292a9b6547c924a5dd 100644 --- a/var/spack/repos/builtin/packages/of-precice/package.py +++ b/var/spack/repos/builtin/packages/of-precice/package.py @@ -42,7 +42,9 @@ def patch(self): add_extra_files(self, self.common, self.assets) # Emit openfoam version immediately, if we resolved the wrong version # 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): """Generate spack-config.sh file."""