208 lines
7.3 KiB
Python
208 lines
7.3 KiB
Python
##############################################################################
|
|
# Copyright (c) 2013, Lawrence Livermore National Security, LLC.
|
|
# Produced at the Lawrence Livermore National Laboratory.
|
|
#
|
|
# This file is part of Spack.
|
|
# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
|
|
# LLNL-CODE-647188
|
|
#
|
|
# For details, see https://scalability-llnl.github.io/spack
|
|
# Please also see the LICENSE file for our notice and the LGPL.
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License (as published by
|
|
# the Free Software Foundation) version 2.1 dated February 1999.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
|
|
# conditions of the GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# 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, 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)
|
|
|
|
|
|
class NoSysTypeError(serr.SpackError):
|
|
def __init__(self):
|
|
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):
|
|
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
|
|
elif hasattr(spack.sys_type, "__call__"):
|
|
return spack.sys_type() #If in __init__.py there is a sys_type() then call that
|
|
else:
|
|
return spack.sys_type # Else use the attributed which defaults to None
|
|
|
|
|
|
# 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.
|
|
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
|
|
|
|
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 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
|
|
|
|
@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()
|
|
|