Simplify the detection protocol for packages
Packages can implement “detect_version” to support detection of external instances of a package. This is generally easier than implementing “determine_spec_details”. The API for determine_version is similar: for example you can return “None” to indicate that an executable is not an instance of a package. Users may implement a “determine_variants” method for a package. When doing external detection, executables are grouped by version and each group results in a single invocation of “determine_variants” for the associated spec. The method returns a string specifying the variants for the package. The method may additionally return a dictionary representing extra attributes for the package. These will be stored in the spec yaml and can be retrieved from self.spec.extra_attributes The Spack GCC package has been updated with an implementation of “determine_variants” which adds the following extra attributes to the package: c, cxx, fortran
This commit is contained in:

committed by
Peter Scheibel

parent
193e8333fa
commit
c0d490ffbe
@@ -2,10 +2,6 @@
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
from spack import *
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
@@ -28,23 +24,13 @@ class Automake(AutotoolsPackage, GNUMirrorPackage):
|
||||
|
||||
build_directory = 'spack-build'
|
||||
|
||||
executables = ['automake']
|
||||
executables = ['^automake$']
|
||||
|
||||
@classmethod
|
||||
def determine_spec_details(cls, prefix, exes_in_prefix):
|
||||
exe_to_path = dict(
|
||||
(os.path.basename(p), p) for p in exes_in_prefix
|
||||
)
|
||||
if 'automake' not in exe_to_path:
|
||||
return None
|
||||
|
||||
exe = spack.util.executable.Executable(exe_to_path['automake'])
|
||||
output = exe('--version', output=str)
|
||||
if output:
|
||||
match = re.search(r'GNU automake\)\s+(\S+)', output)
|
||||
if match:
|
||||
version_str = match.group(1)
|
||||
return Spec('automake@{0}'.format(version_str))
|
||||
def determine_version(cls, exe):
|
||||
output = Executable(exe)('--version', output=str)
|
||||
match = re.search(r'GNU automake\)\s+(\S+)', output)
|
||||
return match.group(1) if match else None
|
||||
|
||||
def patch(self):
|
||||
# The full perl shebang might be too long
|
||||
|
@@ -2,21 +2,18 @@
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
from spack import *
|
||||
|
||||
import re
|
||||
import os
|
||||
|
||||
|
||||
class Cmake(Package):
|
||||
"""A cross-platform, open-source build system. CMake is a family of
|
||||
tools designed to build, test and package software."""
|
||||
tools designed to build, test and package software.
|
||||
"""
|
||||
homepage = 'https://www.cmake.org'
|
||||
url = 'https://github.com/Kitware/CMake/releases/download/v3.15.5/cmake-3.15.5.tar.gz'
|
||||
url = 'https://github.com/Kitware/CMake/releases/download/v3.15.5/cmake-3.15.5.tar.gz'
|
||||
maintainers = ['chuckatkins']
|
||||
|
||||
executables = ['cmake']
|
||||
executables = ['^cmake$']
|
||||
|
||||
version('3.18.1', sha256='c0e3338bd37e67155b9d1e9526fec326b5c541f74857771b7ffed0c46ad62508')
|
||||
version('3.18.0', sha256='83b4ffcb9482a73961521d2bafe4a16df0168f03f56e6624c419c461e5317e29')
|
||||
@@ -163,20 +160,10 @@ class Cmake(Package):
|
||||
phases = ['bootstrap', 'build', 'install']
|
||||
|
||||
@classmethod
|
||||
def determine_spec_details(cls, prefix, exes_in_prefix):
|
||||
exe_to_path = dict(
|
||||
(os.path.basename(p), p) for p in exes_in_prefix
|
||||
)
|
||||
if 'cmake' not in exe_to_path:
|
||||
return None
|
||||
|
||||
cmake = spack.util.executable.Executable(exe_to_path['cmake'])
|
||||
output = cmake('--version', output=str)
|
||||
if output:
|
||||
match = re.search(r'cmake.*version\s+(\S+)', output)
|
||||
if match:
|
||||
version_str = match.group(1)
|
||||
return Spec('cmake@{0}'.format(version_str))
|
||||
def determine_version(cls, exe):
|
||||
output = Executable(exe)('--version', output=str)
|
||||
match = re.search(r'cmake.*version\s+(\S+)', output)
|
||||
return match.group(1) if match else None
|
||||
|
||||
def flag_handler(self, name, flags):
|
||||
if name == 'cxxflags' and self.compiler.name == 'fj':
|
||||
|
@@ -2,16 +2,17 @@
|
||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||
#
|
||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||
|
||||
from spack import *
|
||||
from spack.operating_systems.mac_os import macos_version, macos_sdk_path
|
||||
from llnl.util import tty
|
||||
|
||||
import glob
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import llnl.util.tty as tty
|
||||
import spack.util.executable
|
||||
|
||||
from spack.operating_systems.mac_os import macos_version, macos_sdk_path
|
||||
|
||||
|
||||
class Gcc(AutotoolsPackage, GNUMirrorPackage):
|
||||
"""The GNU Compiler Collection includes front ends for C, C++, Objective-C,
|
||||
@@ -269,6 +270,105 @@ class Gcc(AutotoolsPackage, GNUMirrorPackage):
|
||||
|
||||
build_directory = 'spack-build'
|
||||
|
||||
@property
|
||||
def executables(self):
|
||||
names = [r'gcc', r'[^\w]?g\+\+', r'gfortran']
|
||||
suffixes = [r'', r'-mp-\d+\.\d', r'-\d+\.\d', r'-\d+', r'\d\d']
|
||||
return [r''.join(x) for x in itertools.product(names, suffixes)]
|
||||
|
||||
@classmethod
|
||||
def filter_detected_exes(cls, prefix, exes_in_prefix):
|
||||
result = []
|
||||
for exe in exes_in_prefix:
|
||||
# clang++ matches g++ -> clan[g++]
|
||||
if any(x in exe for x in ('clang', 'ranlib')):
|
||||
continue
|
||||
# Filter out links in favor of real executables
|
||||
if os.path.islink(exe):
|
||||
continue
|
||||
result.append(exe)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def determine_version(cls, exe):
|
||||
version_regex = re.compile(r'([\d\.]+)')
|
||||
for vargs in ('-dumpfullversion', '-dumpversion'):
|
||||
try:
|
||||
output = spack.compiler.get_compiler_version_output(exe, vargs)
|
||||
match = version_regex.search(output)
|
||||
if match:
|
||||
return match.group(1)
|
||||
except spack.util.executable.ProcessError:
|
||||
pass
|
||||
except Exception as e:
|
||||
tty.debug(e)
|
||||
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def determine_variants(cls, exes, version_str):
|
||||
languages, compilers = set(), {}
|
||||
for exe in exes:
|
||||
basename = os.path.basename(exe)
|
||||
if 'gcc' in basename:
|
||||
languages.add('c')
|
||||
compilers['c'] = exe
|
||||
elif 'g++' in basename:
|
||||
languages.add('c++')
|
||||
compilers['cxx'] = exe
|
||||
elif 'gfortran' in basename:
|
||||
languages.add('fortran')
|
||||
compilers['fortran'] = exe
|
||||
variant_str = 'languages={0}'.format(','.join(languages))
|
||||
return variant_str, {'compilers': compilers}
|
||||
|
||||
@classmethod
|
||||
def validate_detected_spec(cls, spec, extra_attributes):
|
||||
# For GCC 'compilers' is a mandatory attribute
|
||||
msg = ('the extra attribute "compilers" must be set for '
|
||||
'the detected spec "{0}"'.format(spec))
|
||||
assert 'compilers' in extra_attributes, msg
|
||||
|
||||
compilers = extra_attributes['compilers']
|
||||
for constraint, key in {
|
||||
'languages=c': 'c',
|
||||
'languages=c++': 'cxx',
|
||||
'languages=fortran': 'fortran'
|
||||
}.items():
|
||||
if spec.satisfies(constraint, strict=True):
|
||||
msg = '{0} not in {1}'
|
||||
assert key in compilers, msg.format(key, spec)
|
||||
|
||||
@property
|
||||
def cc(self):
|
||||
msg = "cannot retrieve C compiler [spec is not concrete]"
|
||||
assert self.spec.concrete, msg
|
||||
if self.spec.external:
|
||||
return self.spec.extra_attributes['compilers'].get('c', None)
|
||||
return self.spec.prefix.bin.gcc if 'languages=c' in self.spec else None
|
||||
|
||||
@property
|
||||
def cxx(self):
|
||||
msg = "cannot retrieve C++ compiler [spec is not concrete]"
|
||||
assert self.spec.concrete, msg
|
||||
if self.spec.external:
|
||||
return self.spec.extra_attributes['compilers'].get('cxx', None)
|
||||
result = None
|
||||
if 'languages=c++' in self.spec:
|
||||
result = os.path.join(self.spec.prefix.bin, 'g++')
|
||||
return result
|
||||
|
||||
@property
|
||||
def fortran(self):
|
||||
msg = "cannot retrieve Fortran compiler [spec is not concrete]"
|
||||
assert self.spec.concrete, msg
|
||||
if self.spec.external:
|
||||
return self.spec.extra_attributes['compilers'].get('fortran', None)
|
||||
result = None
|
||||
if 'languages=fortran' in self.spec:
|
||||
result = self.spec.prefix.bin.gfortran
|
||||
return result
|
||||
|
||||
def url_for_version(self, version):
|
||||
# This function will be called when trying to fetch from url, before
|
||||
# mirrors are tried. It takes care of modifying the suffix of gnu
|
||||
|
Reference in New Issue
Block a user