Building from Source
This page covers the SPIRES build system in detail: Makefile targets, compiler flags, platform-specific setup, library output formats, the test suite, and cross-compilation.
Makefile Targets
Run these from the repository root.
| Target | What It Builds |
|---|---|
make or make all | Both libspires.a (static) and libspires.so (shared) in build/ |
make static | Only the static library build/libspires.a |
make shared | Only the shared library build/libspires.so (or .dylib on macOS) |
make test | Build and run the test suite |
make clean | Remove all build artifacts |
make install | Install headers to /usr/local/include and libraries to /usr/local/lib (may require sudo) |
Compiler Flags
The default Makefile applies these flags:
CFLAGS = -O2 -Wall -Wextra -std=c11 -fopenmp -march=native| Flag | Purpose |
|---|---|
-O2 | Optimization level 2. Good balance of speed and compile time. Use -O3 if you want more aggressive loop/vectorization optimizations at the cost of longer builds. |
-Wall -Wextra | Enable most compiler warnings. SPIRES builds warning-free under both GCC and Clang. |
-std=c11 | Use the C11 standard. Required for designated initializers and _Atomic support. |
-fopenmp | Enable OpenMP threading for parallel reservoir operations (matrix-vector products, state updates). |
-march=native | Generate code tuned to the host CPU’s instruction set (AVX2, NEON, etc.). Remove this flag when cross-compiling. |
Warning:
-march=nativeproduces binaries that may not run on a different CPU microarchitecture. For portable builds, replace it with a specific target like-march=x86-64-v2or remove it entirely.
Platform-Specific Notes
macOS (Homebrew)
Apple Clang does not ship OpenMP or OpenBLAS. Install them with Homebrew and tell the compiler where to find them.
brew install openblas lapack libompThen build with the Homebrew paths:
make \
CFLAGS="-O2 -std=c11 -march=native \
-Xpreprocessor -fopenmp \
-I$(brew --prefix libomp)/include \
-I$(brew --prefix openblas)/include" \
LDFLAGS="-L$(brew --prefix libomp)/lib -lomp \
-L$(brew --prefix openblas)/lib -lopenblas \
-llapacke -lm"Tip: If you see
ld: library 'omp' not found, verify thatlibompis installed and theLDFLAGSpath is correct. Runbrew --prefix libompto confirm the installation directory.
Apple Silicon (M1/M2/M3/M4) Macs will automatically benefit from -march=native generating NEON-optimized code.
Ubuntu / Debian
sudo apt install build-essential libopenblas-dev liblapacke-dev libomp-devThen build normally:
makeGCC on Ubuntu includes OpenMP support by default. No extra flags are needed beyond the Makefile defaults.
Fedora / RHEL
sudo dnf install gcc openblas-devel lapack-devel libomp-develThen build normally:
makeStatic vs Shared Library
SPIRES builds both a static (.a) and a shared (.so / .dylib) library.
Static Linking
gcc -o my_program my_program.c \
-I/path/to/spires/include \
/path/to/spires/build/libspires.a \
-lopenblas -llapacke -lm -fopenmpThe entire SPIRES library is embedded in your binary. No runtime dependency on libspires, though you still depend on OpenBLAS and LAPACKE at runtime.
Shared Linking
gcc -o my_program my_program.c \
-I/path/to/spires/include \
-L/path/to/spires/build \
-lspires -lopenblas -llapacke -lm -fopenmpAt runtime the dynamic linker must find libspires.so. Set the appropriate environment variable:
# Linux
export LD_LIBRARY_PATH=/path/to/spires/build:$LD_LIBRARY_PATH
# macOS
export DYLD_LIBRARY_PATH=/path/to/spires/build:$DYLD_LIBRARY_PATHAlternatively, install the library to a system path with make install.
Tip: For deployment, static linking avoids library-path issues. For development, shared linking gives faster incremental rebuilds since you only relink your program, not the library.
Running the Test Suite
make testThis compiles and runs all tests. The output reports the number of tests passed and failed. A successful run looks like:
Running SPIRES test suite...
[PASS] test_create_destroy
[PASS] test_step_single
[PASS] test_ridge_sine
[PASS] test_online_convergence
[PASS] test_state_copy
[PASS] test_reset
...
All tests passed.To run tests under Valgrind for memory-leak checking:
valgrind --leak-check=full ./build/test_spiresTip: If you have modified neuron models or weight initialization, run the full test suite before committing to catch regressions early.
Troubleshooting Common Build Issues
Missing OpenBLAS Headers
Symptom:
fatal error: cblas.h: No such file or directoryFix: Install the OpenBLAS development package (see Platform-Specific Notes above). If the headers are in a non-standard location, add the path:
make CFLAGS="-O2 -std=c11 -fopenmp -I/usr/include/openblas"On some distributions the header is at /usr/include/x86_64-linux-gnu/cblas.h. Adjust the include path accordingly.
Missing LAPACKE
Symptom:
fatal error: lapacke.h: No such file or directoryFix: Install liblapacke-dev (Debian/Ubuntu) or lapack-devel (Fedora/RHEL). On macOS with Homebrew:
brew install lapack
make CFLAGS="-O2 -std=c11 -I$(brew --prefix lapack)/include" \
LDFLAGS="-L$(brew --prefix lapack)/lib -llapacke"OpenMP Not Found on macOS
Symptom:
clang: error: unsupported option '-fopenmp'Fix: Apple Clang does not support -fopenmp directly. Install libomp via Homebrew and use the -Xpreprocessor -fopenmp flag instead:
brew install libomp
make CFLAGS="-O2 -std=c11 -Xpreprocessor -fopenmp -I$(brew --prefix libomp)/include" \
LDFLAGS="-L$(brew --prefix libomp)/lib -lomp"Alternatively, install GCC from Homebrew (brew install gcc) and build with CC=gcc-14 make.
Linker Errors for BLAS Symbols
Symptom:
undefined reference to `cblas_dgemv'Fix: The linker is not finding OpenBLAS. Ensure -lopenblas (or -lblas) appears in LDFLAGS and the library path is correct:
make LDFLAGS="-L/usr/lib/x86_64-linux-gnu -lopenblas -llapacke -lm"Build Succeeds but Tests Segfault
This usually means the BLAS library is incompatible with the rest of the toolchain (e.g., mixing 32-bit and 64-bit integer interfaces). Ensure all libraries agree on sizeof(int):
# Check OpenBLAS integer width
nm /usr/lib/libopenblas.so | grep ilp64If your OpenBLAS uses 64-bit integers (ILP64), you may need to define OPENBLAS_USE64BITINT at compile time or switch to a 32-bit-integer build of OpenBLAS.
Cross-Compilation
To build SPIRES for a target architecture different from your host, override CC, CFLAGS, and LDFLAGS.
Example: x86_64 Host to aarch64 Target
sudo apt install gcc-aarch64-linux-gnu libopenblas-dev:arm64
make CC=aarch64-linux-gnu-gcc \
CFLAGS="-O2 -std=c11 -fopenmp -march=armv8-a" \
LDFLAGS="-lopenblas -llapacke -lm -lgomp"Key points for cross-compilation:
- Remove
-march=native— it generates code for your host CPU, not the target. - Specify the target architecture explicitly — e.g.,
-march=armv8-afor 64-bit ARM. - Use cross-compiled versions of OpenBLAS and LAPACKE — the host libraries will not work on the target.
- Test on the target or in an emulator (
qemu-aarch64) before deploying.
Example: Building for Embedded Linux (No OpenMP)
If the target does not have OpenMP support:
make CC=arm-linux-gnueabihf-gcc \
CFLAGS="-O2 -std=c11 -march=armv7-a -mfpu=neon" \
LDFLAGS="-lopenblas -llapacke -lm" \
DISABLE_OPENMP=1Tip: Disabling OpenMP means all reservoir operations run single-threaded. This is fine for small reservoirs (under ~1000 neurons) and avoids threading overhead on single-core embedded targets.