diff options
author | Gregory Becker <becker33@llnl.gov> | 2015-11-11 10:35:59 -0800 |
---|---|---|
committer | Gregory Becker <becker33@llnl.gov> | 2015-11-11 10:35:59 -0800 |
commit | 7e6fc79eb240e69ff821899294dc99699ceb1573 (patch) | |
tree | 4c4bf411f87a82ecb8292ce779fcbf48f6e0156c /lib | |
parent | 5347f460b4f2ce8ad64f6ec225779c2a2f944ad4 (diff) | |
parent | e0498ce54ad15787db3c4f187902b424c33013d7 (diff) | |
download | spack-7e6fc79eb240e69ff821899294dc99699ceb1573.tar.gz spack-7e6fc79eb240e69ff821899294dc99699ceb1573.tar.bz2 spack-7e6fc79eb240e69ff821899294dc99699ceb1573.tar.xz spack-7e6fc79eb240e69ff821899294dc99699ceb1573.zip |
Merge branch 'features/spackathon' of github.com:NERSC/spack into crayport
Conflicts:
lib/spack/spack/compiler.py
Diffstat (limited to 'lib')
-rw-r--r-- | lib/spack/spack/__init__.py | 1 | ||||
-rw-r--r-- | lib/spack/spack/architecture.py | 186 | ||||
-rw-r--r-- | lib/spack/spack/architectures/__init__.py | 0 | ||||
-rw-r--r-- | lib/spack/spack/architectures/bgq.py | 19 | ||||
-rw-r--r-- | lib/spack/spack/architectures/cray.py | 22 | ||||
-rw-r--r-- | lib/spack/spack/architectures/linux.py | 17 | ||||
-rw-r--r-- | lib/spack/spack/build_environment.py | 36 | ||||
-rw-r--r-- | lib/spack/spack/cmd/compiler.py | 19 | ||||
-rw-r--r-- | lib/spack/spack/compiler.py | 5 | ||||
-rw-r--r-- | lib/spack/spack/concretize.py | 28 | ||||
-rw-r--r-- | lib/spack/spack/test/__init__.py | 6 | ||||
-rw-r--r-- | lib/spack/spack/test/architecture.py | 19 | ||||
-rw-r--r-- | lib/spack/spack/util/executable.py | 21 | ||||
m--------- | lib/spack/spack/util/python_recipe_parser | 0 |
14 files changed, 324 insertions, 55 deletions
diff --git a/lib/spack/spack/__init__.py b/lib/spack/spack/__init__.py index caa09eb6e0..0f23f61614 100644 --- a/lib/spack/spack/__init__.py +++ b/lib/spack/spack/__init__.py @@ -37,6 +37,7 @@ etc_path = join_path(prefix, "etc") lib_path = join_path(prefix, "lib", "spack") build_env_path = join_path(lib_path, "env") module_path = join_path(lib_path, "spack") +arch_path = join_path(module_path, 'architectures') compilers_path = join_path(module_path, "compilers") test_path = join_path(module_path, "test") hooks_path = join_path(module_path, "hooks") diff --git a/lib/spack/spack/architecture.py b/lib/spack/spack/architecture.py index 0c4b605e91..64416e8b4c 100644 --- a/lib/spack/spack/architecture.py +++ b/lib/spack/spack/architecture.py @@ -23,70 +23,190 @@ # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ############################################################################## import os +import imp import platform as py_platform +import inspect -from llnl.util.lang import memoized +from llnl.util.lang import memoized, list_modules +from llnl.util.filesystem import join_path +import llnl.util.tty as tty import spack +from spack.util.naming import mod_to_class import spack.error as serr from spack.version import Version - +from external import yaml class InvalidSysTypeError(serr.SpackError): def __init__(self, sys_type): - super(InvalidSysTypeError, self).__init__( - "Invalid sys_type value for Spack: " + sys_type) + super(InvalidSysTypeError, self).__init__("Invalid sys_type value for Spack: " + sys_type) class NoSysTypeError(serr.SpackError): def __init__(self): - super(NoSysTypeError, self).__init__( - "Could not determine sys_type for this machine.") + super(NoSysTypeError, self).__init__("Could not determine sys_type for this machine.") + + +class Target(object): + """ Target is the processor of the host machine. The host machine may have different front-end + and back-end targets, especially if it is a Cray machine. The target will have a name and + also the module_name (e.g craype-compiler). Targets will also recognize which architecture + they came from using the set_architecture method. Targets will have compiler finding strategies + """ + default_strategy = None # Can probably add a compiler path here + + def __init__(self,name, module_name=None): + self.name = name # case of cray "ivybridge" but if it's x86_64 + self.module_name = module_name # craype-ivybridge + + def set_architecture(self, architecture): # Target should get the architecture class. + self.architecture = architecture + + @property + def compiler_strategy(self): + if default_strategy: + return default_strategy + elif self.module_name: # If there is a module_name given then use MODULES + return "MODULES" + else: + return "PATH" + +class Architecture(object): + """ Abstract class that each type of Architecture will subclass. Will return a instance of it once it + is returned + """ + + priority = None # Subclass needs to set this number. This controls order in which arch is detected. + front_end = None + back_end = None + default = None # The default back end target. On cray ivybridge + + def __init__(self, name): + self.targets = {} + self.name = name + + def add_target(self, name, target): + """Used by the architecture specific subclass to list available targets. Raises an error + if the architecture specifies a name that is reserved by spack as an alias. + """ + if name in ['front_end', 'fe', 'back_end', 'be', 'default']: + raise ValueError("%s is a spack reserved alias and cannot be the name of a target" % name) + target.set_architecture(self) + self.targets[name] = target + + def target(self, name): + """This is a getter method for the target dictionary that handles defaulting based + on the values provided by default, front-end, and back-end. This can be overwritten + by a subclass for which we want to provide further aliasing options. + """ + if name == 'default': + name = self.default + elif name == 'front_end' or name == 'fe': + name = self.front_end + elif name == 'back_end' or name == 'be': + name = self.back_end + + return self.targets[name] + + @classmethod + def detect(self): + """ Subclass is responsible for implementing this method. + Returns True if the architecture detects if it is the current architecture + and False if it's not. + """ + raise NotImplementedError() + + def __str__(self): + return self.name def get_sys_type_from_spack_globals(): """Return the SYS_TYPE from spack globals, or None if it isn't set.""" if not hasattr(spack, "sys_type"): - return None + return None elif hasattr(spack.sys_type, "__call__"): - return spack.sys_type() + return spack.sys_type() #If in __init__.py there is a sys_type() then call that else: - return spack.sys_type + return spack.sys_type # Else use the attributed which defaults to None -def get_sys_type_from_environment(): - """Return $SYS_TYPE or None if it's not defined.""" - return os.environ.get('SYS_TYPE') +# This is livermore dependent. Hard coded for livermore +#def get_sys_type_from_environment(): +# """Return $SYS_TYPE or None if it's not defined.""" +# return os.environ.get('SYS_TYPE') def get_mac_sys_type(): - """Return a Mac OS SYS_TYPE or None if this isn't a mac.""" + """Return a Mac OS SYS_TYPE or None if this isn't a mac. + Front-end config + """ mac_ver = py_platform.mac_ver()[0] if not mac_ver: return None + return "macosx_%s_%s" % (Version(mac_ver).up_to(2), py_platform.machine()) + + +def get_sys_type_from_uname(): + """ Returns a sys_type from the uname argument + Front-end config + """ + try: + arch_proc = subprocess.Popen(['uname', '-i'], stdout = subprocess.PIPE) + arch, _ = arch_proc.communicate() + return arch.strip() + except: + return None - return "macosx_%s_%s" % ( - Version(mac_ver).up_to(2), py_platform.machine()) +def get_sys_type_from_config_file(): + + spack_home_dir = os.environ["HOME"] + "/.spack" + yaml_file = os.path.join(spack_home_dir, 'architecture.yaml') + try: + config_dict = yaml.load(open(yaml_file)) # Fix this to have yaml.load() + arch = config_dict['architecture'] + front = arch['front'] + back = arch['back'] + return Architecture(front,back) + + except: + print "No architecture.yaml config file found" + return None @memoized -def sys_type(): - """Returns a SysType for the current machine.""" - methods = [get_sys_type_from_spack_globals, - get_sys_type_from_environment, - get_mac_sys_type] +def all_architectures(): + modules = [] + for name in list_modules(spack.arch_path): + mod_name = 'spack.architectures' + name + path = join_path(spack.arch_path, name) + ".py" + mod = imp.load_source(mod_name, path) + class_name = mod_to_class(name) + if not hasattr(mod, class_name): + tty.die('No class %s defined in %s' % (class_name, mod_name)) + cls = getattr(mod, class_name) + if not inspect.isclass(cls): + tty.die('%s.%s is not a class' % (mod_name, class_name)) + + modules.append(cls) + + return modules - # search for a method that doesn't return None - sys_type = None - for method in methods: - sys_type = method() - if sys_type: break - - # Couldn't determine the sys_type for this machine. - if sys_type is None: - return "unknown_arch" - - if not isinstance(sys_type, basestring): - raise InvalidSysTypeError(sys_type) +@memoized +def sys_type(): + """Priority of gathering sys-type. + 1. YAML file that the user specifies the name of the architecture. e.g Cray-XC40 or Cray-XC30 + 2. UNAME + 3. GLOBALS + 4. MAC OSX + Yaml should be a priority here because we want the user to be able to specify the type of architecture to use. + If there is no yaml present then it should move on to the next function and stop immediately once it gets a + arch name + """ + # Try to create an architecture object using the config file FIRST + architecture_list = all_architectures() + architecture_list.sort(key = lambda a: a.priority) + + for arch in architecture_list: + if arch.detect(): + return arch() - return sys_type diff --git a/lib/spack/spack/architectures/__init__.py b/lib/spack/spack/architectures/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/spack/spack/architectures/__init__.py diff --git a/lib/spack/spack/architectures/bgq.py b/lib/spack/spack/architectures/bgq.py new file mode 100644 index 0000000000..d3d4446e09 --- /dev/null +++ b/lib/spack/spack/architectures/bgq.py @@ -0,0 +1,19 @@ +import os + +from spack.architecture import Architecture, Target + +class Bgq(Architecture): + priority = 30 + front_end = 'power7' + back_end = 'powerpc' + default = 'powerpc' + + def __init__(self): + super(Bgq, self).__init__('cray') + self.add_target('power7', Target('power7')) + self.add_target('powerpc', Target('powerpc')) + + @classmethod + def detect(self): + return os.path.exists('/bgsys') + diff --git a/lib/spack/spack/architectures/cray.py b/lib/spack/spack/architectures/cray.py new file mode 100644 index 0000000000..640a3933e0 --- /dev/null +++ b/lib/spack/spack/architectures/cray.py @@ -0,0 +1,22 @@ +import os + +from spack.architecture import Architecture, Target + +class Cray(Architecture): + priority = 20 + front_end = 'sandybridge' + back_end = 'ivybridge' + default = 'ivybridge' + + def __init__(self): + super(Cray, self).__init__('cray') + # Back End compiler needs the proper target module loaded. + self.add_target('ivybridge', Target('ivybridge','craype-ivybridge')) + # Could switch to use modules and fe targets for front end + # Currently using compilers by path for front end. + self.add_target('sandybridge', Target('sandybridge')) + + @classmethod + def detect(self): + return os.path.exists('/opt/cray/craype') + diff --git a/lib/spack/spack/architectures/linux.py b/lib/spack/spack/architectures/linux.py new file mode 100644 index 0000000000..7238575660 --- /dev/null +++ b/lib/spack/spack/architectures/linux.py @@ -0,0 +1,17 @@ +import subprocess +from spack.architecture import Architecture + +class Linux(Architecture): + priority = 60 + front_end = "x86_64" + back_end = "x86_64" + default = "x86_64" + + def __init__(self): + super(Linux, self).__init__('linux') + + @classmethod + def detect(self): + arch = subprocess.Popen(['uname', '-i'], stdout = subprocess.PIPE) + arch, _ = arch.communicate() + return 'x86_64' in arch.strip() diff --git a/lib/spack/spack/build_environment.py b/lib/spack/spack/build_environment.py index 68388958f5..191e858735 100644 --- a/lib/spack/spack/build_environment.py +++ b/lib/spack/spack/build_environment.py @@ -86,6 +86,28 @@ class MakeExecutable(Executable): return super(MakeExecutable, self).__call__(*args, **kwargs) +def load_module(mod): + """Takes a module name and removes modules until it is possible to + load that module. It then loads the provided module. Depends on the + modulecmd implementation of modules used in cray and lmod. + """ + #Create an executable of the module command that will output python code + modulecmd = which('modulecmd') + modulecmd.add_default_arg('python') + + # Read the module and remove any conflicting modules + # We do this without checking that they are already installed + # for ease of programming because unloading a module that is not + # loaded does nothing. + text = modulecmd('show', mod, return_oe=True).split() + for i, word in enumerate(text): + if word == 'conflict': + exec(compile(modulecmd('unload', text[i+1], return_oe=True), '<string>', 'exec')) + # Load the module now that there are no conflicts + load = modulecmd('load', mod, return_oe=True) + exec(compile(load, '<string>', 'exec')) + + def set_compiler_environment_variables(pkg): assert(pkg.spec.concrete) compiler = pkg.compiler @@ -108,11 +130,9 @@ def set_compiler_environment_variables(pkg): os.environ['SPACK_COMPILER_SPEC'] = str(pkg.spec.compiler) - if compiler.PrgEnv: - os.environ['SPACK_CRAYPE'] = compiler.PrgEnv - os.environ['SPACK_COMP_MODULE'] = compiler.module - - + if compiler.modules: + for mod in compiler.modules: + load_module(mod) def set_build_environment_variables(pkg): @@ -163,8 +183,10 @@ def set_build_environment_variables(pkg): pcdir = join_path(p, libdir, 'pkgconfig') if os.path.isdir(pcdir): pkg_config_dirs.append(pcdir) - path_set("PKG_CONFIG_PATH", pkg_config_dirs) + path_put_first("PKG_CONFIG_PATH", pkg_config_dirs) + if pkg.spec.architecture.compiler_strategy.lower() == 'module': + load_module(pkg.spec.architecture.module_name) def set_module_variables_for_package(pkg): """Populate the module scope of install() with some useful functions. @@ -239,8 +261,8 @@ def get_rpaths(pkg): def setup_package(pkg): """Execute all environment setup routines.""" - set_compiler_environment_variables(pkg) set_build_environment_variables(pkg) + set_compiler_environment_variables(pkg) set_module_variables_for_package(pkg) # Allow dependencies to set up environment as well. diff --git a/lib/spack/spack/cmd/compiler.py b/lib/spack/spack/cmd/compiler.py index 2a64dc914e..3e86928977 100644 --- a/lib/spack/spack/cmd/compiler.py +++ b/lib/spack/spack/cmd/compiler.py @@ -29,6 +29,8 @@ from llnl.util.tty.color import colorize from llnl.util.tty.colify import colify from llnl.util.lang import index_by +import spack.architecture +import spack.compiler import spack.compilers import spack.spec import spack.config @@ -38,11 +40,9 @@ from spack.spec import CompilerSpec description = "Manage compilers" def setup_parser(subparser): - sp = subparser.add_subparsers( - metavar='SUBCOMMAND', dest='compiler_command') + sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='compiler_command') - update_parser = sp.add_parser( - 'add', help='Add compilers to the Spack configuration.') + update_parser = sp.add_parser('add', help='Add compilers to the Spack configuration.') update_parser.add_argument('add_paths', nargs=argparse.REMAINDER) remove_parser = sp.add_parser('remove', help='remove compiler') @@ -55,14 +55,17 @@ def setup_parser(subparser): def compiler_add(args): - """Search either $PATH or a list of paths for compilers and add them + """Search either $PATH or a list of paths OR MODULES for compilers and add them to Spack's configuration.""" - paths = args.add_paths + + + paths = args.add_paths # This might be a parser method. Parsing method to add_paths if not paths: paths = get_path('PATH') - + compilers = [c for c in spack.compilers.find_compilers(*args.add_paths) - if c.spec not in spack.compilers.all_compilers()] + if c.spec not in spack.compilers.all_compilers()] + if compilers: spack.compilers.add_compilers_to_config('user', *compilers) diff --git a/lib/spack/spack/compiler.py b/lib/spack/spack/compiler.py index e4bfeea608..67765dfb7b 100644 --- a/lib/spack/spack/compiler.py +++ b/lib/spack/spack/compiler.py @@ -282,6 +282,11 @@ class Compiler(object): modulecmd matches = re.findall(r'(%s)/([^\s(]*)' % cls.PrgEnv_compiler, output) + loaded_modules = os.environ["LOADEDMODULES"].split(":") + #output = _shell('module avail %s' % cls.PrgEnv_compiler) + for module in loaded_modules: + match = re.findall(r'(%s)/([^\s(]*)' % cls.PrgEnv_compiler, module) + for name, version in matches: v = version + '-craype' comp = cls(spack.spec.CompilerSpec(name + '@' + v), diff --git a/lib/spack/spack/concretize.py b/lib/spack/spack/concretize.py index 66002492cb..c5041d67be 100644 --- a/lib/spack/spack/concretize.py +++ b/lib/spack/spack/concretize.py @@ -122,6 +122,34 @@ class DefaultConcretizer(object): return True # changed + def new_concretize_architecture(self, spec): + """If the spec already has an architecture and it is a an architecture type, + return. Otherwise, if it has an architecture that is a string type, generate an + architecture based on that type. If it has no architecture and the root of the + DAG has an architecture, then use that. Otherwise, take the system's default + architecture. + """ + if spec.architecture is not None: + if isinstance(spec.architecture,spack.architecture.Target): + return False + else: + arch = spack.architecture.sys_type() + spec.architecture = arch.target(spec.architecture) + return True #changed + + if spec.root.architecture: + if isinstance(spec.root.architecture,spack.architecture.Target): + spec.architecture = spec.root.architecture + else: + arch = spack.architecture.sys_type() + spec.architecture = arch.target(spec.root.architecture) + else: + arch = spack.architecture.sys_type() + spec.architecture = arch.target('default') + + return True #changed + + def concretize_variants(self, spec): """If the spec already has variants filled in, return. Otherwise, add the default variants from the package specification. diff --git a/lib/spack/spack/test/__init__.py b/lib/spack/spack/test/__init__.py index 6b3715be6f..ed51fac33a 100644 --- a/lib/spack/spack/test/__init__.py +++ b/lib/spack/spack/test/__init__.py @@ -31,7 +31,8 @@ from llnl.util.tty.colify import colify import spack """Names of tests to be included in Spack's test suite""" -test_names = ['versions', +"""test_names = ['architecture', + 'versions', 'url_parse', 'url_substitution', 'packages', @@ -57,7 +58,8 @@ test_names = ['versions', 'optional_deps', 'make_executable', 'configure_guess'] - +""" +test_names = ['architecture'] def list_tests(): """Return names of all tests that can be run for Spack.""" diff --git a/lib/spack/spack/test/architecture.py b/lib/spack/spack/test/architecture.py new file mode 100644 index 0000000000..6ff22aaa59 --- /dev/null +++ b/lib/spack/spack/test/architecture.py @@ -0,0 +1,19 @@ +""" Test checks if the architecture class is created correctly and also that + the functions are looking for the correct architecture name +""" +import unittest +import spack +from spack.architecture import * + +class ArchitectureTest(unittest.TestCase): + + def test_Architecture_class(self): + a = Architecture('Cray-XC40') + a.add_arch_strategy() + self.assertEquals(a.get_arch_dict(), {'Cray-XC40': 'MODULES'}) + + def test_get_sys_type_from_config_file(self): + output_arch_class = get_sys_type_from_config_file() + my_arch_class = Architecture('Linux x86_64','Cray-xc40') + + self.assertEqual(output_arch_class, my_arch_class) diff --git a/lib/spack/spack/util/executable.py b/lib/spack/spack/util/executable.py index d1dfb62ffb..15354089ac 100644 --- a/lib/spack/spack/util/executable.py +++ b/lib/spack/spack/util/executable.py @@ -56,7 +56,11 @@ class Executable(object): def __call__(self, *args, **kwargs): """Run the executable with subprocess.check_output, return output.""" - return_output = kwargs.get("return_output", False) + # Return oe returns a combined stream, setting both output and error + # without setting return oe returns them concatenated by a double line break + return_oe = kwargs.get("return_oe", False) + return_output = True if return_oe else kwargs.get("return_output", False) + return_error = True if return_oe else kwargs.get("return_error", False) fail_on_error = kwargs.get("fail_on_error", True) ignore_errors = kwargs.get("ignore_errors", ()) @@ -95,8 +99,8 @@ class Executable(object): proc = subprocess.Popen( cmd, stdin=input, - stderr=error, - stdout=subprocess.PIPE if return_output else output) + stdout=subprocess.PIPE if return_output else output, + stderr=subprocess.STDOUT if return_oe else (subprocess.PIPE if return_error else error)) out, err = proc.communicate() self.returncode = proc.returncode @@ -104,8 +108,15 @@ class Executable(object): if fail_on_error and rc != 0 and (rc not in ignore_errors): raise ProcessError("Command exited with status %d:" % proc.returncode, cmd_line) - if return_output: - return out + # Return out or error if specified. Return combined stream if requested, + # otherwise return them concatenated by double line break if both requested. + if return_output or return_error: + if return_oe or not return_error: + return out + elif return_output: + return out+'\n\n'+err + else: + return err except OSError, e: raise ProcessError( diff --git a/lib/spack/spack/util/python_recipe_parser b/lib/spack/spack/util/python_recipe_parser new file mode 160000 +Subproject 437a62abb3df7212e3ee20269c0089a0a9766fe |