Multi-compiler support feature-complete. Fix SPACK-3, SPACK-4, SPACK-12.
- Fast compiler finding in path and for other directories - first time spack runs, it searches path. - user can add more compilers with 'spack compiler add' - Finds intel, gcc, clang, and pgi compilers with custom version args. - Builds can plug in alternate compilers with ease (e.g. %intel@12.1)
This commit is contained in:
parent
f1bc65c132
commit
33a11f32fd
@ -94,13 +94,13 @@ def set_compiler_environment_variables(pkg):
|
|||||||
|
|
||||||
# Set SPACK compiler variables so that our wrapper knows what to call
|
# Set SPACK compiler variables so that our wrapper knows what to call
|
||||||
if compiler.cc:
|
if compiler.cc:
|
||||||
os.environ['SPACK_CC'] = compiler.cc.command
|
os.environ['SPACK_CC'] = compiler.cc
|
||||||
if compiler.cxx:
|
if compiler.cxx:
|
||||||
os.environ['SPACK_CXX'] = compiler.cxx.command
|
os.environ['SPACK_CXX'] = compiler.cxx
|
||||||
if compiler.f77:
|
if compiler.f77:
|
||||||
os.environ['SPACK_F77'] = compiler.f77.command
|
os.environ['SPACK_F77'] = compiler.f77
|
||||||
if compiler.fc:
|
if compiler.fc:
|
||||||
os.environ['SPACK_FC'] = compiler.fc.command
|
os.environ['SPACK_FC'] = compiler.fc
|
||||||
|
|
||||||
os.environ['SPACK_COMPILER_SPEC'] = str(pkg.spec.compiler)
|
os.environ['SPACK_COMPILER_SPEC'] = str(pkg.spec.compiler)
|
||||||
|
|
||||||
|
81
lib/spack/spack/cmd/compiler.py
Normal file
81
lib/spack/spack/cmd/compiler.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
##############################################################################
|
||||||
|
# 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 argparse
|
||||||
|
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
import llnl.util.tty as tty
|
||||||
|
from llnl.util.tty.colify import colify
|
||||||
|
from llnl.util.lang import index_by
|
||||||
|
|
||||||
|
import spack.compilers
|
||||||
|
import spack.spec
|
||||||
|
import spack.config
|
||||||
|
from spack.compilation import get_path
|
||||||
|
|
||||||
|
description = "Manage compilers"
|
||||||
|
|
||||||
|
def setup_parser(subparser):
|
||||||
|
sp = subparser.add_subparsers(
|
||||||
|
metavar='SUBCOMMAND', dest='compiler_command')
|
||||||
|
|
||||||
|
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')
|
||||||
|
remove_parser.add_argument('path')
|
||||||
|
|
||||||
|
list_parser = sp.add_parser('list', help='list available compilers')
|
||||||
|
|
||||||
|
|
||||||
|
def compiler_add(args):
|
||||||
|
paths = args.add_paths
|
||||||
|
if not paths:
|
||||||
|
paths = get_path('PATH')
|
||||||
|
|
||||||
|
compilers = spack.compilers.find_compilers(*args.add_paths)
|
||||||
|
spack.compilers.add_compilers_to_config('user', *compilers)
|
||||||
|
|
||||||
|
|
||||||
|
def compiler_remove(args):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def compiler_list(args):
|
||||||
|
tty.msg("Available compilers")
|
||||||
|
|
||||||
|
index = index_by(spack.compilers.all_compilers(), 'name')
|
||||||
|
for name, compilers in index.items():
|
||||||
|
tty.hline(name, char='-', color=spack.spec.compiler_color)
|
||||||
|
colify(reversed(sorted(compilers)), indent=4)
|
||||||
|
|
||||||
|
|
||||||
|
def compiler(parser, args):
|
||||||
|
action = { 'add' : compiler_add,
|
||||||
|
'remove' : compiler_remove,
|
||||||
|
'list' : compiler_list }
|
||||||
|
action[args.compiler_command](args)
|
||||||
|
|
@ -26,15 +26,9 @@
|
|||||||
from llnl.util.tty.colify import colify
|
from llnl.util.tty.colify import colify
|
||||||
from llnl.util.lang import index_by
|
from llnl.util.lang import index_by
|
||||||
|
|
||||||
import spack.compilers
|
from spack.cmd.compiler import compiler_list
|
||||||
import spack.spec
|
|
||||||
|
|
||||||
description = "List available compilers"
|
description = "List available compilers. Same as 'spack compiler list'."
|
||||||
|
|
||||||
def compilers(parser, args):
|
def compilers(parser, args):
|
||||||
tty.msg("Available compilers")
|
compiler_list(args)
|
||||||
|
|
||||||
index = index_by(spack.compilers.all_compilers(), 'name')
|
|
||||||
for name, compilers in index.items():
|
|
||||||
tty.hline(name, char='-', color=spack.spec.compiler_color)
|
|
||||||
colify(compilers, indent=4)
|
|
||||||
|
@ -32,9 +32,8 @@
|
|||||||
description = "Get and set configuration options."
|
description = "Get and set configuration options."
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
|
# User can only choose one
|
||||||
scope_group = subparser.add_mutually_exclusive_group()
|
scope_group = subparser.add_mutually_exclusive_group()
|
||||||
|
|
||||||
# File scope
|
|
||||||
scope_group.add_argument(
|
scope_group.add_argument(
|
||||||
'--user', action='store_const', const='user', dest='scope',
|
'--user', action='store_const', const='user', dest='scope',
|
||||||
help="Use config file in user home directory (default).")
|
help="Use config file in user home directory (default).")
|
||||||
@ -42,36 +41,44 @@ def setup_parser(subparser):
|
|||||||
'--site', action='store_const', const='site', dest='scope',
|
'--site', action='store_const', const='site', dest='scope',
|
||||||
help="Use config file in spack prefix.")
|
help="Use config file in spack prefix.")
|
||||||
|
|
||||||
# Get (vs. default set)
|
sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='config_command')
|
||||||
subparser.add_argument(
|
|
||||||
'--get', action='store_true', dest='get',
|
|
||||||
help="Get the value associated with a key.")
|
|
||||||
|
|
||||||
# positional arguments (value is only used on set)
|
set_parser = sp.add_parser('set', help='Set configuration values.')
|
||||||
subparser.add_argument(
|
set_parser.add_argument('key', help="Key to set value for.")
|
||||||
'key', help="Get the value associated with KEY")
|
set_parser.add_argument('value', nargs='?', default=None,
|
||||||
subparser.add_argument(
|
help="Value to associate with key")
|
||||||
'value', nargs='?', default=None,
|
|
||||||
help="Value to associate with key")
|
get_parser = sp.add_parser('get', help='Get configuration values.')
|
||||||
|
get_parser.add_argument('key', help="Key to get value for.")
|
||||||
|
|
||||||
|
edit_parser = sp.add_parser('edit', help='Edit configuration file.')
|
||||||
|
|
||||||
|
|
||||||
|
def config_set(args):
|
||||||
|
# default scope for writing is 'user'
|
||||||
|
if not args.scope:
|
||||||
|
args.scope = 'user'
|
||||||
|
|
||||||
|
config = spack.config.get_config(args.scope)
|
||||||
|
config.set_value(args.key, args.value)
|
||||||
|
config.write()
|
||||||
|
|
||||||
|
|
||||||
|
def config_get(args):
|
||||||
|
config = spack.config.get_config(args.scope)
|
||||||
|
print config.get_value(args.key)
|
||||||
|
|
||||||
|
|
||||||
|
def config_edit(args):
|
||||||
|
if not args.scope:
|
||||||
|
args.scope = 'user'
|
||||||
|
config_file = spack.config.get_filename(args.scope)
|
||||||
|
spack.editor(config_file)
|
||||||
|
|
||||||
|
|
||||||
def config(parser, args):
|
def config(parser, args):
|
||||||
key, value = args.key, args.value
|
action = { 'set' : config_set,
|
||||||
|
'get' : config_get,
|
||||||
|
'edit' : config_edit }
|
||||||
|
action[args.config_command](args)
|
||||||
|
|
||||||
# If we're writing need to do a few checks.
|
|
||||||
if not args.get:
|
|
||||||
# Default scope for writing is user scope.
|
|
||||||
if not args.scope:
|
|
||||||
args.scope = 'user'
|
|
||||||
|
|
||||||
if args.value is None:
|
|
||||||
tty.die("No value for '%s'. " % args.key
|
|
||||||
+ "Spack config requires a key and a value.")
|
|
||||||
|
|
||||||
config = spack.config.get_config(args.scope)
|
|
||||||
|
|
||||||
if args.get:
|
|
||||||
print config.get_value(key)
|
|
||||||
else:
|
|
||||||
config.set_value(key, value)
|
|
||||||
config.write()
|
|
||||||
|
@ -1,26 +1,67 @@
|
|||||||
|
##############################################################################
|
||||||
|
# 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 os
|
||||||
import re
|
import re
|
||||||
import itertools
|
import itertools
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
import llnl.util.tty as tty
|
||||||
from llnl.util.lang import memoized
|
from llnl.util.lang import memoized
|
||||||
from llnl.util.filesystem import join_path
|
from llnl.util.filesystem import join_path
|
||||||
|
|
||||||
import spack.error
|
import spack.error
|
||||||
import spack.spec
|
import spack.spec
|
||||||
|
from spack.util.multiproc import parmap
|
||||||
|
from spack.util.executable import *
|
||||||
from spack.version import Version
|
from spack.version import Version
|
||||||
from spack.util.executable import Executable, which
|
|
||||||
from spack.compilation import get_path
|
from spack.compilation import get_path
|
||||||
|
|
||||||
_default_order = ['']
|
__all__ = ['Compiler', 'get_compiler_version']
|
||||||
|
|
||||||
def _verify_executables(*paths):
|
def _verify_executables(*paths):
|
||||||
for path in paths:
|
for path in paths:
|
||||||
if not os.path.isfile(path) and os.access(path, os.X_OK):
|
if not os.path.isfile(path) and os.access(path, os.X_OK):
|
||||||
raise CompilerAccessError(path)
|
raise CompilerAccessError(path)
|
||||||
|
|
||||||
@memoized
|
|
||||||
def get_compiler_version(compiler, version_arg):
|
_version_cache = {}
|
||||||
return compiler(version_arg, return_output=True)
|
|
||||||
|
def get_compiler_version(compiler_path, version_arg, regex='(.*)'):
|
||||||
|
if not compiler_path in _version_cache:
|
||||||
|
compiler = Executable(compiler_path)
|
||||||
|
output = compiler(version_arg, return_output=True, error=None)
|
||||||
|
|
||||||
|
match = re.search(regex, output)
|
||||||
|
_version_cache[compiler_path] = match.group(1) if match else None
|
||||||
|
|
||||||
|
return _version_cache[compiler_path]
|
||||||
|
|
||||||
|
|
||||||
|
def dumpversion(compiler_path):
|
||||||
|
"""Simple default dumpversion method -- this is what gcc does."""
|
||||||
|
return get_compiler_version(compiler_path, '-dumpversion')
|
||||||
|
|
||||||
|
|
||||||
class Compiler(object):
|
class Compiler(object):
|
||||||
@ -48,37 +89,67 @@ class Compiler(object):
|
|||||||
# Optional suffix regexes for searching for this type of compiler.
|
# Optional suffix regexes for searching for this type of compiler.
|
||||||
# Suffixes are used by some frameworks, e.g. macports uses an '-mp-X.Y'
|
# Suffixes are used by some frameworks, e.g. macports uses an '-mp-X.Y'
|
||||||
# version suffix for gcc.
|
# version suffix for gcc.
|
||||||
suffixes = []
|
suffixes = [r'-.*']
|
||||||
|
|
||||||
# Names of generic arguments used by this compiler
|
# Names of generic arguments used by this compiler
|
||||||
arg_version = '-dumpversion'
|
|
||||||
arg_rpath = '-Wl,-rpath,%s'
|
arg_rpath = '-Wl,-rpath,%s'
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, cc, cxx, f77, fc):
|
def __init__(self, cc, cxx, f77, fc, version=None):
|
||||||
def make_exe(exe):
|
def check(exe):
|
||||||
if exe is None:
|
if exe is None:
|
||||||
return None
|
return None
|
||||||
_verify_executables(exe)
|
_verify_executables(exe)
|
||||||
return Executable(exe)
|
return exe
|
||||||
|
|
||||||
self.cc = make_exe(cc)
|
self.cc = check(cc)
|
||||||
self.cxx = make_exe(cxx)
|
self.cxx = check(cxx)
|
||||||
self.f77 = make_exe(f77)
|
self.f77 = check(f77)
|
||||||
self.fc = make_exe(fc)
|
self.fc = check(fc)
|
||||||
|
|
||||||
|
# Allow versions to be memoized so we don't have to run
|
||||||
def _tuple(self):
|
# compilers many times. Record them in the version cache if
|
||||||
return (self.cc, self.cxx, self.f77, self.fc)
|
# we get them in a constructor
|
||||||
|
#
|
||||||
|
# TODO: what to do if compilers have different versions?
|
||||||
|
#
|
||||||
|
self._version = version
|
||||||
|
self._cache_version()
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def version(self):
|
def version(self):
|
||||||
for comp in self._tuple():
|
if not self._version:
|
||||||
if comp is not None:
|
v = self.cc_version(self.cc)
|
||||||
v = get_compiler_version(comp, self.arg_version)
|
if v is not None:
|
||||||
|
self._version = v
|
||||||
return Version(v)
|
return Version(v)
|
||||||
raise InvalidCompilerError()
|
|
||||||
|
v = self.cxx_version(self.cxx)
|
||||||
|
if v is not None:
|
||||||
|
self._version = v
|
||||||
|
return Version(v)
|
||||||
|
|
||||||
|
v = self.f77_version(self.f77)
|
||||||
|
if v is not None:
|
||||||
|
self._version = v
|
||||||
|
return Version(v)
|
||||||
|
|
||||||
|
v = self.fc_version(self.fc)
|
||||||
|
if v is not None:
|
||||||
|
self._version = v
|
||||||
|
return Version(v)
|
||||||
|
|
||||||
|
raise InvalidCompilerError()
|
||||||
|
|
||||||
|
return Version(self._version)
|
||||||
|
|
||||||
|
|
||||||
|
def _cache_version(self):
|
||||||
|
_version_cache[self.cc] = self._version
|
||||||
|
_version_cache[self.cxx] = self._version
|
||||||
|
_version_cache[self.f77] = self._version
|
||||||
|
_version_cache[self.fc] = self._version
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -87,66 +158,115 @@ def spec(self):
|
|||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _find_matches_in_path(cls, compiler_names, *path):
|
def default_version(cls, cc):
|
||||||
"""Try to find compilers with the supplied names in any of the suppled
|
"""Override just this to override all compiler version functions."""
|
||||||
paths. Searches for all combinations of each name with the
|
return dumpversion(cc)
|
||||||
compiler's specified prefixes and suffixes. Compilers are
|
|
||||||
returned in a dict from (prefix, suffix) tuples to paths to
|
@classmethod
|
||||||
the compilers with those prefixes and suffixes.
|
def cc_version(cls, cc):
|
||||||
|
return cls.default_version(cc)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def cxx_version(cls, cxx):
|
||||||
|
return cls.default_version(cxx)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def f77_version(cls, f77):
|
||||||
|
return cls.default_version(f77)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def fc_version(cls, fc):
|
||||||
|
return cls.default_version(fc)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _find_matches_in_path(cls, compiler_names, detect_version, *path):
|
||||||
|
"""Finds compilers in the paths supplied.
|
||||||
|
|
||||||
|
Looks for all combinations of ``compiler_names`` with the
|
||||||
|
``prefixes`` and ``suffixes`` defined for this compiler
|
||||||
|
class. If any compilers match the compiler_names,
|
||||||
|
prefixes, or suffixes, uses ``detect_version`` to figure
|
||||||
|
out what version the compiler is.
|
||||||
|
|
||||||
|
This returns a dict with compilers grouped by (prefix,
|
||||||
|
suffix, version) tuples. This can be further organized by
|
||||||
|
find().
|
||||||
"""
|
"""
|
||||||
if not path:
|
if not path:
|
||||||
path = get_path('PATH')
|
path = get_path('PATH')
|
||||||
|
|
||||||
matches = {}
|
prefixes = [''] + cls.prefixes
|
||||||
ps_pairs = [p for p in itertools.product(
|
suffixes = [''] + cls.suffixes
|
||||||
[''] + cls.prefixes, [''] + cls.suffixes)]
|
|
||||||
|
|
||||||
|
checks = []
|
||||||
for directory in path:
|
for directory in path:
|
||||||
files = os.listdir(directory)
|
files = os.listdir(directory)
|
||||||
|
for exe in files:
|
||||||
|
full_path = join_path(directory, exe)
|
||||||
|
|
||||||
for pre_re, suf_re in ps_pairs:
|
prod = itertools.product(prefixes, compiler_names, suffixes)
|
||||||
for compiler_name in compiler_names:
|
for pre, name, suf in prod:
|
||||||
regex = r'^(%s)%s(%s)$' % (
|
regex = r'^(%s)%s(%s)$' % (pre, re.escape(name), suf)
|
||||||
pre_re, re.escape(compiler_name), suf_re)
|
|
||||||
|
|
||||||
for exe in files:
|
match = re.match(regex, exe)
|
||||||
match = re.match(regex, exe)
|
if match:
|
||||||
if match:
|
key = (full_path,) + match.groups()
|
||||||
pair = match.groups()
|
checks.append(key)
|
||||||
if pair not in matches:
|
|
||||||
matches[pair] = join_path(directory, exe)
|
|
||||||
|
|
||||||
return matches
|
def check(key):
|
||||||
|
try:
|
||||||
|
full_path, prefix, suffix = key
|
||||||
|
version = detect_version(full_path)
|
||||||
|
return (version, prefix, suffix, full_path)
|
||||||
|
except ProcessError, e:
|
||||||
|
tty.debug("Couldn't get version for compiler %s" % full_path, e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
successful = [key for key in parmap(check, checks) if key is not None]
|
||||||
|
return { (v, p, s) : path for v, p, s, path in successful }
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def find(cls, *path):
|
def find(cls, *path):
|
||||||
"""Try to find this type of compiler in the user's environment. For
|
"""Try to find this type of compiler in the user's
|
||||||
each set of compilers found, this returns a 4-tuple with
|
environment. For each set of compilers found, this returns
|
||||||
the cc, cxx, f77, and fc paths.
|
compiler objects with the cc, cxx, f77, fc paths and the
|
||||||
|
version filled in.
|
||||||
|
|
||||||
This will search for compilers with the names in cc_names,
|
This will search for compilers with the names in cc_names,
|
||||||
cxx_names, etc. and it will group 4-tuples if they have
|
cxx_names, etc. and it will group them if they have common
|
||||||
common prefixes and suffixes. e.g., gcc-mp-4.7 would be
|
prefixes, suffixes, and versions. e.g., gcc-mp-4.7 would
|
||||||
grouped with g++-mp-4.7 and gfortran-mp-4.7.
|
be grouped with g++-mp-4.7 and gfortran-mp-4.7.
|
||||||
|
|
||||||
Example return values::
|
Example return values::
|
||||||
|
|
||||||
[ ('/usr/bin/gcc', '/usr/bin/g++',
|
[ gcc('/usr/bin/gcc', '/usr/bin/g++',
|
||||||
'/usr/bin/gfortran', '/usr/bin/gfortran'),
|
'/usr/bin/gfortran', '/usr/bin/gfortran',
|
||||||
|
Version('4.4.5')),
|
||||||
('/usr/bin/gcc-mp-4.5', '/usr/bin/g++-mp-4.5',
|
gcc('/usr/bin/gcc-mp-4.5', '/usr/bin/g++-mp-4.5',
|
||||||
'/usr/bin/gfortran-mp-4.5', '/usr/bin/gfortran-mp-4.5') ]
|
'/usr/bin/gfortran-mp-4.5', '/usr/bin/gfortran-mp-4.5',
|
||||||
|
Version('4.7.2')) ]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
pair_names = [cls._find_matches_in_path(names, *path) for names in (
|
dicts = parmap(
|
||||||
cls.cc_names, cls.cxx_names, cls.f77_names, cls.fc_names)]
|
lambda t: cls._find_matches_in_path(*t),
|
||||||
|
[(cls.cc_names, cls.cc_version) + tuple(path),
|
||||||
|
(cls.cxx_names, cls.cxx_version) + tuple(path),
|
||||||
|
(cls.f77_names, cls.f77_version) + tuple(path),
|
||||||
|
(cls.fc_names, cls.fc_version) + tuple(path)])
|
||||||
|
|
||||||
keys = set()
|
all_keys = set()
|
||||||
for p in pair_names:
|
for d in dicts:
|
||||||
keys.update(p)
|
all_keys.update(d)
|
||||||
|
|
||||||
return [ tuple(pn[k] if k in pn else None for pn in pair_names)
|
compilers = []
|
||||||
for k in keys ]
|
for k in all_keys:
|
||||||
|
ver, pre, suf = k
|
||||||
|
paths = tuple(pn[k] if k in pn else None for pn in dicts)
|
||||||
|
args = paths + (ver,)
|
||||||
|
compilers.append(cls(*args))
|
||||||
|
|
||||||
|
return compilers
|
||||||
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -156,11 +276,8 @@ def __repr__(self):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Return a string represntation of the compiler toolchain."""
|
"""Return a string represntation of the compiler toolchain."""
|
||||||
def p(path):
|
return "%s(%s)" % (
|
||||||
return ' '.join(path.exe) if path else None
|
self.name, '\n '.join((str(s) for s in (self.cc, self.cxx, self.f77, self.fc))))
|
||||||
return "%s(%s, %s, %s, %s)" % (
|
|
||||||
self.name,
|
|
||||||
p(self.cc), p(self.cxx), p(self.f77), p(self.fc))
|
|
||||||
|
|
||||||
|
|
||||||
class CompilerAccessError(spack.error.SpackError):
|
class CompilerAccessError(spack.error.SpackError):
|
||||||
|
@ -22,7 +22,11 @@
|
|||||||
# 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
|
||||||
##############################################################################
|
##############################################################################
|
||||||
|
"""This module contains functions related to finding compilers on the
|
||||||
|
system and configuring Spack to use multiple compilers.
|
||||||
|
"""
|
||||||
import imp
|
import imp
|
||||||
|
import os
|
||||||
|
|
||||||
from llnl.util.lang import memoized, list_modules
|
from llnl.util.lang import memoized, list_modules
|
||||||
from llnl.util.filesystem import join_path
|
from llnl.util.filesystem import join_path
|
||||||
@ -32,13 +36,16 @@
|
|||||||
import spack.spec
|
import spack.spec
|
||||||
import spack.config
|
import spack.config
|
||||||
|
|
||||||
|
from spack.util.multiproc import parmap
|
||||||
from spack.compiler import Compiler
|
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
|
from spack.util.naming import mod_to_class
|
||||||
|
from spack.compilation import get_path
|
||||||
|
|
||||||
_imported_compilers_module = 'spack.compilers'
|
_imported_compilers_module = 'spack.compilers'
|
||||||
_required_instance_vars = ['cc', 'cxx', 'f77', 'fc']
|
_required_instance_vars = ['cc', 'cxx', 'f77', 'fc']
|
||||||
|
|
||||||
|
_default_order = ['gcc', 'intel', 'pgi', 'clang']
|
||||||
|
|
||||||
def _auto_compiler_spec(function):
|
def _auto_compiler_spec(function):
|
||||||
def converter(cspec_like):
|
def converter(cspec_like):
|
||||||
@ -59,23 +66,72 @@ def _get_config():
|
|||||||
if existing:
|
if existing:
|
||||||
return config
|
return config
|
||||||
|
|
||||||
user_config = spack.config.get_config('user')
|
compilers = find_compilers(*get_path('PATH'))
|
||||||
|
new_compilers = [
|
||||||
compilers = find_default_compilers()
|
c for c in compilers if c.spec not in existing]
|
||||||
for name, clist in compilers.items():
|
add_compilers_to_config('user', *new_compilers)
|
||||||
for compiler in clist:
|
|
||||||
if compiler.spec not in existing:
|
|
||||||
add_compiler(user_config, compiler)
|
|
||||||
user_config.write()
|
|
||||||
|
|
||||||
# After writing compilers to the user config, return a full config
|
# After writing compilers to the user config, return a full config
|
||||||
# from all files.
|
# from all files.
|
||||||
return spack.config.get_config()
|
return spack.config.get_config(refresh=True)
|
||||||
|
|
||||||
|
|
||||||
|
@memoized
|
||||||
|
def default_compiler():
|
||||||
|
versions = []
|
||||||
|
for name in _default_order: # TODO: customize order.
|
||||||
|
versions = find(name)
|
||||||
|
if versions: break
|
||||||
|
|
||||||
|
if not versions:
|
||||||
|
raise NoCompilersError()
|
||||||
|
|
||||||
|
return sorted(versions)[-1]
|
||||||
|
|
||||||
|
|
||||||
|
def find_compilers(*path):
|
||||||
|
"""Return a list of compilers found in the suppied paths.
|
||||||
|
This invokes the find() method for each Compiler class,
|
||||||
|
and appends the compilers detected to a list.
|
||||||
|
"""
|
||||||
|
# Make sure path elements exist, and include /bin directories
|
||||||
|
# under prefixes.
|
||||||
|
filtered_path = []
|
||||||
|
for p in path:
|
||||||
|
# Eliminate symlinks and just take the real directories.
|
||||||
|
p = os.path.realpath(p)
|
||||||
|
if not os.path.isdir(p):
|
||||||
|
continue
|
||||||
|
filtered_path.append(p)
|
||||||
|
|
||||||
|
# Check for a bin directory, add it if it exists
|
||||||
|
bin = join_path(p, 'bin')
|
||||||
|
if os.path.isdir(bin):
|
||||||
|
filtered_path.append(os.path.realpath(bin))
|
||||||
|
|
||||||
|
# Once the paths are cleaned up, do a search for each type of
|
||||||
|
# compiler. We can spawn a bunch of parallel searches to reduce
|
||||||
|
# the overhead of spelunking all these directories.
|
||||||
|
types = all_compiler_types()
|
||||||
|
compiler_lists = parmap(lambda cls: cls.find(*filtered_path), types)
|
||||||
|
|
||||||
|
# ensure all the version calls we made are cached in the parent
|
||||||
|
# process, as well. This speeds up Spack a lot.
|
||||||
|
clist = reduce(lambda x,y: x+y, compiler_lists)
|
||||||
|
for c in clist: c._cache_version()
|
||||||
|
return clist
|
||||||
|
|
||||||
|
|
||||||
|
def add_compilers_to_config(scope, *compilers):
|
||||||
|
config = spack.config.get_config(scope)
|
||||||
|
for compiler in compilers:
|
||||||
|
add_compiler(config, compiler)
|
||||||
|
config.write()
|
||||||
|
|
||||||
|
|
||||||
def add_compiler(config, compiler):
|
def add_compiler(config, compiler):
|
||||||
def setup_field(cspec, name, exe):
|
def setup_field(cspec, name, exe):
|
||||||
path = ' '.join(exe.exe) if exe else "None"
|
path = exe if exe else "None"
|
||||||
config.set_value('compiler', cspec, name, path)
|
config.set_value('compiler', cspec, name, path)
|
||||||
|
|
||||||
for c in _required_instance_vars:
|
for c in _required_instance_vars:
|
||||||
@ -134,7 +190,9 @@ def get_compiler(cspec):
|
|||||||
compiler_paths.append(compiler_path)
|
compiler_paths.append(compiler_path)
|
||||||
else:
|
else:
|
||||||
compiler_paths.append(None)
|
compiler_paths.append(None)
|
||||||
return cls(*compiler_paths)
|
|
||||||
|
args = tuple(compiler_paths) + (compiler_spec.version,)
|
||||||
|
return cls(*args)
|
||||||
|
|
||||||
matches = find(compiler_spec)
|
matches = find(compiler_spec)
|
||||||
return [get_compiler(cspec) for cspec in matches]
|
return [get_compiler(cspec) for cspec in matches]
|
||||||
@ -168,34 +226,14 @@ def all_compiler_types():
|
|||||||
return [class_for_compiler_name(c) for c in supported_compilers()]
|
return [class_for_compiler_name(c) for c in supported_compilers()]
|
||||||
|
|
||||||
|
|
||||||
def find_default_compilers():
|
|
||||||
"""Search the user's environment to get default compilers. Each
|
|
||||||
compiler class can have its own find() class method that can be
|
|
||||||
customized to locate that type of compiler.
|
|
||||||
"""
|
|
||||||
# Compiler name is inserted on load by class_for_compiler_name
|
|
||||||
return {
|
|
||||||
Compiler.name : [Compiler(*c) for c in Compiler.find()]
|
|
||||||
for Compiler in all_compiler_types() }
|
|
||||||
|
|
||||||
|
|
||||||
@memoized
|
|
||||||
def default_compiler():
|
|
||||||
"""Get the spec for the default compiler on the system.
|
|
||||||
Currently just returns the system's default gcc.
|
|
||||||
|
|
||||||
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)
|
|
||||||
version = gcc('-dumpversion', return_output=True)
|
|
||||||
return spack.spec.CompilerSpec('gcc', version)
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidCompilerConfigurationError(spack.error.SpackError):
|
class InvalidCompilerConfigurationError(spack.error.SpackError):
|
||||||
def __init__(self, compiler_spec):
|
def __init__(self, compiler_spec):
|
||||||
super(InvalidCompilerConfigurationError, self).__init__(
|
super(InvalidCompilerConfigurationError, self).__init__(
|
||||||
"Invalid configuration for [compiler \"%s\"]: " % compiler_spec,
|
"Invalid configuration for [compiler \"%s\"]: " % compiler_spec,
|
||||||
"Compiler configuration must contain entries for all compilers: %s"
|
"Compiler configuration must contain entries for all compilers: %s"
|
||||||
% _required_instance_vars)
|
% _required_instance_vars)
|
||||||
|
|
||||||
|
|
||||||
|
class NoCompilersError(spack.error.SpackError):
|
||||||
|
def __init__(self):
|
||||||
|
super(NoCompilersError, self).__init__("Spack could not find any compilers!")
|
||||||
|
@ -37,5 +37,16 @@ class Clang(Compiler):
|
|||||||
# Subclasses use possible names of Fortran 90 compiler
|
# Subclasses use possible names of Fortran 90 compiler
|
||||||
fc_names = []
|
fc_names = []
|
||||||
|
|
||||||
def __init__(self, cc, cxx, f77, fc):
|
|
||||||
super(Clang, self).__init__(cc, cxx, f77, fc)
|
@classmethod
|
||||||
|
def default_version(self, comp):
|
||||||
|
"""The '--version' option works for clang compilers.
|
||||||
|
Output looks like this::
|
||||||
|
|
||||||
|
clang version 3.1 (trunk 149096)
|
||||||
|
Target: x86_64-unknown-linux-gnu
|
||||||
|
Thread model: posix
|
||||||
|
"""
|
||||||
|
return get_compiler_version(
|
||||||
|
comp, '--version', r'clang version ([^ ]+)')
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
# 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
|
||||||
##############################################################################
|
##############################################################################
|
||||||
from spack.compiler import Compiler
|
from spack.compiler import *
|
||||||
|
|
||||||
class Gcc(Compiler):
|
class Gcc(Compiler):
|
||||||
# Subclasses use possible names of C compiler
|
# Subclasses use possible names of C compiler
|
||||||
@ -40,5 +40,14 @@ class Gcc(Compiler):
|
|||||||
# MacPorts builds gcc versions with prefixes and -mp-X.Y suffixes.
|
# MacPorts builds gcc versions with prefixes and -mp-X.Y suffixes.
|
||||||
suffixes = [r'-mp-\d\.\d']
|
suffixes = [r'-mp-\d\.\d']
|
||||||
|
|
||||||
def __init__(self, cc, cxx, f77, fc):
|
@classmethod
|
||||||
super(Gcc, self).__init__(cc, cxx, f77, fc)
|
def fc_version(cls, fc):
|
||||||
|
return get_compiler_version(
|
||||||
|
fc, '-dumpversion',
|
||||||
|
# older gfortran versions don't have simple dumpversion output.
|
||||||
|
r'(?:GNU Fortran \(GCC\))?(\d+\.\d+\.\d+)')
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def f77_version(cls, f77):
|
||||||
|
return cls.fc_version(f77)
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
# 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
|
||||||
##############################################################################
|
##############################################################################
|
||||||
from spack.compiler import Compiler
|
from spack.compiler import *
|
||||||
|
|
||||||
class Intel(Compiler):
|
class Intel(Compiler):
|
||||||
# Subclasses use possible names of C compiler
|
# Subclasses use possible names of C compiler
|
||||||
@ -37,5 +37,21 @@ class Intel(Compiler):
|
|||||||
# Subclasses use possible names of Fortran 90 compiler
|
# Subclasses use possible names of Fortran 90 compiler
|
||||||
fc_names = ['ifort']
|
fc_names = ['ifort']
|
||||||
|
|
||||||
def __init__(self, cc, cxx, f77, fc):
|
|
||||||
super(Intel, self).__init__(cc, cxx, f77, fc)
|
@classmethod
|
||||||
|
def default_version(cls, comp):
|
||||||
|
"""The '--version' option seems to be the most consistent one
|
||||||
|
for intel compilers. Output looks like this::
|
||||||
|
|
||||||
|
icpc (ICC) 12.1.5 20120612
|
||||||
|
Copyright (C) 1985-2012 Intel Corporation. All rights reserved.
|
||||||
|
|
||||||
|
or::
|
||||||
|
|
||||||
|
ifort (IFORT) 12.1.5 20120612
|
||||||
|
Copyright (C) 1985-2012 Intel Corporation. All rights reserved.
|
||||||
|
"""
|
||||||
|
return get_compiler_version(
|
||||||
|
comp, '--version', r'\((?:IFORT|ICC)\) ([^ ]+)')
|
||||||
|
|
||||||
|
|
||||||
|
51
lib/spack/spack/compilers/pgi.py
Normal file
51
lib/spack/spack/compilers/pgi.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
##############################################################################
|
||||||
|
# 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
|
||||||
|
##############################################################################
|
||||||
|
from spack.compiler import *
|
||||||
|
|
||||||
|
class Pgi(Compiler):
|
||||||
|
# Subclasses use possible names of C compiler
|
||||||
|
cc_names = ['pgcc']
|
||||||
|
|
||||||
|
# Subclasses use possible names of C++ compiler
|
||||||
|
cxx_names = ['pgCC']
|
||||||
|
|
||||||
|
# Subclasses use possible names of Fortran 77 compiler
|
||||||
|
f77_names = ['pgf77']
|
||||||
|
|
||||||
|
# Subclasses use possible names of Fortran 90 compiler
|
||||||
|
fc_names = ['pgf95', 'pgf90']
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def default_version(cls, comp):
|
||||||
|
"""The '-V' option works for all the PGI compilers.
|
||||||
|
Output looks like this::
|
||||||
|
|
||||||
|
pgf95 10.2-0 64-bit target on x86-64 Linux -tp nehalem-64
|
||||||
|
Copyright 1989-2000, The Portland Group, Inc. All Rights Reserved.
|
||||||
|
Copyright 2000-2010, STMicroelectronics, Inc. All Rights Reserved.
|
||||||
|
"""
|
||||||
|
return get_compiler_version(
|
||||||
|
comp, '-V', r'pg[^ ]* ([^ ]+) \d\d\d?-bit target')
|
||||||
|
|
45
lib/spack/spack/util/multiproc.py
Normal file
45
lib/spack/spack/util/multiproc.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
##############################################################################
|
||||||
|
# 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
|
||||||
|
##############################################################################
|
||||||
|
"""
|
||||||
|
This implements a parallel map operation but it can accept more values
|
||||||
|
than multiprocessing.Pool.apply() can. For example, apply() will fail
|
||||||
|
to pickle functions if they're passed indirectly as parameters.
|
||||||
|
"""
|
||||||
|
from multiprocessing import Process, Pipe
|
||||||
|
from itertools import izip
|
||||||
|
|
||||||
|
def spawn(f):
|
||||||
|
def fun(pipe,x):
|
||||||
|
pipe.send(f(x))
|
||||||
|
pipe.close()
|
||||||
|
return fun
|
||||||
|
|
||||||
|
def parmap(f,X):
|
||||||
|
pipe=[Pipe() for x in X]
|
||||||
|
proc=[Process(target=spawn(f),args=(c,x)) for x,(p,c) in izip(X,pipe)]
|
||||||
|
[p.start() for p in proc]
|
||||||
|
[p.join() for p in proc]
|
||||||
|
return [p.recv() for (p,c) in pipe]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user