|
| 1 | +# PyNN test environment — three options for running the test suite |
| 2 | +# |
| 3 | +# Option A (native): builds NEST from source into a project-local venv+prefix |
| 4 | +# Option B (Docker): runs tests in an Ubuntu container with bind-mounted source |
| 5 | +# Option C (conda): uses micromamba + conda-forge (no compilation) |
| 6 | +# |
| 7 | +# All targets accept NEST_VERSION=<version> to switch NEST versions. |
| 8 | +# |
| 9 | +# Version string format differs between Options A/B and C for pre-releases: |
| 10 | +# stable: NEST_VERSION=3.9 (same for all options) |
| 11 | +# pre-release: GitHub tag format for A/B (e.g. 3.10.0rc1) |
| 12 | +# conda-forge format for C (e.g. 3.10_rc1) |
| 13 | +# Check https://github.com/nest/nest-simulator/releases for GitHub tag names. |
| 14 | + |
| 15 | +NEST_VERSION ?= 3.9 |
| 16 | + |
| 17 | +# ── Option A: paths and variables ───────────────────────────────────────────── |
| 18 | +NEST_PREFIX = $(CURDIR)/.local-nest$(NEST_VERSION) |
| 19 | +NEST_PY = $(NEST_PREFIX)/bin/python3 |
| 20 | +NEST_PIP = $(NEST_PREFIX)/bin/pip |
| 21 | +NEST_PYTEST = $(NEST_PREFIX)/bin/pytest |
| 22 | +NEST_VENV_STAMP = $(NEST_PREFIX)/.venv-ready |
| 23 | +NEST_STAMP = $(NEST_PREFIX)/.nest-installed |
| 24 | +NEST_SRC_DIR = $(CURDIR)/.nest-src |
| 25 | +NEST_BUILD_DIR = $(CURDIR)/.nest-build/nest-$(NEST_VERSION) |
| 26 | +NEST_TARBALL = $(NEST_SRC_DIR)/nest-$(NEST_VERSION).tar.gz |
| 27 | +NEST_SRC_UNPACKED = $(NEST_SRC_DIR)/nest-simulator-$(NEST_VERSION) |
| 28 | +NPROC = $(shell sysctl -n hw.logicalcpu 2>/dev/null || echo 4) |
| 29 | +# cmake will search /usr/local by default; override if your C deps are elsewhere |
| 30 | +EXTRA_CMAKE_ARGS ?= |
| 31 | + |
| 32 | +# ── Option B: variables ──────────────────────────────────────────────────────── |
| 33 | +DOCKER_IMAGE = pynn-test:nest$(NEST_VERSION) |
| 34 | +# Pass NEST_VERSION as env var so docker-compose.yml substitution picks it up |
| 35 | +COMPOSE = NEST_VERSION=$(NEST_VERSION) docker compose -f test/docker-compose.yml |
| 36 | + |
| 37 | +# ── Option C: variables ──────────────────────────────────────────────────────── |
| 38 | +MICROMAMBA ?= micromamba |
| 39 | +CONDAENV_PREFIX = $(CURDIR)/.condaenv-nest$(NEST_VERSION) |
| 40 | +CONDA_ENV_FILE = test/environment-nest$(NEST_VERSION).yml |
| 41 | + |
| 42 | +# ══════════════════════════════════════════════════════════════════════════════ |
| 43 | +# Option A: native venv + NEST built from source |
| 44 | +# ══════════════════════════════════════════════════════════════════════════════ |
| 45 | + |
| 46 | +.PHONY: setup |
| 47 | +setup: $(NEST_STAMP) ## [A] Build NEST $(NEST_VERSION) from source + create venv |
| 48 | + @echo "" |
| 49 | + @echo "Setup complete. Run 'make test NEST_VERSION=$(NEST_VERSION)'" |
| 50 | + |
| 51 | +# 1. Create venv and install Python build prerequisites |
| 52 | +# mpi4py must be compiled against the same MPI that NEST will link to, |
| 53 | +# and must be present before cmake runs so NEST can detect it. |
| 54 | +$(NEST_VENV_STAMP): |
| 55 | + python3 -m venv $(NEST_PREFIX) |
| 56 | + $(NEST_PIP) install --upgrade pip |
| 57 | + $(NEST_PIP) install "cython<3.1.0" |
| 58 | + MPICC=$(MPI_ROOT)/bin/mpicc \ |
| 59 | + $(NEST_PIP) install --no-binary=mpi4py mpi4py |
| 60 | + touch $@ |
| 61 | + |
| 62 | +# 2. Download NEST tarball |
| 63 | +$(NEST_TARBALL): |
| 64 | + @mkdir -p $(NEST_SRC_DIR) |
| 65 | + curl -fL -o $@ \ |
| 66 | + https://github.com/nest/nest-simulator/archive/refs/tags/v$(NEST_VERSION).tar.gz |
| 67 | + |
| 68 | +# 3. Unpack source and apply backport of NEST PR #3794: |
| 69 | +# "Add missing include needed on macOS 26.4" — <cstddef> was previously |
| 70 | +# available in numerics.h only via transitive includes that macOS 26 removed. |
| 71 | +$(NEST_SRC_UNPACKED): $(NEST_TARBALL) |
| 72 | + tar xzf $< -C $(NEST_SRC_DIR) |
| 73 | + sed -i '' 's|#include <cmath>|#include <cmath>\n#include <cstddef>|' \ |
| 74 | + $(NEST_SRC_UNPACKED)/libnestutil/numerics.h |
| 75 | + touch $@ |
| 76 | + |
| 77 | +# 4. cmake build, install, then install remaining Python deps |
| 78 | +$(NEST_STAMP): $(NEST_VENV_STAMP) $(NEST_SRC_UNPACKED) |
| 79 | + mkdir -p $(NEST_BUILD_DIR) |
| 80 | + cd $(NEST_BUILD_DIR) && cmake \ |
| 81 | + -DCMAKE_INSTALL_PREFIX=$(NEST_PREFIX) \ |
| 82 | + -DPython_EXECUTABLE=$(NEST_PY) \ |
| 83 | + -Dwith-mpi=ON \ |
| 84 | + -Dwith-python=ON \ |
| 85 | + -Dwith-gsl=ON \ |
| 86 | + -Dwith-ltdl=ON \ |
| 87 | + -Dwith-openmp=OFF \ |
| 88 | + $(EXTRA_CMAKE_ARGS) \ |
| 89 | + $(NEST_SRC_UNPACKED) |
| 90 | + cd $(NEST_BUILD_DIR) && make -j$(NPROC) |
| 91 | + cd $(NEST_BUILD_DIR) && make install |
| 92 | + # Build and install PyNN NEST extensions (pynn_extensions module) |
| 93 | + mkdir -p $(NEST_BUILD_DIR)/pynn_extensions |
| 94 | + cd $(NEST_BUILD_DIR)/pynn_extensions && cmake \ |
| 95 | + -Dwith-nest=$(NEST_PREFIX)/bin/nest-config \ |
| 96 | + $(EXTRA_CMAKE_ARGS) \ |
| 97 | + $(CURDIR)/pyNN/nest/extensions |
| 98 | + cd $(NEST_BUILD_DIR)/pynn_extensions && make install |
| 99 | + $(NEST_PIP) install \ |
| 100 | + "neuron>=9.0.0" nrnutils "arbor==0.9.0" \ |
| 101 | + brian2 libNeuroML scipy matplotlib Cheetah3 h5py Jinja2 \ |
| 102 | + pytest pytest-xdist pytest-cov flake8 |
| 103 | + $(NEST_PIP) install -e . |
| 104 | + # Compile NEURON .mod mechanisms against the venv's NEURON. |
| 105 | + # The compiled arm64/ dir lives in the source tree and is version-specific, |
| 106 | + # so it must be rebuilt whenever the NEURON version changes. |
| 107 | + cd $(CURDIR)/pyNN/neuron/nmodl && $(NEST_PREFIX)/bin/nrnivmodl . |
| 108 | + touch $@ |
| 109 | + |
| 110 | +.PHONY: test |
| 111 | +test: $(NEST_STAMP) ## [A] Run full test suite (NEST_VERSION=$(NEST_VERSION)) |
| 112 | + $(NEST_PYTEST) -v -n auto test/ |
| 113 | + |
| 114 | +.PHONY: test-unit |
| 115 | +test-unit: $(NEST_STAMP) ## [A] Unit tests only (no simulator needed) |
| 116 | + $(NEST_PYTEST) -n auto test/unittests/ |
| 117 | + |
| 118 | +.PHONY: test-nest |
| 119 | +test-nest: $(NEST_STAMP) ## [A] NEST system + scenario tests |
| 120 | + $(NEST_PYTEST) -n auto test/system/test_nest.py test/system/scenarios/ |
| 121 | + |
| 122 | +.PHONY: test-neuron |
| 123 | +test-neuron: $(NEST_STAMP) ## [A] NEURON system + scenario tests |
| 124 | + $(NEST_PYTEST) -n auto test/system/test_neuron.py test/system/scenarios/ |
| 125 | + |
| 126 | +.PHONY: test-brian2 |
| 127 | +test-brian2: $(NEST_STAMP) ## [A] Brian2 system tests |
| 128 | + $(NEST_PYTEST) -n auto test/system/test_brian2.py |
| 129 | + |
| 130 | +.PHONY: clean-nmodl |
| 131 | +clean-nmodl: ## [A] Remove compiled NEURON mechanisms (required before switching NEURON versions) |
| 132 | + rm -rf $(CURDIR)/pyNN/neuron/nmodl/arm64 \ |
| 133 | + $(CURDIR)/pyNN/neuron/nmodl/x86_64 |
| 134 | + |
| 135 | +.PHONY: clean |
| 136 | +clean: ## [A] Remove .local-nest$(NEST_VERSION)/ (triggers full rebuild) |
| 137 | + rm -rf $(NEST_PREFIX) |
| 138 | + |
| 139 | +.PHONY: clean-build |
| 140 | +clean-build: ## [A] Remove cmake build dir only (retry after cmake failure) |
| 141 | + rm -rf $(NEST_BUILD_DIR) |
| 142 | + |
| 143 | +.PHONY: clean-all |
| 144 | +clean-all: ## [A] Remove all .local-nest*/, .nest-build/, .nest-src/ |
| 145 | + rm -rf .local-nest*/ .nest-build/ .nest-src/ |
| 146 | + |
| 147 | +# ══════════════════════════════════════════════════════════════════════════════ |
| 148 | +# Option B: Docker with bind-mounted source |
| 149 | +# ══════════════════════════════════════════════════════════════════════════════ |
| 150 | + |
| 151 | +.PHONY: docker-build |
| 152 | +docker-build: ## [B] Build Docker image pynn-test:nest$(NEST_VERSION) |
| 153 | + $(COMPOSE) build |
| 154 | + |
| 155 | +.PHONY: docker-test |
| 156 | +docker-test: ## [B] Run full test suite in Docker |
| 157 | + $(COMPOSE) run --rm pynn pytest -v -n auto test/ |
| 158 | + |
| 159 | +.PHONY: docker-test-unit |
| 160 | +docker-test-unit: ## [B] Unit tests in Docker |
| 161 | + $(COMPOSE) run --rm pynn pytest -n auto test/unittests/ |
| 162 | + |
| 163 | +.PHONY: docker-test-nest |
| 164 | +docker-test-nest: ## [B] NEST tests in Docker |
| 165 | + $(COMPOSE) run --rm pynn pytest -n auto test/system/test_nest.py test/system/scenarios/ |
| 166 | + |
| 167 | +.PHONY: docker-test-neuron |
| 168 | +docker-test-neuron: ## [B] NEURON tests in Docker |
| 169 | + $(COMPOSE) run --rm pynn pytest -n auto test/system/test_neuron.py test/system/scenarios/ |
| 170 | + |
| 171 | +.PHONY: docker-test-brian2 |
| 172 | +docker-test-brian2: ## [B] Brian2 tests in Docker |
| 173 | + $(COMPOSE) run --rm pynn pytest -n auto test/system/test_brian2.py |
| 174 | + |
| 175 | +.PHONY: docker-shell |
| 176 | +docker-shell: ## [B] Interactive bash inside Docker container |
| 177 | + $(COMPOSE) run --rm pynn bash |
| 178 | + |
| 179 | +# ══════════════════════════════════════════════════════════════════════════════ |
| 180 | +# Option C: micromamba + conda-forge (no compilation) |
| 181 | +# Install micromamba first: "${SHELL}" <(curl -L micro.mamba.pm/install.sh) |
| 182 | +# conda-forge RC version string uses underscore: e.g. NEST_VERSION=3.10_rc1 |
| 183 | +# ══════════════════════════════════════════════════════════════════════════════ |
| 184 | + |
| 185 | +.PHONY: conda-setup |
| 186 | +conda-setup: ## [C] Create micromamba env .condaenv-nest$(NEST_VERSION)/ |
| 187 | + @test -f $(CONDA_ENV_FILE) || \ |
| 188 | + (echo "Error: $(CONDA_ENV_FILE) not found."; \ |
| 189 | + echo "Copy test/environment-nest3.9.yml and update the nest-simulator pin."; \ |
| 190 | + exit 1) |
| 191 | + $(MICROMAMBA) env create -p $(CONDAENV_PREFIX) -f $(CONDA_ENV_FILE) -y |
| 192 | + $(MICROMAMBA) run -p $(CONDAENV_PREFIX) pip install -e . |
| 193 | + |
| 194 | +.PHONY: conda-test |
| 195 | +conda-test: ## [C] Run full test suite via micromamba |
| 196 | + $(MICROMAMBA) run -p $(CONDAENV_PREFIX) pytest -v -n auto test/ |
| 197 | + |
| 198 | +.PHONY: conda-test-unit |
| 199 | +conda-test-unit: ## [C] Unit tests via micromamba |
| 200 | + $(MICROMAMBA) run -p $(CONDAENV_PREFIX) pytest -n auto test/unittests/ |
| 201 | + |
| 202 | +.PHONY: conda-test-nest |
| 203 | +conda-test-nest: ## [C] NEST tests via micromamba |
| 204 | + $(MICROMAMBA) run -p $(CONDAENV_PREFIX) \ |
| 205 | + pytest -n auto test/system/test_nest.py test/system/scenarios/ |
| 206 | + |
| 207 | +.PHONY: conda-test-neuron |
| 208 | +conda-test-neuron: ## [C] NEURON tests via micromamba |
| 209 | + $(MICROMAMBA) run -p $(CONDAENV_PREFIX) \ |
| 210 | + pytest -n auto test/system/test_neuron.py test/system/scenarios/ |
| 211 | + |
| 212 | +.PHONY: conda-test-brian2 |
| 213 | +conda-test-brian2: ## [C] Brian2 tests via micromamba |
| 214 | + $(MICROMAMBA) run -p $(CONDAENV_PREFIX) pytest -n auto test/system/test_brian2.py |
| 215 | + |
| 216 | +.PHONY: conda-shell |
| 217 | +conda-shell: ## [C] Interactive shell via micromamba |
| 218 | + $(MICROMAMBA) run -p $(CONDAENV_PREFIX) bash |
| 219 | + |
| 220 | +.PHONY: conda-clean |
| 221 | +conda-clean: ## [C] Remove .condaenv-nest$(NEST_VERSION)/ |
| 222 | + rm -rf $(CONDAENV_PREFIX) |
| 223 | + |
| 224 | +# ══════════════════════════════════════════════════════════════════════════════ |
| 225 | +# Help |
| 226 | +# ══════════════════════════════════════════════════════════════════════════════ |
| 227 | + |
| 228 | +.PHONY: help |
| 229 | +help: ## Show available targets |
| 230 | + @echo "Usage: make <target> [NEST_VERSION=3.9]" |
| 231 | + @echo "" |
| 232 | + @grep -E '^[a-zA-Z0-9_-]+:.*##' $(MAKEFILE_LIST) | \ |
| 233 | + awk 'BEGIN {FS = ":.*##"}; {printf " %-26s %s\n", $$1, $$2}' |
| 234 | + @echo "" |
| 235 | + @echo "Current NEST_VERSION: $(NEST_VERSION)" |
| 236 | + |
| 237 | +.DEFAULT_GOAL := help |
0 commit comments