intel oneapi classic bootstrapping (#31285)
The `intel` compiler at versions > 20 is provided by the `intel-oneapi-compilers-classic` package (a thin wrapper around the `intel-oneapi-compilers` package), and the `oneapi` compiler is provided by the `intel-oneapi-compilers` package. Prior to this work, neither of these compilers could be bootstrapped by Spack as part of an install with `install_missing_compilers: True`. Changes made to make these two packages bootstrappable: 1. The `intel-oneapi-compilers-classic` package includes a bin directory and symlinks to the compiler executables, not just logical pointers in Spack. 2. Spack can look for bootstrapped compilers in directories other than `$prefix/bin`, defined on a per-package basis 3. `intel-oneapi-compilers` specifies a non-default search directory for the compiler executables. 4. The `spack.compilers` module now can make more advanced associations between packages and compilers, not just simple name translations 5. Spack support for lmod hierarchies accounts for differences between package names and the associated compiler names for `intel-oneapi-compilers/oneapi`, `intel-oneapi-compilers-classic/intel@20:`, `llvm+clang/clang`, and `llvm-amdgpu/rocmcc`. - [x] full end-to-end testing - [x] add unit tests
This commit is contained in:
parent
f099a68e65
commit
c9561c5a0e
@ -49,12 +49,26 @@
|
|||||||
"clang": "llvm+clang",
|
"clang": "llvm+clang",
|
||||||
"oneapi": "intel-oneapi-compilers",
|
"oneapi": "intel-oneapi-compilers",
|
||||||
"rocmcc": "llvm-amdgpu",
|
"rocmcc": "llvm-amdgpu",
|
||||||
|
"intel@2020:": "intel-oneapi-compilers-classic",
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: generating this from the previous dict causes docs errors
|
||||||
|
package_name_to_compiler_name = {
|
||||||
|
"llvm": "clang",
|
||||||
|
"intel-oneapi-compilers": "oneapi",
|
||||||
|
"llvm-amdgpu": "rocmcc",
|
||||||
|
"intel-oneapi-compilers-classic": "intel",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def pkg_spec_for_compiler(cspec):
|
def pkg_spec_for_compiler(cspec):
|
||||||
"""Return the spec of the package that provides the compiler."""
|
"""Return the spec of the package that provides the compiler."""
|
||||||
spec_str = "%s@%s" % (_compiler_to_pkg.get(cspec.name, cspec.name), cspec.versions)
|
for spec, package in _compiler_to_pkg.items():
|
||||||
|
if cspec.satisfies(spec):
|
||||||
|
spec_str = "%s@%s" % (package, cspec.versions)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
spec_str = str(cspec)
|
||||||
return spack.spec.Spec(spec_str)
|
return spack.spec.Spec(spec_str)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1241,6 +1241,12 @@ def _add_tasks(self, request, all_deps):
|
|||||||
fail_fast = request.install_args.get("fail_fast")
|
fail_fast = request.install_args.get("fail_fast")
|
||||||
self.fail_fast = self.fail_fast or fail_fast
|
self.fail_fast = self.fail_fast or fail_fast
|
||||||
|
|
||||||
|
def _add_compiler_package_to_config(self, pkg):
|
||||||
|
compiler_search_prefix = getattr(pkg, "compiler_search_prefix", pkg.spec.prefix)
|
||||||
|
spack.compilers.add_compilers_to_config(
|
||||||
|
spack.compilers.find_compilers([compiler_search_prefix])
|
||||||
|
)
|
||||||
|
|
||||||
def _install_task(self, task):
|
def _install_task(self, task):
|
||||||
"""
|
"""
|
||||||
Perform the installation of the requested spec and/or dependency
|
Perform the installation of the requested spec and/or dependency
|
||||||
@ -1266,9 +1272,7 @@ def _install_task(self, task):
|
|||||||
if use_cache and _install_from_cache(pkg, cache_only, explicit, unsigned):
|
if use_cache and _install_from_cache(pkg, cache_only, explicit, unsigned):
|
||||||
self._update_installed(task)
|
self._update_installed(task)
|
||||||
if task.compiler:
|
if task.compiler:
|
||||||
spack.compilers.add_compilers_to_config(
|
self._add_compiler_package_to_config(pkg)
|
||||||
spack.compilers.find_compilers([pkg.spec.prefix])
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
pkg.run_tests = tests is True or tests and pkg.name in tests
|
pkg.run_tests = tests is True or tests and pkg.name in tests
|
||||||
@ -1296,9 +1300,7 @@ def _install_task(self, task):
|
|||||||
|
|
||||||
# If a compiler, ensure it is added to the configuration
|
# If a compiler, ensure it is added to the configuration
|
||||||
if task.compiler:
|
if task.compiler:
|
||||||
spack.compilers.add_compilers_to_config(
|
self._add_compiler_package_to_config(pkg)
|
||||||
spack.compilers.find_compilers([pkg.spec.prefix])
|
|
||||||
)
|
|
||||||
except spack.build_environment.StopPhase as e:
|
except spack.build_environment.StopPhase as e:
|
||||||
# A StopPhase exception means that do_install was asked to
|
# A StopPhase exception means that do_install was asked to
|
||||||
# stop early from clients, and is not an error at this point
|
# stop early from clients, and is not an error at this point
|
||||||
@ -1717,9 +1719,7 @@ def install(self):
|
|||||||
|
|
||||||
# It's an already installed compiler, add it to the config
|
# It's an already installed compiler, add it to the config
|
||||||
if task.compiler:
|
if task.compiler:
|
||||||
spack.compilers.add_compilers_to_config(
|
self._add_compiler_package_to_config(pkg)
|
||||||
spack.compilers.find_compilers([pkg.spec.prefix])
|
|
||||||
)
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# At this point we've failed to get a write or a read
|
# At this point we've failed to get a write or a read
|
||||||
|
@ -184,22 +184,10 @@ def provides(self):
|
|||||||
# If it is in the list of supported compilers family -> compiler
|
# If it is in the list of supported compilers family -> compiler
|
||||||
if self.spec.name in spack.compilers.supported_compilers():
|
if self.spec.name in spack.compilers.supported_compilers():
|
||||||
provides["compiler"] = spack.spec.CompilerSpec(str(self.spec))
|
provides["compiler"] = spack.spec.CompilerSpec(str(self.spec))
|
||||||
# Special case for llvm
|
elif self.spec.name in spack.compilers.package_name_to_compiler_name:
|
||||||
if self.spec.name == "llvm":
|
# If it is the package for a supported compiler, but of a different name
|
||||||
provides["compiler"] = spack.spec.CompilerSpec(str(self.spec))
|
cname = spack.compilers.package_name_to_compiler_name[self.spec.name]
|
||||||
provides["compiler"].name = "clang"
|
provides["compiler"] = spack.spec.CompilerSpec("%s@%s" % (cname, self.spec.version))
|
||||||
# Special case for llvm-amdgpu
|
|
||||||
if self.spec.name == "llvm-amdgpu":
|
|
||||||
provides["compiler"] = spack.spec.CompilerSpec(str(self.spec))
|
|
||||||
provides["compiler"].name = "rocmcc"
|
|
||||||
# Special case for oneapi
|
|
||||||
if self.spec.name == "intel-oneapi-compilers":
|
|
||||||
provides["compiler"] = spack.spec.CompilerSpec(str(self.spec))
|
|
||||||
provides["compiler"].name = "oneapi"
|
|
||||||
# Special case for oneapi classic
|
|
||||||
if self.spec.name == "intel-oneapi-compilers-classic":
|
|
||||||
provides["compiler"] = spack.spec.CompilerSpec(str(self.spec))
|
|
||||||
provides["compiler"].name = "intel"
|
|
||||||
|
|
||||||
# All the other tokens in the hierarchy must be virtual dependencies
|
# All the other tokens in the hierarchy must be virtual dependencies
|
||||||
for x in self.hierarchy_tokens:
|
for x in self.hierarchy_tokens:
|
||||||
|
@ -487,6 +487,17 @@ def fake_package_list(compiler, architecture, pkgs):
|
|||||||
assert installer.build_pq[0][1].compiler
|
assert installer.build_pq[0][1].compiler
|
||||||
|
|
||||||
|
|
||||||
|
def test_bootstrapping_compilers_with_different_names_from_spec(
|
||||||
|
install_mockery, mutable_config, mock_fetch
|
||||||
|
):
|
||||||
|
with spack.config.override("config:install_missing_compilers", True):
|
||||||
|
with spack.concretize.disable_compiler_existence_check():
|
||||||
|
spec = spack.spec.Spec("trivial-install-test-package%oneapi@22.2.0").concretized()
|
||||||
|
spec.package.do_install()
|
||||||
|
|
||||||
|
assert spack.spec.CompilerSpec("oneapi@22.2.0") in spack.compilers.all_compiler_specs()
|
||||||
|
|
||||||
|
|
||||||
def test_dump_packages_deps_ok(install_mockery, tmpdir, mock_packages):
|
def test_dump_packages_deps_ok(install_mockery, tmpdir, mock_packages):
|
||||||
"""Test happy path for dump_packages with dependencies."""
|
"""Test happy path for dump_packages with dependencies."""
|
||||||
|
|
||||||
|
@ -81,6 +81,15 @@ def test_file_layout(self, compiler, provider, factory, module_configuration):
|
|||||||
else:
|
else:
|
||||||
assert repetitions == 1
|
assert repetitions == 1
|
||||||
|
|
||||||
|
def test_compilers_provided_different_name(self, factory, module_configuration):
|
||||||
|
module_configuration("complex_hierarchy")
|
||||||
|
module, spec = factory("intel-oneapi-compilers%clang@3.3")
|
||||||
|
|
||||||
|
provides = module.conf.provides
|
||||||
|
|
||||||
|
assert "compiler" in provides
|
||||||
|
assert provides["compiler"] == spack.spec.CompilerSpec("oneapi@3.0")
|
||||||
|
|
||||||
def test_simple_case(self, modulefile_content, module_configuration):
|
def test_simple_case(self, modulefile_content, module_configuration):
|
||||||
"""Tests the generation of a simple TCL module file."""
|
"""Tests the generation of a simple TCL module file."""
|
||||||
|
|
||||||
|
@ -0,0 +1,28 @@
|
|||||||
|
# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other
|
||||||
|
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
|
from spack.package import *
|
||||||
|
|
||||||
|
|
||||||
|
class IntelOneapiCompilers(Package):
|
||||||
|
"""Simple compiler package."""
|
||||||
|
|
||||||
|
homepage = "http://www.example.com"
|
||||||
|
url = "http://www.example.com/oneapi-1.0.tar.gz"
|
||||||
|
|
||||||
|
version("1.0", "0123456789abcdef0123456789abcdef")
|
||||||
|
version("2.0", "abcdef0123456789abcdef0123456789")
|
||||||
|
version("3.0", "def0123456789abcdef0123456789abc")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def compiler_search_prefix(self):
|
||||||
|
return self.prefix.foo.bar.baz.bin
|
||||||
|
|
||||||
|
def install(self, spec, prefix):
|
||||||
|
# Create the minimal compiler that will fool `spack compiler find`
|
||||||
|
mkdirp(self.compiler_search_prefix)
|
||||||
|
with open(self.compiler_search_prefix.icx, "w") as f:
|
||||||
|
f.write('#!/bin/bash\necho "oneAPI DPC++ Compiler %s"' % str(spec.version))
|
||||||
|
set_executable(self.compiler_search_prefix.icx)
|
@ -2,6 +2,9 @@
|
|||||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
import os
|
||||||
|
|
||||||
|
from llnl.util.link_tree import LinkTree
|
||||||
|
|
||||||
from spack.package import *
|
from spack.package import *
|
||||||
|
|
||||||
@ -19,8 +22,6 @@ class IntelOneapiCompilersClassic(Package):
|
|||||||
|
|
||||||
has_code = False
|
has_code = False
|
||||||
|
|
||||||
phases = []
|
|
||||||
|
|
||||||
# Versions before 2021 are in the `intel` package
|
# Versions before 2021 are in the `intel` package
|
||||||
# intel-oneapi versions before 2022 use intel@19.0.4
|
# intel-oneapi versions before 2022 use intel@19.0.4
|
||||||
for ver, oneapi_ver in {
|
for ver, oneapi_ver in {
|
||||||
@ -35,8 +36,19 @@ class IntelOneapiCompilersClassic(Package):
|
|||||||
version(ver)
|
version(ver)
|
||||||
depends_on("intel-oneapi-compilers@" + oneapi_ver, when="@" + ver, type="run")
|
depends_on("intel-oneapi-compilers@" + oneapi_ver, when="@" + ver, type="run")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def oneapi_compiler_prefix(self):
|
||||||
|
oneapi_version = self.spec["intel-oneapi-compilers"].version
|
||||||
|
return self.spec["intel-oneapi-compilers"].prefix.compiler.join(str(oneapi_version))
|
||||||
|
|
||||||
def setup_run_environment(self, env):
|
def setup_run_environment(self, env):
|
||||||
"""Adds environment variables to the generated module file."""
|
"""Adds environment variables to the generated module file.
|
||||||
|
|
||||||
|
These environment variables come from running:
|
||||||
|
.. code-block:: console
|
||||||
|
$ source {prefix}/{component}/{version}/env/vars.sh
|
||||||
|
and from setting CC/CXX/F77/FC
|
||||||
|
"""
|
||||||
oneapi_pkg = self.spec["intel-oneapi-compilers"].package
|
oneapi_pkg = self.spec["intel-oneapi-compilers"].package
|
||||||
|
|
||||||
oneapi_pkg.setup_run_environment(env)
|
oneapi_pkg.setup_run_environment(env)
|
||||||
@ -46,3 +58,26 @@ def setup_run_environment(self, env):
|
|||||||
env.set("CXX", bin_prefix.icpc)
|
env.set("CXX", bin_prefix.icpc)
|
||||||
env.set("F77", bin_prefix.ifort)
|
env.set("F77", bin_prefix.ifort)
|
||||||
env.set("FC", bin_prefix.ifort)
|
env.set("FC", bin_prefix.ifort)
|
||||||
|
|
||||||
|
def install(self, spec, prefix):
|
||||||
|
# If we symlink top-level directories directly, files won't show up in views
|
||||||
|
# Create real dirs and symlink files instead
|
||||||
|
self.symlink_dir(self.oneapi_compiler_prefix.linux.bin.intel64, prefix.bin)
|
||||||
|
self.symlink_dir(self.oneapi_compiler_prefix.linux.lib, prefix.lib)
|
||||||
|
self.symlink_dir(self.oneapi_compiler_prefix.linux.include, prefix.include)
|
||||||
|
self.symlink_dir(self.oneapi_compiler_prefix.linux.compiler, prefix.compiler)
|
||||||
|
self.symlink_dir(self.oneapi_compiler_prefix.documentation.en.man, prefix.man)
|
||||||
|
|
||||||
|
def symlink_dir(self, src, dest):
|
||||||
|
# Create a real directory at dest
|
||||||
|
mkdirp(dest)
|
||||||
|
|
||||||
|
# Symlink all files in src to dest keeping directories as dirs
|
||||||
|
for entry in os.listdir(src):
|
||||||
|
src_path = os.path.join(src, entry)
|
||||||
|
dest_path = os.path.join(dest, entry)
|
||||||
|
if os.path.isdir(src_path) and os.access(src_path, os.X_OK):
|
||||||
|
link_tree = LinkTree(src_path)
|
||||||
|
link_tree.merge(dest_path)
|
||||||
|
else:
|
||||||
|
os.symlink(src_path, dest_path)
|
||||||
|
@ -149,6 +149,10 @@ class IntelOneapiCompilers(IntelOneApiPackage):
|
|||||||
def component_dir(self):
|
def component_dir(self):
|
||||||
return "compiler"
|
return "compiler"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def compiler_search_prefix(self):
|
||||||
|
return self.prefix.compiler.join(str(self.version)).linux.bin
|
||||||
|
|
||||||
def setup_run_environment(self, env):
|
def setup_run_environment(self, env):
|
||||||
"""Adds environment variables to the generated module file.
|
"""Adds environment variables to the generated module file.
|
||||||
|
|
||||||
@ -169,7 +173,7 @@ def setup_run_environment(self, env):
|
|||||||
|
|
||||||
def install(self, spec, prefix):
|
def install(self, spec, prefix):
|
||||||
# Copy instead of install to speed up debugging
|
# Copy instead of install to speed up debugging
|
||||||
# install_tree('/opt/intel/oneapi/compiler', self.prefix)
|
# install_tree("/opt/intel/oneapi/compiler", self.prefix)
|
||||||
|
|
||||||
# install cpp
|
# install cpp
|
||||||
super(IntelOneapiCompilers, self).install(spec, prefix)
|
super(IntelOneapiCompilers, self).install(spec, prefix)
|
||||||
|
Loading…
Reference in New Issue
Block a user