Add CompilerSpec class and loading capability.
- spack.spec.Compiler is now spack.spec.CompilerSpec - Can load a spack.compilers.* module for a particular spec - e.g. load Gcc module for gcc@4.7 spec.
This commit is contained in:
@@ -107,7 +107,7 @@ Package class names
|
|||||||
The **class name** (``Libelf`` in our example) is formed by converting
|
The **class name** (``Libelf`` in our example) is formed by converting
|
||||||
words separated by `-` or ``_`` in the file name to camel case. If
|
words separated by `-` or ``_`` in the file name to camel case. If
|
||||||
the name starts with a number, we prefix the class name with
|
the name starts with a number, we prefix the class name with
|
||||||
``Num_``. Here are some examples:
|
``_``. Here are some examples:
|
||||||
|
|
||||||
================= =================
|
================= =================
|
||||||
Module Name Class Name
|
Module Name Class Name
|
||||||
@@ -115,7 +115,7 @@ the name starts with a number, we prefix the class name with
|
|||||||
``foo_bar`` ``FooBar``
|
``foo_bar`` ``FooBar``
|
||||||
``docbook-xml`` ``DocbookXml``
|
``docbook-xml`` ``DocbookXml``
|
||||||
``FooBar`` ``Foobar``
|
``FooBar`` ``Foobar``
|
||||||
``3proxy`` ``Num_3proxy``
|
``3proxy`` ``_3proxy``
|
||||||
================= =================
|
================= =================
|
||||||
|
|
||||||
The class name is needed by Spack to properly import a package, but
|
The class name is needed by Spack to properly import a package, but
|
||||||
|
@@ -34,6 +34,7 @@
|
|||||||
from llnl.util.filesystem import *
|
from llnl.util.filesystem import *
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
|
from spack.compilers import compiler_for_spec
|
||||||
from spack.util.executable import Executable, which
|
from spack.util.executable import Executable, which
|
||||||
from spack.util.environment import *
|
from spack.util.environment import *
|
||||||
|
|
||||||
@@ -79,6 +80,11 @@ def __call__(self, *args, **kwargs):
|
|||||||
super(MakeExecutable, self).__call__(*args, **kwargs)
|
super(MakeExecutable, self).__call__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def set_compiler_environment_variables(pkg):
|
||||||
|
assert(pkg.spec.concrete)
|
||||||
|
compiler = compiler_for_spec(pkg.spec.compiler)
|
||||||
|
|
||||||
|
|
||||||
def set_build_environment_variables(pkg):
|
def set_build_environment_variables(pkg):
|
||||||
"""This ensures a clean install environment when we build packages.
|
"""This ensures a clean install environment when we build packages.
|
||||||
"""
|
"""
|
||||||
|
@@ -25,11 +25,21 @@
|
|||||||
#
|
#
|
||||||
# This needs to be expanded for full compiler support.
|
# This needs to be expanded for full compiler support.
|
||||||
#
|
#
|
||||||
|
import imp
|
||||||
|
|
||||||
from llnl.util.lang import memoized, list_modules
|
from llnl.util.lang import memoized, list_modules
|
||||||
|
from llnl.util.filesystem import join_path
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
|
import spack.error
|
||||||
import spack.spec
|
import spack.spec
|
||||||
|
from spack.compiler import Compiler
|
||||||
from spack.util.executable import which
|
from spack.util.executable import which
|
||||||
|
from spack.util.naming import mod_to_class
|
||||||
|
|
||||||
|
_imported_compilers_module = 'spack.compiler.versions'
|
||||||
|
_imported_versions_module = 'spack.compilers'
|
||||||
|
|
||||||
|
|
||||||
@memoized
|
@memoized
|
||||||
def supported_compilers():
|
def supported_compilers():
|
||||||
@@ -41,29 +51,58 @@ def supported_compilers():
|
|||||||
return sorted(c for c in list_modules(spack.compilers_path))
|
return sorted(c for c in list_modules(spack.compilers_path))
|
||||||
|
|
||||||
|
|
||||||
|
def supported(compiler_spec):
|
||||||
|
"""Test if a particular compiler is supported."""
|
||||||
|
if not isinstance(compiler_spec, spack.spec.CompilerSpec):
|
||||||
|
compiler_spec = spack.spec.CompilerSpec(compiler_spec)
|
||||||
|
return compiler_spec.name in supported_compilers()
|
||||||
|
|
||||||
|
|
||||||
def available_compilers():
|
def available_compilers():
|
||||||
"""Return a list of specs for all the compiler versions currently
|
"""Return a list of specs for all the compiler versions currently
|
||||||
available to build with. These are instances of
|
available to build with. These are instances of
|
||||||
spack.spec.Compiler.
|
CompilerSpec.
|
||||||
"""
|
"""
|
||||||
return [spack.spec.Compiler(c)
|
return [spack.spec.CompilerSpec(c)
|
||||||
for c in list_modules(spack.compiler_version_path)]
|
for c in list_modules(spack.compiler_version_path)]
|
||||||
|
|
||||||
|
|
||||||
def supported(compiler_spec):
|
def compiler_for_spec(compiler_spec):
|
||||||
"""Test if a particular compiler is supported."""
|
"""This gets an instance of an actual spack.compiler.Compiler object
|
||||||
if not isinstance(compiler_spec, spack.spec.Compiler):
|
from a compiler spec. The spec needs to be concrete for this to
|
||||||
compiler_spec = spack.spec.Compiler(compiler_spec)
|
work; it will raise an error if passed an abstract compiler.
|
||||||
return compiler_spec.name in supported_compilers()
|
"""
|
||||||
|
matches = [c for c in available_compilers() if c.satisfies(compiler_spec)]
|
||||||
|
|
||||||
|
# TODO: do something when there are zero matches.
|
||||||
|
assert(len(matches) >= 1)
|
||||||
|
|
||||||
|
compiler = matches[0]
|
||||||
|
file_path = join_path(spack.compiler_version_path, "%s.py" % compiler)
|
||||||
|
|
||||||
|
mod = imp.load_source(_imported_versions_module, file_path)
|
||||||
|
compiler_class = class_for_compiler_name(compiler.name)
|
||||||
|
|
||||||
|
return compiler_class(mod.cc, mod.cxx, mod.f77, mod.f90)
|
||||||
|
|
||||||
|
|
||||||
|
def class_for_compiler_name(compiler_name):
|
||||||
|
assert(supported(compiler_name))
|
||||||
|
|
||||||
|
file_path = join_path(spack.compilers_path, compiler_name + ".py")
|
||||||
|
compiler_mod = imp.load_source(_imported_compilers_module, file_path)
|
||||||
|
return getattr(compiler_mod, mod_to_class(compiler_name))
|
||||||
|
|
||||||
|
|
||||||
@memoized
|
@memoized
|
||||||
def default_compiler():
|
def default_compiler():
|
||||||
"""Get the spec for the default compiler supported by Spack.
|
"""Get the spec for the default compiler on the system.
|
||||||
Currently just returns the system's default gcc.
|
Currently just returns the system's default gcc.
|
||||||
|
|
||||||
TODO: provide a better way to specify/find this on startup.
|
TODO: provide a more sensible default. e.g. on Intel systems
|
||||||
|
we probably want icc. On Mac OS, clang. Probably need
|
||||||
|
to inspect the system and figure this out.
|
||||||
"""
|
"""
|
||||||
gcc = which('gcc', required=True)
|
gcc = which('gcc', required=True)
|
||||||
version = gcc('-dumpversion', return_output=True)
|
version = gcc('-dumpversion', return_output=True)
|
||||||
return spack.spec.Compiler('gcc', version)
|
return spack.spec.CompilerSpec('gcc', version)
|
||||||
|
@@ -99,6 +99,15 @@ def concretize_compiler(self, spec):
|
|||||||
this one has a strict compiler requirement. Otherwise, try to
|
this one has a strict compiler requirement. Otherwise, try to
|
||||||
build with the compiler that will be used by libraries that
|
build with the compiler that will be used by libraries that
|
||||||
link to this one, to maximize compatibility.
|
link to this one, to maximize compatibility.
|
||||||
|
|
||||||
|
TODO: In many cases we probably want to look for installed
|
||||||
|
versions of each package and use *that* version if we
|
||||||
|
can link to it. The policy implemented here will
|
||||||
|
tend to rebuild a lot of stuff becasue it will prefer
|
||||||
|
a compiler in the spec to any compiler already-
|
||||||
|
installed things were built with. There is likely
|
||||||
|
some better policy that finds some middle ground
|
||||||
|
between these two extremes.
|
||||||
"""
|
"""
|
||||||
if spec.compiler and spec.compiler.concrete:
|
if spec.compiler and spec.compiler.concrete:
|
||||||
return
|
return
|
||||||
|
@@ -657,6 +657,7 @@ def do_install(self, **kwargs):
|
|||||||
spack.install_layout.make_path_for_spec(self.spec)
|
spack.install_layout.make_path_for_spec(self.spec)
|
||||||
|
|
||||||
# Set up process's build environment before running install.
|
# Set up process's build environment before running install.
|
||||||
|
build_env.set_compiler_environment_variables(self)
|
||||||
build_env.set_build_environment_variables(self)
|
build_env.set_build_environment_variables(self)
|
||||||
build_env.set_module_variables_for_package(self)
|
build_env.set_module_variables_for_package(self)
|
||||||
|
|
||||||
|
@@ -22,10 +22,8 @@
|
|||||||
# along with this program; if not, write to the Free Software Foundation,
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
##############################################################################
|
##############################################################################
|
||||||
import re
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import string
|
|
||||||
import inspect
|
import inspect
|
||||||
import glob
|
import glob
|
||||||
import imp
|
import imp
|
||||||
@@ -38,6 +36,7 @@
|
|||||||
import spack.error
|
import spack.error
|
||||||
import spack.spec
|
import spack.spec
|
||||||
from spack.virtual import ProviderIndex
|
from spack.virtual import ProviderIndex
|
||||||
|
from spack.util.naming import mod_to_class, validate_module_name
|
||||||
|
|
||||||
# Name of module under which packages are imported
|
# Name of module under which packages are imported
|
||||||
_imported_packages_module = 'spack.packages'
|
_imported_packages_module = 'spack.packages'
|
||||||
@@ -45,42 +44,6 @@
|
|||||||
# Name of the package file inside a package directory
|
# Name of the package file inside a package directory
|
||||||
_package_file_name = 'package.py'
|
_package_file_name = 'package.py'
|
||||||
|
|
||||||
# Valid package names can contain '-' but can't start with it.
|
|
||||||
valid_package_re = r'^\w[\w-]*$'
|
|
||||||
|
|
||||||
# Don't allow consecutive [_-] in package names
|
|
||||||
invalid_package_re = r'[_-][_-]+'
|
|
||||||
|
|
||||||
|
|
||||||
def valid_package_name(pkg_name):
|
|
||||||
"""Return whether the pkg_name is valid for use in Spack."""
|
|
||||||
return (re.match(valid_package_re, pkg_name) and
|
|
||||||
not re.search(invalid_package_re, pkg_name))
|
|
||||||
|
|
||||||
|
|
||||||
def validate_package_name(pkg_name):
|
|
||||||
"""Raise an exception if pkg_name is not valid."""
|
|
||||||
if not valid_package_name(pkg_name):
|
|
||||||
raise InvalidPackageNameError(pkg_name)
|
|
||||||
|
|
||||||
|
|
||||||
def class_name_for_package_name(pkg_name):
|
|
||||||
"""Get a name for the class the package file should contain. Note that
|
|
||||||
conflicts don't matter because the classes are in different modules.
|
|
||||||
"""
|
|
||||||
validate_package_name(pkg_name)
|
|
||||||
|
|
||||||
class_name = pkg_name.replace('_', '-')
|
|
||||||
class_name = string.capwords(class_name, '-')
|
|
||||||
class_name = class_name.replace('-', '')
|
|
||||||
|
|
||||||
# If a class starts with a number, prefix it with Number_ to make it a valid
|
|
||||||
# Python class name.
|
|
||||||
if re.match(r'^[0-9]', class_name):
|
|
||||||
class_name = "Num_%s" % class_name
|
|
||||||
|
|
||||||
return class_name
|
|
||||||
|
|
||||||
|
|
||||||
def _autospec(function):
|
def _autospec(function):
|
||||||
"""Decorator that automatically converts the argument of a single-arg
|
"""Decorator that automatically converts the argument of a single-arg
|
||||||
@@ -143,7 +106,7 @@ def filename_for_package_name(self, pkg_name):
|
|||||||
package doesn't exist yet, so callers will need to ensure
|
package doesn't exist yet, so callers will need to ensure
|
||||||
the package exists before importing.
|
the package exists before importing.
|
||||||
"""
|
"""
|
||||||
validate_package_name(pkg_name)
|
validate_module_name(pkg_name)
|
||||||
pkg_dir = self.dirname_for_package_name(pkg_name)
|
pkg_dir = self.dirname_for_package_name(pkg_name)
|
||||||
return join_path(pkg_dir, _package_file_name)
|
return join_path(pkg_dir, _package_file_name)
|
||||||
|
|
||||||
@@ -200,7 +163,7 @@ def get_class_for_package_name(self, pkg_name):
|
|||||||
else:
|
else:
|
||||||
raise UnknownPackageError(pkg_name)
|
raise UnknownPackageError(pkg_name)
|
||||||
|
|
||||||
class_name = class_name_for_package_name(pkg_name)
|
class_name = mod_to_class(pkg_name)
|
||||||
try:
|
try:
|
||||||
module_name = _imported_packages_module + '.' + pkg_name
|
module_name = _imported_packages_module + '.' + pkg_name
|
||||||
module = imp.load_source(module_name, file_path)
|
module = imp.load_source(module_name, file_path)
|
||||||
@@ -259,14 +222,6 @@ def quote(string):
|
|||||||
out.write('}\n')
|
out.write('}\n')
|
||||||
|
|
||||||
|
|
||||||
class InvalidPackageNameError(spack.error.SpackError):
|
|
||||||
"""Raised when we encounter a bad package name."""
|
|
||||||
def __init__(self, name):
|
|
||||||
super(InvalidPackageNameError, self).__init__(
|
|
||||||
"Invalid package name: " + name)
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
|
|
||||||
class UnknownPackageError(spack.error.SpackError):
|
class UnknownPackageError(spack.error.SpackError):
|
||||||
"""Raised when we encounter a package spack doesn't have."""
|
"""Raised when we encounter a package spack doesn't have."""
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
|
@@ -102,8 +102,7 @@
|
|||||||
import spack
|
import spack
|
||||||
import spack.parse
|
import spack.parse
|
||||||
import spack.error
|
import spack.error
|
||||||
import spack.compilers
|
from spack.compilers import supported as supported_compiler
|
||||||
import spack.compilers.gcc
|
|
||||||
|
|
||||||
from spack.version import *
|
from spack.version import *
|
||||||
from spack.util.string import *
|
from spack.util.string import *
|
||||||
@@ -169,18 +168,30 @@ def __call__(self, match):
|
|||||||
|
|
||||||
|
|
||||||
@key_ordering
|
@key_ordering
|
||||||
class Compiler(object):
|
class CompilerSpec(object):
|
||||||
"""The Compiler field represents the compiler or range of compiler
|
"""The CompilerSpec field represents the compiler or range of compiler
|
||||||
versions that a package should be built with. Compilers have a
|
versions that a package should be built with. CompilerSpecs have a
|
||||||
name and a version list. """
|
name and a version list. """
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
nargs = len(args)
|
nargs = len(args)
|
||||||
if nargs == 1:
|
if nargs == 1:
|
||||||
# If there is one argument, it's a spec to parse
|
arg = args[0]
|
||||||
c = SpecParser().parse_compiler(args[0])
|
# If there is one argument, it's either another CompilerSpec
|
||||||
|
# to copy or a string to parse
|
||||||
|
if isinstance(arg, basestring):
|
||||||
|
c = SpecParser().parse_compiler(arg)
|
||||||
self.name = c.name
|
self.name = c.name
|
||||||
self.versions = c.versions
|
self.versions = c.versions
|
||||||
|
|
||||||
|
elif isinstance(arg, CompilerSpec):
|
||||||
|
self.name = arg.name
|
||||||
|
self.versions = arg.versions.copy()
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise TypeError(
|
||||||
|
"Can only build CompilerSpec from string or CompilerSpec." +
|
||||||
|
" Found %s" % type(arg))
|
||||||
|
|
||||||
elif nargs == 2:
|
elif nargs == 2:
|
||||||
name, version = args
|
name, version = args
|
||||||
self.name = name
|
self.name = name
|
||||||
@@ -197,12 +208,14 @@ def _add_version(self, version):
|
|||||||
|
|
||||||
|
|
||||||
def _autospec(self, compiler_spec_like):
|
def _autospec(self, compiler_spec_like):
|
||||||
if not isinstance(compiler_spec_like, Compiler):
|
if isinstance(compiler_spec_like, CompilerSpec):
|
||||||
return Compiler(compiler_spec_like)
|
|
||||||
return compiler_spec_like
|
return compiler_spec_like
|
||||||
|
return CompilerSpec(compiler_spec_like)
|
||||||
|
|
||||||
|
|
||||||
def satisfies(self, other):
|
def satisfies(self, other):
|
||||||
|
# TODO: This should not just look for overlapping versions.
|
||||||
|
# TODO: e.g., 4.7.3 should satisfy a requirement for 4.7.
|
||||||
other = self._autospec(other)
|
other = self._autospec(other)
|
||||||
return (self.name == other.name and
|
return (self.name == other.name and
|
||||||
self.versions.overlaps(other.versions))
|
self.versions.overlaps(other.versions))
|
||||||
@@ -218,7 +231,7 @@ def constrain(self, other):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def concrete(self):
|
def concrete(self):
|
||||||
"""A Compiler spec is concrete if its versions are concrete."""
|
"""A CompilerSpec is concrete if its versions are concrete."""
|
||||||
return self.versions.concrete
|
return self.versions.concrete
|
||||||
|
|
||||||
|
|
||||||
@@ -230,7 +243,7 @@ def version(self):
|
|||||||
|
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
clone = Compiler.__new__(Compiler)
|
clone = CompilerSpec.__new__(CompilerSpec)
|
||||||
clone.name = self.name
|
clone.name = self.name
|
||||||
clone.versions = self.versions.copy()
|
clone.versions = self.versions.copy()
|
||||||
return clone
|
return clone
|
||||||
@@ -353,7 +366,7 @@ def _add_variant(self, name, enabled):
|
|||||||
|
|
||||||
def _set_compiler(self, compiler):
|
def _set_compiler(self, compiler):
|
||||||
"""Called by the parser to set the compiler."""
|
"""Called by the parser to set the compiler."""
|
||||||
if self.compiler: raise DuplicateCompilerError(
|
if self.compiler: raise DuplicateCompilerSpecError(
|
||||||
"Spec for '%s' cannot have two compilers." % self.name)
|
"Spec for '%s' cannot have two compilers." % self.name)
|
||||||
self.compiler = compiler
|
self.compiler = compiler
|
||||||
|
|
||||||
@@ -808,7 +821,7 @@ def validate_names(self):
|
|||||||
|
|
||||||
# validate compiler in addition to the package name.
|
# validate compiler in addition to the package name.
|
||||||
if spec.compiler:
|
if spec.compiler:
|
||||||
if not spack.compilers.supported(spec.compiler):
|
if not supported_compiler(spec.compiler):
|
||||||
raise UnsupportedCompilerError(spec.compiler.name)
|
raise UnsupportedCompilerError(spec.compiler.name)
|
||||||
|
|
||||||
|
|
||||||
@@ -1320,7 +1333,7 @@ def compiler(self):
|
|||||||
self.expect(ID)
|
self.expect(ID)
|
||||||
self.check_identifier()
|
self.check_identifier()
|
||||||
|
|
||||||
compiler = Compiler.__new__(Compiler)
|
compiler = CompilerSpec.__new__(CompilerSpec)
|
||||||
compiler.name = self.token.value
|
compiler.name = self.token.value
|
||||||
compiler.versions = VersionList()
|
compiler.versions = VersionList()
|
||||||
if self.accept(AT):
|
if self.accept(AT):
|
||||||
@@ -1402,10 +1415,10 @@ def __init__(self, message):
|
|||||||
super(DuplicateVariantError, self).__init__(message)
|
super(DuplicateVariantError, self).__init__(message)
|
||||||
|
|
||||||
|
|
||||||
class DuplicateCompilerError(SpecError):
|
class DuplicateCompilerSpecError(SpecError):
|
||||||
"""Raised when the same compiler occurs in a spec twice."""
|
"""Raised when the same compiler occurs in a spec twice."""
|
||||||
def __init__(self, message):
|
def __init__(self, message):
|
||||||
super(DuplicateCompilerError, self).__init__(message)
|
super(DuplicateCompilerSpecError, self).__init__(message)
|
||||||
|
|
||||||
|
|
||||||
class UnsupportedCompilerError(SpecError):
|
class UnsupportedCompilerError(SpecError):
|
||||||
|
@@ -25,7 +25,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
from spack.spec import Spec, Compiler
|
from spack.spec import Spec, CompilerSpec
|
||||||
from spack.test.mock_packages_test import *
|
from spack.test.mock_packages_test import *
|
||||||
|
|
||||||
class ConcretizeTest(MockPackagesTest):
|
class ConcretizeTest(MockPackagesTest):
|
||||||
@@ -169,7 +169,7 @@ def test_compiler_inheritance(self):
|
|||||||
spec = Spec('mpileaks')
|
spec = Spec('mpileaks')
|
||||||
spec.normalize()
|
spec.normalize()
|
||||||
|
|
||||||
spec['dyninst'].compiler = Compiler('clang')
|
spec['dyninst'].compiler = CompilerSpec('clang')
|
||||||
spec.concretize()
|
spec.concretize()
|
||||||
|
|
||||||
# TODO: not exactly the syntax I would like.
|
# TODO: not exactly the syntax I would like.
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
import spack
|
import spack
|
||||||
import spack.packages as packages
|
import spack.packages as packages
|
||||||
|
from spack.util.naming import mod_to_class
|
||||||
from spack.test.mock_packages_test import *
|
from spack.test.mock_packages_test import *
|
||||||
|
|
||||||
|
|
||||||
@@ -58,8 +59,8 @@ def test_nonexisting_package_filename(self):
|
|||||||
|
|
||||||
|
|
||||||
def test_package_class_names(self):
|
def test_package_class_names(self):
|
||||||
self.assertEqual('Mpich', packages.class_name_for_package_name('mpich'))
|
self.assertEqual('Mpich', mod_to_class('mpich'))
|
||||||
self.assertEqual('PmgrCollective', packages.class_name_for_package_name('pmgr_collective'))
|
self.assertEqual('PmgrCollective', mod_to_class('pmgr_collective'))
|
||||||
self.assertEqual('PmgrCollective', packages.class_name_for_package_name('pmgr-collective'))
|
self.assertEqual('PmgrCollective', mod_to_class('pmgr-collective'))
|
||||||
self.assertEqual('Pmgrcollective', packages.class_name_for_package_name('PmgrCollective'))
|
self.assertEqual('Pmgrcollective', mod_to_class('PmgrCollective'))
|
||||||
self.assertEqual('Num_3db', packages.class_name_for_package_name('3db'))
|
self.assertEqual('_3db', mod_to_class('3db'))
|
||||||
|
@@ -133,12 +133,12 @@ def test_duplicate_depdendence(self):
|
|||||||
self.assertRaises(DuplicateDependencyError, self.check_parse, "x ^y ^y")
|
self.assertRaises(DuplicateDependencyError, self.check_parse, "x ^y ^y")
|
||||||
|
|
||||||
def test_duplicate_compiler(self):
|
def test_duplicate_compiler(self):
|
||||||
self.assertRaises(DuplicateCompilerError, self.check_parse, "x%intel%intel")
|
self.assertRaises(DuplicateCompilerSpecError, self.check_parse, "x%intel%intel")
|
||||||
self.assertRaises(DuplicateCompilerError, self.check_parse, "x%intel%gcc")
|
self.assertRaises(DuplicateCompilerSpecError, self.check_parse, "x%intel%gcc")
|
||||||
self.assertRaises(DuplicateCompilerError, self.check_parse, "x%gcc%intel")
|
self.assertRaises(DuplicateCompilerSpecError, self.check_parse, "x%gcc%intel")
|
||||||
self.assertRaises(DuplicateCompilerError, self.check_parse, "x ^y%intel%intel")
|
self.assertRaises(DuplicateCompilerSpecError, self.check_parse, "x ^y%intel%intel")
|
||||||
self.assertRaises(DuplicateCompilerError, self.check_parse, "x ^y%intel%gcc")
|
self.assertRaises(DuplicateCompilerSpecError, self.check_parse, "x ^y%intel%gcc")
|
||||||
self.assertRaises(DuplicateCompilerError, self.check_parse, "x ^y%gcc%intel")
|
self.assertRaises(DuplicateCompilerSpecError, self.check_parse, "x ^y%gcc%intel")
|
||||||
|
|
||||||
|
|
||||||
# ================================================================================
|
# ================================================================================
|
||||||
|
61
lib/spack/spack/util/naming.py
Normal file
61
lib/spack/spack/util/naming.py
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
# Need this because of spack.util.string
|
||||||
|
from __future__ import absolute_import
|
||||||
|
import string
|
||||||
|
import re
|
||||||
|
|
||||||
|
import spack
|
||||||
|
|
||||||
|
# Valid module names can contain '-' but can't start with it.
|
||||||
|
_valid_module_re = r'^\w[\w-]*$'
|
||||||
|
|
||||||
|
|
||||||
|
def mod_to_class(mod_name):
|
||||||
|
"""Convert a name from module style to class name style. Spack mostly
|
||||||
|
follows `PEP-8 <http://legacy.python.org/dev/peps/pep-0008/>`_:
|
||||||
|
|
||||||
|
* Module and package names use lowercase_with_underscores.
|
||||||
|
* Class names use the CapWords convention.
|
||||||
|
|
||||||
|
Regular source code follows these convetions. Spack is a bit
|
||||||
|
more liberal with its Package names nad Compiler names:
|
||||||
|
|
||||||
|
* They can contain '-' as well as '_', but cannot start with '-'.
|
||||||
|
* They can start with numbers, e.g. "3proxy".
|
||||||
|
|
||||||
|
This function converts from the module convention to the class
|
||||||
|
convention by removing _ and - and converting surrounding
|
||||||
|
lowercase text to CapWords. If mod_name starts with a number,
|
||||||
|
the class name returned will be prepended with '_' to make a
|
||||||
|
valid Python identifier.
|
||||||
|
"""
|
||||||
|
validate_module_name(mod_name)
|
||||||
|
|
||||||
|
class_name = re.sub(r'[-_]+', '-', mod_name)
|
||||||
|
class_name = string.capwords(class_name, '-')
|
||||||
|
class_name = class_name.replace('-', '')
|
||||||
|
|
||||||
|
# If a class starts with a number, prefix it with Number_ to make it a valid
|
||||||
|
# Python class name.
|
||||||
|
if re.match(r'^[0-9]', class_name):
|
||||||
|
class_name = "_%s" % class_name
|
||||||
|
|
||||||
|
return class_name
|
||||||
|
|
||||||
|
|
||||||
|
def valid_module_name(mod_name):
|
||||||
|
"""Return whether the mod_name is valid for use in Spack."""
|
||||||
|
return bool(re.match(_valid_module_re, mod_name))
|
||||||
|
|
||||||
|
|
||||||
|
def validate_module_name(mod_name):
|
||||||
|
"""Raise an exception if mod_name is not valid."""
|
||||||
|
if not valid_module_name(mod_name):
|
||||||
|
raise InvalidModuleNameError(mod_name)
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidModuleNameError(spack.error.SpackError):
|
||||||
|
"""Raised when we encounter a bad module name."""
|
||||||
|
def __init__(self, name):
|
||||||
|
super(InvalidModuleNameError, self).__init__(
|
||||||
|
"Invalid module name: " + name)
|
||||||
|
self.name = name
|
Reference in New Issue
Block a user