Skip to content

Commit 329adb1

Browse files
authored
Intel classic compiler (#305)
Add support for providing the "intel classic" compiler. This is for supporting users who still have not migrated from the `ifort` compiler. It installs both `intel-oneapi-compilers` (for C and C++) and the `intel-oneapi-compilers-classic` (for Fortran) Spack packages. The user probably has to hack the `prefer` line in the `environments.yaml` description of the target environment.... but this is a rarely used feature that does not justify the refactoring required to make an elegant solution.
1 parent db5e1de commit 329adb1

11 files changed

Lines changed: 110 additions & 23 deletions

File tree

CLAUDE.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,15 @@ llvm: # optional
8383
version: "16"
8484
llvm-amdgpu: # optional
8585
version: "6.0"
86-
intel-oneapi-compilers: # optional
86+
intel-oneapi: # optional; spack package intel-oneapi-compilers
8787
version: "2024.1"
88+
intel-classic: # optional; spack package intel-oneapi-compilers-classic
89+
version: "2021.10.0"
8890
```
8991

90-
Build order: `gcc` is built first (using system compiler), then `nvhpc`/`llvm`/`llvm-amdgpu`/`intel-oneapi-compilers` are built using the gcc toolchain. Stackinator appends opinionated variants (e.g. `gcc@13 +bootstrap`, `nvhpc@25.1 ~mpi~blas~lapack`, `llvm@16 +clang ~gold`). Each compiler becomes a separate spec group in the unified `spack.yaml`.
92+
Build order: `gcc` is built first (using system compiler), then `nvhpc`/`llvm`/`llvm-amdgpu`/`intel-oneapi`/`intel-classic` are built using the gcc toolchain. Stackinator appends opinionated variants (e.g. `gcc@13 +bootstrap`, `nvhpc@25.1 ~mpi~blas~lapack`, `llvm@16 +clang ~gold`). Each compiler becomes a separate spec group in the unified `spack.yaml`.
93+
94+
The `intel-oneapi` and `intel-classic` recipe keys differ from their spack package names. Three distinct names are involved: the **recipe key** (`intel-oneapi`/`intel-classic`; used in compilers.yaml, environments.yaml `compiler`, spec-group names, `needs`), the **spack package** (`intel-oneapi-compilers`/`intel-oneapi-compilers-classic`; used to build the spec, query the spack DB in `compiler-config.py`, and as the packages.yaml key), and the **spack compiler name** (`oneapi`/`intel`; used only in the auto-generated `prefer` constraint). `Recipe` stores the package name in `self.compilers[key]["package"]`; `compiler_names` returns package names, and each environment gets a `compiler_packages` list mapping its recipe keys to package names for the view-symlink filter.
9195

9296
### `environments.yaml` (required)
9397
```yaml
@@ -291,7 +295,7 @@ The build runs inside a bwrap sandbox (`bwrap-mutable-root.sh`) that:
291295
The unified `spack.yaml` uses Spack 1.2 spec groups to express the build order and per-group concretizer settings. Structure:
292296
293297
- **gcc group**: `explicit: false`, override sets static-library variants for gcc's dependencies (mpc, gmp, mpfr, zstd, zlib)
294-
- **nvhpc/llvm/llvm-amdgpu/intel-oneapi-compilers groups**: `explicit: false`, `needs: [gcc]`, `reuse: false`
298+
- **nvhpc/llvm/llvm-amdgpu/intel-oneapi/intel-classic groups**: `explicit: false`, `needs: [gcc]`, `reuse: false`
295299
- **uenv_tools group**: `explicit: false`, `needs: [gcc]`, installs `squashfs`
296300
- **user environment groups**: `needs: [compiler list]`, override sets `concretizer.unify`, `concretizer.duplicates.strategy`, `packages.all.prefer`, `packages.all.variants`, and `packages.mpi.require` per-environment
297301

docs/recipes.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,15 @@ The compilers are built in multiple stages:
110110
* `gcc:version`: The version of gcc
111111
1. *llvm*: (optional) The llvm toolchain is built using the gcc toolchain installed in step 1.
112112
* `llvm:version`: The version of llvm
113+
1. *llvm-amdgpu*: (optional) The AMD ROCm llvm toolchain is built using the gcc toolchain installed in step 1.
114+
* `llvm-amdgpu:version`: The version of llvm-amdgpu
113115
1. *nvhpc*: (optional) The nvhpc toolchain is built using the gcc toolchain installed in step 1.
114116
* `nvhpc:version`: The version of nvhpc
117+
1. *intel-oneapi*: (optional) The Intel oneAPI (`icx`/`icpx`/`ifx`) toolchain is built using the gcc toolchain installed in step 1.
118+
* `intel-oneapi:version`: The version of the oneAPI compilers (spack package `intel-oneapi-compilers`)
119+
1. *intel-classic*: (optional) The classic Intel (`icc`/`icpc`/`ifort`) toolchain is built using the gcc toolchain installed in step 1.
120+
* `intel-classic:version`: The version of the classic compilers (spack package `intel-oneapi-compilers-classic`)
121+
* **note**: the intel-classic compiler is used to provide a compiler, and will install a copy of `intel-oneapi-compilers` to provide the C and C++ compilers.
115122

116123
The first step - building `gcc` - is required, so that the simplest stack will provide at least one version of gcc compiled for the target architecture.
117124

@@ -121,6 +128,13 @@ The first step - building `gcc` - is required, so that the simplest stack will p
121128
* `nvhpc:version:"21.7"` generates `nvhpc@21.7 ~mpi~blas~lapack`
122129
* `llvm:version:"14"` generates `llvm@14 +clang ~gold`
123130
* `gcc:version:"13"` generates `gcc@13 +bootstrap`
131+
* `intel-oneapi:version:"2024.1"` generates `intel-oneapi-compilers@2024.1`
132+
* `intel-classic:version:"2021.10.0"` generates `intel-oneapi-compilers-classic@2021.10.0`
133+
134+
!!! note
135+
The `intel-oneapi` and `intel-classic` keys use short recipe names that map to
136+
the longer spack package names `intel-oneapi-compilers` and
137+
`intel-oneapi-compilers-classic` respectively.
124138

125139
The default variants can be customised by setting the optional `spec` field on a compiler, which **replaces** the default variants for that compiler:
126140

stackinator/etc/compiler-config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"llvm-amdgpu": [("clang", "c"), ("clang++", "cxx"), ("flang-new", "fortran")],
2323
"nvhpc": [("nvc", "c"), ("nvc++", "cxx"), ("nvfortran", "fortran")],
2424
"intel-oneapi-compilers": [("icx", "c"), ("icpx", "cxx"), ("ifx", "fortran")],
25+
"intel-oneapi-compilers-classic": [("icc", "c"), ("icpc", "cxx"), ("ifort", "fortran")],
2526
}
2627

2728

stackinator/recipe.py

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,13 @@ def environment_view_meta(self):
382382

383383
@property
384384
def compiler_names(self):
385-
"""Names of the compiler packages installed in this recipe (excludes system gcc)."""
386-
return [name for name, c in self.compilers.items() if not c.get("system", False)]
385+
"""Spack package names of the compilers installed in this recipe (excludes system gcc).
386+
387+
The recipe key (e.g. 'intel-classic') can differ from the spack package
388+
name (e.g. 'intel-oneapi-compilers-classic'); the latter is what the spack
389+
DB is queried with, so return the 'package' field when present.
390+
"""
391+
return [c.get("package", name) for name, c in self.compilers.items() if not c.get("system", False)]
387392

388393
# creates the self.environments field that describes the full specifications
389394
# for all of the environments sets, grouped in environments, from the raw
@@ -432,9 +437,10 @@ def generate_environment_specs(self, raw):
432437
for name, config in environments.items():
433438
if config["prefer"] is None:
434439
compiler_key = config["compiler"][0]
435-
# spack uses a different name for the intel oneapi compilers
436-
# than the package that installs them.
437-
compiler_name = "oneapi" if compiler_key == "intel-oneapi-compilers" else compiler_key
440+
# spack uses a different name for the intel compilers than the
441+
# recipe key or the package that installs them.
442+
spack_compiler_names = {"intel-oneapi": "oneapi", "intel-classic": "intel"}
443+
compiler_name = spack_compiler_names.get(compiler_key, compiler_key)
438444
compiler_version = self.compilers[compiler_key].get("version")
439445
versioned = f"{compiler_name}@{compiler_version}" if compiler_version else compiler_name
440446
config["prefer"] = [
@@ -445,6 +451,12 @@ def generate_environment_specs(self, raw):
445451
for name, config in environments.items():
446452
config["needs"] = [c for c in config["compiler"] if not self.compilers.get(c, {}).get("system", False)]
447453

454+
# Map each environment's compilers (recipe keys) to their spack package
455+
# names. Used by the view-symlink step, which filters the generated
456+
# packages.yaml (keyed by spack package name) to this environment's compilers.
457+
for name, config in environments.items():
458+
config["compiler_packages"] = [self.compilers.get(c, {}).get("package", c) for c in config["compiler"]]
459+
448460
# Build view metadata
449461
env_names = set()
450462
for name, config in environments.items():
@@ -506,17 +518,22 @@ def generate_compiler_specs(self, raw):
506518

507519
# the default spec (variants etc) for each compiler, used when the recipe
508520
# does not provide an explicit 'spec' field
509-
for name, default_suffix in [
510-
("nvhpc", "~mpi~blas~lapack"),
511-
("llvm", "+clang ~gold"),
512-
("llvm-amdgpu", ""),
513-
("intel-oneapi-compilers", ""),
521+
# each entry is (recipe key, spack package name, default variant suffix).
522+
# the recipe key can differ from the spack package name (e.g. the intel
523+
# compilers), so the package name is used to build the spec and stored on
524+
# the compiler dict for downstream steps (compiler-config, view symlinks).
525+
for name, package, default_suffix in [
526+
("nvhpc", "nvhpc", "~mpi~blas~lapack"),
527+
("llvm", "llvm", "+clang ~gold"),
528+
("llvm-amdgpu", "llvm-amdgpu", ""),
529+
("intel-oneapi", "intel-oneapi-compilers", ""),
530+
("intel-classic", "intel-oneapi-compilers-classic", ""),
514531
]:
515532
if raw.get(name) is not None:
516533
version = raw[name]["version"]
517534
suffix = raw[name].get("spec") or default_suffix
518-
spec = f"{name}@{version} {suffix}".strip()
519-
compilers[name] = {"specs": [spec], "version": version}
535+
spec = f"{package}@{version} {suffix}".strip()
536+
compilers[name] = {"specs": [spec], "version": version, "package": package}
520537

521538
self.compilers = compilers
522539

stackinator/schema/compilers.json

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,30 @@
8989
],
9090
"default": null
9191
},
92-
"intel-oneapi-compilers": {
92+
"intel-oneapi": {
93+
"oneOf": [
94+
{
95+
"type": "object",
96+
"properties": {
97+
"version": {"type": "string"},
98+
"spec": {
99+
"oneOf": [
100+
{"type": "string"},
101+
{"type": "null"}
102+
],
103+
"default": null
104+
}
105+
},
106+
"additionalProperties": false,
107+
"required": ["version"]
108+
},
109+
{
110+
"type": "null"
111+
}
112+
],
113+
"default": null
114+
},
115+
"intel-classic": {
93116
"oneOf": [
94117
{
95118
"type": "object",

stackinator/schema/environments.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
"type": "array",
6464
"items": {
6565
"type": "string",
66-
"enum": ["gcc", "nvhpc", "llvm", "llvm-amdgpu", "intel-oneapi-compilers"]
66+
"enum": ["gcc", "nvhpc", "llvm", "llvm-amdgpu", "intel-oneapi", "intel-classic"]
6767
}
6868
},
6969
"specs": {

stackinator/templates/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ views: install compiler-config.yaml
101101
$(SANDBOX) mkdir -p $(STORE)/env/{{ view.name }}
102102
$(SANDBOX) sh -c '$(SPACK) env activate -d $(ENV_ROOT) --with-view {{ view.name }} --sh > $(STORE)/env/{{ view.name }}/activate.sh'
103103
$(SANDBOX) $(BUILD_ROOT)/envvars.py view \
104-
{% if view.extra.add_compilers %}--compilers=$(BUILD_ROOT)/compiler-config.yaml --compiler-names={{ config.compiler | join(',') }} {% endif %}\
104+
{% if view.extra.add_compilers %}--compilers=$(BUILD_ROOT)/compiler-config.yaml --compiler-names={{ config.compiler_packages | join(',') }} {% endif %}\
105105
--prefix_paths="{{ view.extra.prefix_string }}" \
106106
$(STORE)/env/{{ view.name }} \
107107
$(BUILD_ROOT)

stackinator/templates/spack.yaml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,21 +69,38 @@ spack:
6969
reuse: false
7070

7171
{% endif %}
72-
{% if compilers.get('intel-oneapi-compilers') %}
73-
- group: intel-oneapi-compilers
72+
{% if compilers.get('intel-oneapi') %}
73+
- group: intel-oneapi
7474
explicit: false
7575
{% if not system_gcc %}
7676
needs: [gcc]
7777
{% endif %}
7878
specs:
79-
{% for spec in compilers['intel-oneapi-compilers'].specs %}
79+
{% for spec in compilers['intel-oneapi'].specs %}
8080
- '{{ spec }}'
8181
{% endfor %}
8282
override:
8383
concretizer:
8484
unify: when_possible
8585
reuse: false
8686

87+
{% endif %}
88+
{% if compilers.get('intel-classic') %}
89+
- group: intel-classic
90+
explicit: false
91+
{% if not system_gcc %}
92+
needs: [gcc]
93+
{% endif %}
94+
specs:
95+
- 'intel-oneapi-compilers'
96+
{% for spec in compilers['intel-classic'].specs %}
97+
- '{{ spec }}'
98+
{% endfor %}
99+
override:
100+
concretizer:
101+
unify: true
102+
reuse: false
103+
87104
{% endif %}
88105
# ---- Internal tools group ----
89106
# squashfs is required to create the final squashfs image.

unittests/test_recipe.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,19 @@ def test_generate_compiler_specs_defaults():
5353
"nvhpc": {"version": "25.1", "spec": None},
5454
"llvm": {"version": "16", "spec": None},
5555
"llvm-amdgpu": {"version": "6.0", "spec": None},
56-
"intel-oneapi-compilers": {"version": "2024.1", "spec": None},
56+
"intel-oneapi": {"version": "2024.1", "spec": None},
57+
"intel-classic": {"version": "2021.10.0", "spec": None},
5758
}
5859
)
5960
assert recipe.compilers["gcc"]["specs"] == ["gcc@13 +bootstrap"]
6061
assert recipe.compilers["nvhpc"]["specs"] == ["nvhpc@25.1 ~mpi~blas~lapack"]
6162
assert recipe.compilers["llvm"]["specs"] == ["llvm@16 +clang ~gold"]
6263
assert recipe.compilers["llvm-amdgpu"]["specs"] == ["llvm-amdgpu@6.0"]
63-
assert recipe.compilers["intel-oneapi-compilers"]["specs"] == ["intel-oneapi-compilers@2024.1"]
64+
# the recipe key differs from the spack package name for the intel compilers
65+
assert recipe.compilers["intel-oneapi"]["specs"] == ["intel-oneapi-compilers@2024.1"]
66+
assert recipe.compilers["intel-oneapi"]["package"] == "intel-oneapi-compilers"
67+
assert recipe.compilers["intel-classic"]["specs"] == ["intel-oneapi-compilers-classic@2021.10.0"]
68+
assert recipe.compilers["intel-classic"]["package"] == "intel-oneapi-compilers-classic"
6469
assert not recipe.use_system_gcc
6570

6671

unittests/test_schema.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ def test_compilers_yaml(yaml_path):
126126
assert raw["gcc"] == {"version": "11", "spec": "~bootstrap+nvptx"}
127127
assert raw["llvm"] == {"version": "13", "spec": None}
128128
assert raw["nvhpc"] == {"version": "25.1", "spec": None}
129+
assert raw["intel-oneapi"] == {"version": "2024.1", "spec": None}
130+
assert raw["intel-classic"] == {"version": "2021.10.0", "spec": None}
129131

130132
# spec must be a string (or null)
131133
with pytest.raises(schema.ValidationError):

0 commit comments

Comments
 (0)