SCLS, the Scientific Core Library Stack, is an opinionated build and packaging system for scientific computing libraries.
The project exists to solve a specific problem: getting a consistent, usable stack of numerical libraries built and installed across real machines, not idealized ones. That includes modern Enterprise Linux systems with RPM packaging, Debian/Ubuntu systems with DEB packaging, older or awkward Linux environments where native packaging is not an option, and macOS on Intel and, since the first verified GCC bootstrap on 2026-09-10, Apple Silicon (the rest of the stack there is still in bring-up).
Scientific Core Library Stack (SCLS) Copyright (c) 2026, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy). All rights reserved.
If you have questions about your rights to use or distribute this software, please contact Berkeley Lab's Intellectual Property Office at IPO@lbl.gov.
NOTICE. This Software was developed under funding from the U.S. Department of Energy and the U.S. Government consequently retains certain rights. As such, the U.S. Government has been granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software to reproduce, distribute copies to the public, prepare derivative works, and perform publicly and display publicly, and to permit others to do so.
SCLS is licensed under the Lawrence Berkeley National Laboratory BSD variant, SPDX identifier BSD-3-Clause-LBNL. See LICENSE.
SCLS builds libraries that are meant to work together as a coherent stack: BLAS/LAPACK, MPI-enabled math libraries, sparse solvers, graph partitioners, I/O libraries, and their dependencies.
The goal is not to expose every possible build option. The goal is to produce a stack that is:
- consistent
- reproducible
- installable
- usable by downstream scientific software without constant manual repair
On systems where RPM integration makes sense, SCLS aims to produce packages that can be installed with a normal package manager workflow such as dnf. On systems where that is not realistic, SCLS still aims to build and install the same stack directly with Unix-style prefix installs.
SCLS is deliberately opinionated.
This is not a general-purpose meta-build framework. It makes choices so the resulting stack remains coherent:
- GCC is the default toolchain because it is open, reliable, and has strong Fortran support.
- MKL and OpenBLAS are treated as backend choices selected by flavor (
mklvsgcc), not as an invitation to rebuild everything ad hoc. - LP64 is the default integer model; ILP64 is supported when it is actually needed.
- OpenMPI is the default MPI implementation.
- Shared libraries are the normal target.
- Numerical correctness matters more than aggressive flag games; this project is not interested in
-ffast-math. - Python is not part of the stack. Interpreter choice is too site- and user-specific (system, pyenv, conda, spack, modules), so SCLS does not ship a Python and recipes disable Python (and other language) bindings by default. Users bring their own interpreter and bind against the installed C/C++ libraries themselves.
- Boost is not part of the stack. Modern C++ (C++17/20) has absorbed most of what scientific code historically needed from Boost (
filesystem,optional,variant,any,string_view), and the libraries SCLS cares about either never required Boost or have dropped the dependency. Adding Boost would mean pulling in a very large, slow-to-build tree of sublibraries to satisfy a shrinking set of optional features. Where a recipe offers a "build tests with Boost" switch, SCLS disables it. - Published binaries carry BSD-3-Clause-compatible licenses only: permissive, or weak copyleft (LGPL, CeCILL-C, EPL-2.0) whose source and notice obligations SCLS can satisfy. No GPL library, of any version, is part of a distributed binary flavor. GPL build tools are allowed in the build scripts, because macOS and Unix systems without SCLS binaries may need them; they are executed during the build, not linked into the delivered libraries (the GCC runtime is linked, under the GCC Runtime Library Exception). See
doc/LICENSE_POLICY.mdfor the package policy and rationale. - Binaries depend only on what the recipe declares, not on what the build host happens to have. Autodetection at configure or Makefile time is a silent-failure pattern: a missing probe produces a binary that quietly lacks features the recipe claimed it would have. OpenBLAS's OpenMP support is the canonical case — when
getconf _NPROCESSORS_ONLNreturns 1 on a single-CPU build host (mockbuild container, CI runner with cgroup limits, small VM), OpenBLAS silently builds sequential regardless ofUSE_OPENMP=1. Recipes therefore declare explicit caps and build dependencies so the same recipe produces the same binary across build hosts.
The bias is toward a curated stack rather than an infinitely configurable one. If the requirement is "I need exactly seventeen custom feature toggles for one package," this is the wrong tool. If the requirement is "I need PETSc, HDF5, OpenBLAS, NetCDF, MUMPS, and friends to build and coexist sanely," this is exactly the kind of tool that helps.
Sometimes you should.
But in scientific and HPC environments, system packages often stop being enough:
- the distro is too old
- the available versions do not line up well
- the ABI story is inconsistent across dependencies
- the machine is locked down
- the platform is unusual
- the package set is incomplete
SCLS exists for the cases where you still need a real stack, and you want one build model across Linux and macOS rather than a pile of unrelated one-off installs.
SCLS supports multiple delivery paths built from the same recipe and flavor model.
On RHEL-family Enterprise Linux systems such as RHEL, Rocky Linux, AlmaLinux, CentOS Stream, and Amazon Linux 2023, SCLS can generate SPEC files and build RPMs. This is the preferred route when the target system supports it, because it gives you normal package-manager installation and removal semantics.
On Debian/Ubuntu hosts SCLS produces .deb packages from the same recipes and flavors. The builder stages into a DESTDIR buildroot and wraps it with dpkg-deb --build, the DEB analogue of the RPM path. A 3.0 (quilt) source-package triplet (.dsc + .orig.tar.<ext> + .debian.tar.xz) is produced alongside the binary for license-compliance parity with source RPMs. Recipe rpm_build_requires and rpm_requires are translated to Debian names via packaging/system_packages.yaml; any unknown RHEL name is a hard error, not a silent fallback.
DEB packaging is intentionally less feature-complete than RPM packaging. It can split recipe subpackages such as *-examples, but it does not currently build the optional all-examples meta-package that the RPM path emits. Adding full DEB meta-package parity would be extra packaging machinery for little practical improvement in the current stack.
SCLS also supports a direct Unix builder for environments where native packaging is not realistic or not desirable. This is useful for:
- older enterprise Linux systems
- HPC systems with limited admin control
- distributions outside the RPM path
- "Linux From Scratch"-style deployment into a controlled prefix
SCLS supports direct builds on macOS as well. The macos flavor is exercised regularly on Intel developer workstations. On Apple Silicon the aarch64-apple-darwin GCC branch that Homebrew carries is vendored in patches/gcc/ and applied automatically on arm64 hosts; the three-stage GCC bootstrap and the packages up to and including OpenMPI were first built and installed on an M2 Pro on 2026-09-10 (see doc/MACOS_BUILD.md). Everything after OpenMPI in the build order — ScaLAPACK, MUMPS, SCOTCH, the SLATE/STRUMPACK stack, PETSc, VTK — has not been built on arm64 yet and should be treated as untested there.
SCLS does not rely on a separate CI workflow as its primary test gate. Package builds automatically run the tests declared by each recipe, on the build host and for the selected platform and flavor. The complete build/install path is the meaningful validation: configure, compile, package, install, verify dependencies and installed versions, and run the upstream tests. This catches integration and ABI problems that a lightweight CI check would not.
Validation is therefore recorded per package, flavor, platform, and build host. A successful build on one flavor or architecture does not establish support for another; unbuilt or untested combinations remain explicitly marked as such.
The project is built around three core concepts.
recipes/ contains package definitions. Recipes describe version, sources, dependencies, build system behavior, tests, patches, and feature requirements.
flavors/ contains platform and toolchain choices. A flavor defines the target platform, compilers, optimization flags, math backend, MPI implementation, and install prefix. See Flavors above for the available flavors and their prefixes.
python/ contains the code that turns recipes and flavors into actual builds:
python/rpm_builder.py: RPM packaging for Enterprise Linux targetspython/deb_builder.py: Debian/Ubuntu.debpackaging, including 3.0 (quilt) source packagespython/unix_builder.py: direct Unix-style builds and installspython/build_common.py: shared build logicpython/build_order.py: dependency orderingpython/patch_common.py: patch selection and applicationpython/math_common.py: math-library-related configuration
recipes/: package definitionsflavors/: compiler/platform/math configurationspatches/: package patchestemplates/: SPEC, DEBIAN/control, and related templatespackaging/: cross-distribution packaging data (e.g. RHEL→Debian system-package name mapping)python/: builder implementationfiles/: tracked install manifestschangelogs/: package changelog sources (used by both RPM and DEB builders)rpmbuild/: local RPM build treedebbuild/: local DEB build tracking (marker files for build-order resolution)work/: downloaded sources and build artifacts (includingwork/pkgsfor binary.deboutput andwork/spkgsfor source packages)
A flavor defines the target platform, compilers, optimization flags, math backend, and install prefix. Each flavor installs into its own prefix, so multiple flavors can coexist.
| Flavor | Prefix | Compiler | Math | Notes |
|---|---|---|---|---|
gcc |
/opt/scls/gcc |
GCC | OpenBLAS | Default production build |
mkl |
/opt/scls/mkl |
GCC | Intel MKL | Requires intel-oneapi-mkl RPMs |
debug |
/opt/scls/debug |
GCC | Reference | -Og -g, for valgrind / sanitizers |
intel |
/opt/scls/intel |
Intel | Intel MKL | Requires Intel oneAPI compilers |
lbl |
custom site prefix | GCC | OpenBLAS | LBL site-specific |
macos |
/opt/scls |
GCC | OpenBLAS | Intel Macs; Apple Silicon in bring-up |
gcc-mkl-cuda |
/opt/scls/gcc-mkl-cuda |
GCC | Intel MKL | CUDA-enabled (NVIDIA HPC SDK); untested |
Flavors map to distinct deployment classes. The target informs which external dependencies are built against the system vs. supplied by the SCLS stack, and which schedulers and fabrics are pulled in.
| Flavor | Install target | Hardware / environment assumption |
|---|---|---|
lbl |
LBL HPC compute nodes | InfiniBand fabric and Slurm workload manager; builds against system PMIx, hwloc, libevent, and Slurm to stay binary-compatible with site infrastructure |
gcc / mkl / debug |
Cloud VMs (AWS, Azure, GCP), workstations, non-LBL HPC clusters — distributed via RPM/DEB repos | Mixed: TCP-only on commodity VMs and desktops through RDMA-capable fabric on cloud HPC instances and generic on-prem HPC clusters |
macos |
Developer workstation | TCP-only; macOS has no RDMA story |
Two policies follow from this table, and they're worth stating explicitly because the reasoning isn't symmetric.
Slurm is scoped strictly to lbl. Workstation and cloud installs of gcc/mkl/debug do not pull in a workload manager. Slurm is LBL-specific site infrastructure, not a general assumption — a researcher on an AWS instance or a laptop should not be forced to install libslurm-dev to get OpenMPI working.
High-performance fabrics are supported universally, except macOS where UCX isn't built at all. UCX in gcc/mkl/debug is configured with --with-verbs and --with-rdmacm. This matters for more than just on-prem HPC: AWS EFA (Elastic Fabric Adapter, available on hpc7a/p5/etc.) exposes itself through the libibverbs API, as do Azure HPC Mellanox SKUs. The runtime cost on a host with no fabric is ~500 KB of unused libraries that UCX dlopens and gracefully skips when no hardware is present; the cost of not supporting them universally is a ~10× MPI performance cliff on any cloud HPC or non-LBL HPC deployment of gcc/mkl/debug.
Slurm and RDMA fabric are correlated on traditional HPC clusters but not coupled. AWS HPC instances have EFA but no Slurm by default; the policies above handle that case correctly.
SCLS distinguishes the machine that produces packages from the machine that consumes them. RPMs and DEBs are typically produced once on a centralized build host (mock/pbuilder container, CI runner, maintainer workstation) and then distributed via a package repository or shared storage to many install hosts. Recipe rpm_build_requires / rpm_requires declarations describe the build host's and install host's needs respectively; the SCLS requires: field describes inter-package dependencies inside the stack.
The install host does need a compiler: SCLS is a development stack and users build against it, so scls-<flavor>-environment requires the toolchain the flavor was built with (gcc, gcc-c++, gcc-gfortran, glibc-devel, make on RHEL-family hosts; gcc, g++, gfortran, libc6-dev, make on Debian/Ubuntu) and recommends doxygen. The lbl flavor ships its own GCC and needs only make. The intel flavor cannot express its compiler as a distro package: install Intel oneAPI (icx/ifx) from Intel's repository before installing the stack.
The practical consequence: installing libibverbs-dev on your build host to compile scls-gcc-ucx does not mean the resulting package will require libibverbs-dev at install time. It requires only libibverbs1 (the runtime soname package), which is tiny and harmless on machines without IB hardware.
The scls wrapper is the recommended entry point. It reads flavor.conf (YAML) to determine the active flavor and dispatches to the appropriate builder.
# flavor.conf
flavor: gcc
# gcc_toolset: 15 # uncomment on RHEL 8 to use Red Hat gcc-toolset-15
# extra_packages: # uncomment to build packages not in the flavor's allowlist
# - binutils
# - gccBuild and install the full flavor in dependency order:
./scls build allFor a granular or resumable flow, build and install the next package:
./scls build next
./scls install nextBuild a specific package:
./scls build petsc
./scls install petscOther commands:
./scls spec petsc # Generate SPEC file only (RPM mode)
./scls list # List installed packages
./scls order # Show build order
./scls check-updates # Check upstream sources for newer versionsEach installed flavor ships activation scripts at both activate and
activate.sh. The canonical short form is:
source /opt/scls/<flavor>/share/scls/activateActivation adds the prefix's bin/ directory to PATH, exposes package
metadata through PKG_CONFIG_PATH and CMAKE_PREFIX_PATH, and exports
package-specific variables when the relevant package is installed. It does not
add the stack's lib/ directory to LD_LIBRARY_PATH or DYLD_LIBRARY_PATH;
runtime lookup is handled by rpaths. The script does remove stale SCLS entries
from loader-path variables when switching between flavors.
For PETSc, PETSC_DIR points at the installed SCLS prefix and PETSC_ARCH is intentionally empty:
PETSC_DIR=/opt/scls/<flavor>
PETSC_ARCH=
SCLS installs PETSc as an installed-prefix package, not as an in-place PETSc source tree with a runtime architecture directory, so consume it through PETSC_DIR, pkg-config, CMake, or PETSc's installed configuration files under lib/petsc/conf.
PETSc, SLEPc, and SUNDIALS install their upstream tutorial/example sources under %{prefix}/share/<package>/examples/. In RPM builds, these are split into <package>-examples subpackages and are not pulled in by the flavor meta-package. An optional companion meta-package, scls-<flavor>-examples, is built alongside scls-<flavor> and groups all of them; install it if you want the examples on disk. DEB builds produce the per-package example subpackages but do not currently produce an all-examples meta-package.
For cases where the wrapper is not suitable:
# RPM SPEC file only (Enterprise Linux):
python python/rpm_builder.py --package <package> --flavor <flavor> --spec-only
# Build RPM package (Enterprise Linux):
python python/rpm_builder.py --package <package> --flavor <flavor>
# Generic Unix-style build/install:
python python/unix_builder.py --package <package> --flavor <flavor> build install
# macOS build with PKG creation:
python python/unix_builder.py --package <package> --flavor macos build install pkg
# Build order:
python python/build_order.py recipes --flavor <flavor>- RPM builds use the project-local
rpmbuild/tree. - Sources are cached under
work/. - Patches are stored under
patches/<package>/. - File manifests live under
files/. - Package metadata for installed stacks is tracked in the SCLS registry under the chosen prefix.
CLAUDE.mdcontains repository guidance for Claude Code.CODEX.mdpoints Codex-style agents at the same repository guidance.
SCLS is a pragmatic working project, not a polished framework. The implementation is opinionated because the problem is opinionated: scientific libraries either fit together as a stack or they do not.
That tradeoff is intentional.