Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,15 @@ llvm: # optional
version: "16"
llvm-amdgpu: # optional
version: "6.0"
intel-oneapi-compilers: # optional
intel-oneapi: # optional; spack package intel-oneapi-compilers
version: "2024.1"
intel-classic: # optional; spack package intel-oneapi-compilers-classic
version: "2021.10.0"
```

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`.
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`.

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.

### `environments.yaml` (required)
```yaml
Expand Down Expand Up @@ -291,7 +295,7 @@ The build runs inside a bwrap sandbox (`bwrap-mutable-root.sh`) that:
The unified `spack.yaml` uses Spack 1.2 spec groups to express the build order and per-group concretizer settings. Structure:

- **gcc group**: `explicit: false`, override sets static-library variants for gcc's dependencies (mpc, gmp, mpfr, zstd, zlib)
- **nvhpc/llvm/llvm-amdgpu/intel-oneapi-compilers groups**: `explicit: false`, `needs: [gcc]`, `reuse: false`
- **nvhpc/llvm/llvm-amdgpu/intel-oneapi/intel-classic groups**: `explicit: false`, `needs: [gcc]`, `reuse: false`
- **uenv_tools group**: `explicit: false`, `needs: [gcc]`, installs `squashfs`
- **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

Expand Down
14 changes: 14 additions & 0 deletions docs/recipes.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,15 @@ The compilers are built in multiple stages:
* `gcc:version`: The version of gcc
1. *llvm*: (optional) The llvm toolchain is built using the gcc toolchain installed in step 1.
* `llvm:version`: The version of llvm
1. *llvm-amdgpu*: (optional) The AMD ROCm llvm toolchain is built using the gcc toolchain installed in step 1.
* `llvm-amdgpu:version`: The version of llvm-amdgpu
1. *nvhpc*: (optional) The nvhpc toolchain is built using the gcc toolchain installed in step 1.
* `nvhpc:version`: The version of nvhpc
1. *intel-oneapi*: (optional) The Intel oneAPI (`icx`/`icpx`/`ifx`) toolchain is built using the gcc toolchain installed in step 1.
* `intel-oneapi:version`: The version of the oneAPI compilers (spack package `intel-oneapi-compilers`)
1. *intel-classic*: (optional) The classic Intel (`icc`/`icpc`/`ifort`) toolchain is built using the gcc toolchain installed in step 1.
* `intel-classic:version`: The version of the classic compilers (spack package `intel-oneapi-compilers-classic`)
* **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.

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.

Expand All @@ -121,6 +128,13 @@ The first step - building `gcc` - is required, so that the simplest stack will p
* `nvhpc:version:"21.7"` generates `nvhpc@21.7 ~mpi~blas~lapack`
* `llvm:version:"14"` generates `llvm@14 +clang ~gold`
* `gcc:version:"13"` generates `gcc@13 +bootstrap`
* `intel-oneapi:version:"2024.1"` generates `intel-oneapi-compilers@2024.1`
* `intel-classic:version:"2021.10.0"` generates `intel-oneapi-compilers-classic@2021.10.0`

!!! note
The `intel-oneapi` and `intel-classic` keys use short recipe names that map to
the longer spack package names `intel-oneapi-compilers` and
`intel-oneapi-compilers-classic` respectively.

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

Expand Down
1 change: 1 addition & 0 deletions stackinator/etc/compiler-config.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"llvm-amdgpu": [("clang", "c"), ("clang++", "cxx"), ("flang-new", "fortran")],
"nvhpc": [("nvc", "c"), ("nvc++", "cxx"), ("nvfortran", "fortran")],
"intel-oneapi-compilers": [("icx", "c"), ("icpx", "cxx"), ("ifx", "fortran")],
"intel-oneapi-compilers-classic": [("icc", "c"), ("icpc", "cxx"), ("ifort", "fortran")],
}


Expand Down
41 changes: 29 additions & 12 deletions stackinator/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,13 @@ def environment_view_meta(self):

@property
def compiler_names(self):
"""Names of the compiler packages installed in this recipe (excludes system gcc)."""
return [name for name, c in self.compilers.items() if not c.get("system", False)]
"""Spack package names of the compilers installed in this recipe (excludes system gcc).

The recipe key (e.g. 'intel-classic') can differ from the spack package
name (e.g. 'intel-oneapi-compilers-classic'); the latter is what the spack
DB is queried with, so return the 'package' field when present.
"""
return [c.get("package", name) for name, c in self.compilers.items() if not c.get("system", False)]

# creates the self.environments field that describes the full specifications
# for all of the environments sets, grouped in environments, from the raw
Expand Down Expand Up @@ -432,9 +437,10 @@ def generate_environment_specs(self, raw):
for name, config in environments.items():
if config["prefer"] is None:
compiler_key = config["compiler"][0]
# spack uses a different name for the intel oneapi compilers
# than the package that installs them.
compiler_name = "oneapi" if compiler_key == "intel-oneapi-compilers" else compiler_key
# spack uses a different name for the intel compilers than the
# recipe key or the package that installs them.
spack_compiler_names = {"intel-oneapi": "oneapi", "intel-classic": "intel"}
compiler_name = spack_compiler_names.get(compiler_key, compiler_key)
compiler_version = self.compilers[compiler_key].get("version")
versioned = f"{compiler_name}@{compiler_version}" if compiler_version else compiler_name
config["prefer"] = [
Expand All @@ -445,6 +451,12 @@ def generate_environment_specs(self, raw):
for name, config in environments.items():
config["needs"] = [c for c in config["compiler"] if not self.compilers.get(c, {}).get("system", False)]

# Map each environment's compilers (recipe keys) to their spack package
# names. Used by the view-symlink step, which filters the generated
# packages.yaml (keyed by spack package name) to this environment's compilers.
for name, config in environments.items():
config["compiler_packages"] = [self.compilers.get(c, {}).get("package", c) for c in config["compiler"]]

# Build view metadata
env_names = set()
for name, config in environments.items():
Expand Down Expand Up @@ -506,17 +518,22 @@ def generate_compiler_specs(self, raw):

# the default spec (variants etc) for each compiler, used when the recipe
# does not provide an explicit 'spec' field
for name, default_suffix in [
("nvhpc", "~mpi~blas~lapack"),
("llvm", "+clang ~gold"),
("llvm-amdgpu", ""),
("intel-oneapi-compilers", ""),
# each entry is (recipe key, spack package name, default variant suffix).
# the recipe key can differ from the spack package name (e.g. the intel
# compilers), so the package name is used to build the spec and stored on
# the compiler dict for downstream steps (compiler-config, view symlinks).
for name, package, default_suffix in [
("nvhpc", "nvhpc", "~mpi~blas~lapack"),
("llvm", "llvm", "+clang ~gold"),
("llvm-amdgpu", "llvm-amdgpu", ""),
("intel-oneapi", "intel-oneapi-compilers", ""),
("intel-classic", "intel-oneapi-compilers-classic", ""),
]:
if raw.get(name) is not None:
version = raw[name]["version"]
suffix = raw[name].get("spec") or default_suffix
spec = f"{name}@{version} {suffix}".strip()
compilers[name] = {"specs": [spec], "version": version}
spec = f"{package}@{version} {suffix}".strip()
compilers[name] = {"specs": [spec], "version": version, "package": package}

self.compilers = compilers

Expand Down
25 changes: 24 additions & 1 deletion stackinator/schema/compilers.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,30 @@
],
"default": null
},
"intel-oneapi-compilers": {
"intel-oneapi": {
"oneOf": [
{
"type": "object",
"properties": {
"version": {"type": "string"},
"spec": {
"oneOf": [
{"type": "string"},
{"type": "null"}
],
"default": null
}
},
"additionalProperties": false,
"required": ["version"]
},
{
"type": "null"
}
],
"default": null
},
"intel-classic": {
"oneOf": [
{
"type": "object",
Expand Down
2 changes: 1 addition & 1 deletion stackinator/schema/environments.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"type": "array",
"items": {
"type": "string",
"enum": ["gcc", "nvhpc", "llvm", "llvm-amdgpu", "intel-oneapi-compilers"]
"enum": ["gcc", "nvhpc", "llvm", "llvm-amdgpu", "intel-oneapi", "intel-classic"]
}
},
"specs": {
Expand Down
2 changes: 1 addition & 1 deletion stackinator/templates/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ views: install compiler-config.yaml
$(SANDBOX) mkdir -p $(STORE)/env/{{ view.name }}
$(SANDBOX) sh -c '$(SPACK) env activate -d $(ENV_ROOT) --with-view {{ view.name }} --sh > $(STORE)/env/{{ view.name }}/activate.sh'
$(SANDBOX) $(BUILD_ROOT)/envvars.py view \
{% if view.extra.add_compilers %}--compilers=$(BUILD_ROOT)/compiler-config.yaml --compiler-names={{ config.compiler | join(',') }} {% endif %}\
{% if view.extra.add_compilers %}--compilers=$(BUILD_ROOT)/compiler-config.yaml --compiler-names={{ config.compiler_packages | join(',') }} {% endif %}\
--prefix_paths="{{ view.extra.prefix_string }}" \
$(STORE)/env/{{ view.name }} \
$(BUILD_ROOT)
Expand Down
23 changes: 20 additions & 3 deletions stackinator/templates/spack.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,21 +69,38 @@ spack:
reuse: false

{% endif %}
{% if compilers.get('intel-oneapi-compilers') %}
- group: intel-oneapi-compilers
{% if compilers.get('intel-oneapi') %}
- group: intel-oneapi
explicit: false
{% if not system_gcc %}
needs: [gcc]
{% endif %}
specs:
{% for spec in compilers['intel-oneapi-compilers'].specs %}
{% for spec in compilers['intel-oneapi'].specs %}
- '{{ spec }}'
{% endfor %}
override:
concretizer:
unify: when_possible
reuse: false

{% endif %}
{% if compilers.get('intel-classic') %}
- group: intel-classic
explicit: false
{% if not system_gcc %}
needs: [gcc]
{% endif %}
specs:
- 'intel-oneapi-compilers'
{% for spec in compilers['intel-classic'].specs %}
- '{{ spec }}'
{% endfor %}
override:
concretizer:
unify: true
reuse: false

{% endif %}
# ---- Internal tools group ----
# squashfs is required to create the final squashfs image.
Expand Down
9 changes: 7 additions & 2 deletions unittests/test_recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,19 @@ def test_generate_compiler_specs_defaults():
"nvhpc": {"version": "25.1", "spec": None},
"llvm": {"version": "16", "spec": None},
"llvm-amdgpu": {"version": "6.0", "spec": None},
"intel-oneapi-compilers": {"version": "2024.1", "spec": None},
"intel-oneapi": {"version": "2024.1", "spec": None},
"intel-classic": {"version": "2021.10.0", "spec": None},
}
)
assert recipe.compilers["gcc"]["specs"] == ["gcc@13 +bootstrap"]
assert recipe.compilers["nvhpc"]["specs"] == ["nvhpc@25.1 ~mpi~blas~lapack"]
assert recipe.compilers["llvm"]["specs"] == ["llvm@16 +clang ~gold"]
assert recipe.compilers["llvm-amdgpu"]["specs"] == ["llvm-amdgpu@6.0"]
assert recipe.compilers["intel-oneapi-compilers"]["specs"] == ["intel-oneapi-compilers@2024.1"]
# the recipe key differs from the spack package name for the intel compilers
assert recipe.compilers["intel-oneapi"]["specs"] == ["intel-oneapi-compilers@2024.1"]
assert recipe.compilers["intel-oneapi"]["package"] == "intel-oneapi-compilers"
assert recipe.compilers["intel-classic"]["specs"] == ["intel-oneapi-compilers-classic@2021.10.0"]
assert recipe.compilers["intel-classic"]["package"] == "intel-oneapi-compilers-classic"
assert not recipe.use_system_gcc


Expand Down
2 changes: 2 additions & 0 deletions unittests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ def test_compilers_yaml(yaml_path):
assert raw["gcc"] == {"version": "11", "spec": "~bootstrap+nvptx"}
assert raw["llvm"] == {"version": "13", "spec": None}
assert raw["nvhpc"] == {"version": "25.1", "spec": None}
assert raw["intel-oneapi"] == {"version": "2024.1", "spec": None}
assert raw["intel-classic"] == {"version": "2021.10.0", "spec": None}

# spec must be a string (or null)
with pytest.raises(schema.ValidationError):
Expand Down
4 changes: 4 additions & 0 deletions unittests/yaml/compilers.full.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ nvhpc:
version: "25.1"
llvm-amdgpu:
version: "6.3"
intel-oneapi:
version: "2024.1"
intel-classic:
version: "2021.10.0"
Loading