Skip to content

Commit 12281c2

Browse files
JCGoranmatz-enrnhines
authored
cmake: export targets, allow building mechanisms via CMake, part 1 (#3468)
This PR aims to allow dependent projects to build their models via CMake rather than a combination of shell scripts and Makefiles. In the long term, this may help facilitate building natively on Windows. To try: ``` nrnivmodl-cmake ``` or ``` git clone -b imported-main https://github.com/BlueBrain/neurodamus-models.git cd neurodamus-models ``` Create a `CMakeLists.txt` with contents like: ```cmake cmake_minimum_required(VERSION 3.28) project(newrodamus) find_package(neuron REQUIRED) create_libnrnmech(MOD_FILES neocortex/mod/v6/CaDynamics_DC0.mod neocortex/mod/v6/Ca_HVA2.mod neocortex/mod/v6/Ca_LVAst.mod neocortex/mod/v6/DetAMPANMDA.mod neocortex/mod/v6/DetGABAAB.mod neocortex/mod/v6/GluSynapse.mod neocortex/mod/v6/Ih.mod neocortex/mod/v6/K_Pst.mod neocortex/mod/v6/K_Tst.mod neocortex/mod/v6/KdShu2007.mod neocortex/mod/v6/NaTg.mod neocortex/mod/v6/Nap_Et2.mod neocortex/mod/v6/ProbAMPANMDA_EMS.mod neocortex/mod/v6/ProbGABAAB_EMS.mod neocortex/mod/v6/SK_E2.mod neocortex/mod/v6/SKv3_1.mod neocortex/mod/v6/StochKv3.mod neocortex/mod/v6/TTXDynamicsSwitch.mod neocortex/mod/v6/VecStim.mod neocortex/mod/v6/gap.mod neocortex/mod/v6/netstim_inhpoisson.mod ) ``` Then build and install: ``` cmake -B build -S . -GNinja -DCMAKE_INSTALL_PREFIX=x86_64 cmake --build build cmake --install build ``` --------- Co-authored-by: Matthias Wolf <matthias.wolf@epfl.ch> Co-authored-by: nrnhines <michael.hines@yale.edu>
1 parent 5c586fb commit 12281c2

26 files changed

Lines changed: 1085 additions & 110 deletions

.github/workflows/wheels-template.yml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ jobs:
7373
run: |
7474
brew install --cask xquartz
7575
brew uninstall cmake || echo "CMake was not pre-installed"
76-
brew install flex bison cmake mpich
76+
brew install flex bison cmake mpich ninja
7777
brew unlink mpich && brew install openmpi
7878
# Install newer version of Bash on MacOS
7979
brew install bash
@@ -127,7 +127,7 @@ jobs:
127127
if: runner.os == 'Linux'
128128
run: |
129129
sudo apt update
130-
sudo apt install -y mpich openmpi-bin libopenmpi-dev libmpich-dev
130+
sudo apt install -y mpich openmpi-bin libopenmpi-dev libmpich-dev ninja-build
131131
132132
- name: "Apply workaround for MPICH on Ubuntu 24.04"
133133
if: inputs.platform == 'ubuntu-24.04'
@@ -141,6 +141,22 @@ jobs:
141141
wget 'https://launchpad.net/ubuntu/+source/mpich/4.2.0-5.1/+build/28285882/+files/libmpich12_4.2.0-5.1_amd64.deb'
142142
sudo dpkg --install mpich_4.2.0-5.1_amd64.deb libmpich12_4.2.0-5.1_amd64.deb
143143
144+
- name: Set env vars for testing (Linux)
145+
if: startsWith(inputs.platform, "ubuntu")
146+
run: |
147+
echo CMAKE_BUILD_PARALLEL_LEVEL=4 >> $GITHUB_ENV
148+
echo CMAKE_GENERATOR=Ninja >> $GITHUB_ENV
149+
150+
- name: Set env vars for testing (MacOS)
151+
if: startsWith(runner.os, "macOS")
152+
run: |
153+
if [[ "${{runner.os}}" == "macOS-15-intel" ]]; then
154+
echo CMAKE_BUILD_PARALLEL_LEVEL=4 >> $GITHUB_ENV
155+
else
156+
echo CMAKE_BUILD_PARALLEL_LEVEL=3 >> $GITHUB_ENV
157+
fi
158+
echo CMAKE_GENERATOR=Ninja >> $GITHUB_ENV
159+
144160
- name: Test wheel with ${{ inputs.python_version }}
145161
run: |
146162
minor_version="$(python${{ inputs.python_version }} -c 'import sys;print(sys.version_info.minor)')"

CMakeLists.txt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,8 @@ endif()
587587
# initialize CLI11 submodule
588588
cpp_cc_git_submodule(CLI11 BUILD PACKAGE CLI11 REQUIRED)
589589

590+
# coreneuron targets will get propagated down from the subdirectory
591+
590592
# =============================================================================
591593
# Enable NMODL code-generator support
592594
# =============================================================================
@@ -1117,6 +1119,52 @@ if(NRN_MACOS_BUILD AND NOT SKBUILD)
11171119
nrn_macos_after_install()
11181120
endif()
11191121

1122+
# =============================================================================
1123+
# Install CMake glue
1124+
# =============================================================================
1125+
configure_file(cmake/neuronMechMaker.cmake
1126+
"${PROJECT_BINARY_DIR}/lib/cmake/neuron/neuronMechMaker.cmake" COPYONLY)
1127+
configure_file(cmake/mod_reg_nrn.cpp.in "${PROJECT_BINARY_DIR}/share/nrn/mod_reg_nrn.cpp.in"
1128+
COPYONLY)
1129+
configure_file(cmake/mod_reg_corenrn.cpp.in
1130+
"${PROJECT_BINARY_DIR}/share/nrn/mod_reg_corenrn.cpp.in" COPYONLY)
1131+
install(
1132+
TARGETS ${CORENRN_INSTALL_LIB_TARGETS}
1133+
EXPORT NeuronTargets
1134+
DESTINATION "${NRN_INSTALL_DATA_PREFIX}lib")
1135+
install(
1136+
TARGETS ${CORENRN_INSTALL_BIN_TARGETS}
1137+
EXPORT NeuronTargets
1138+
DESTINATION "${NRN_INSTALL_DATA_PREFIX}bin")
1139+
export(
1140+
EXPORT NeuronTargets
1141+
FILE ${PROJECT_BINARY_DIR}/lib/cmake/neuron/neuronTargets.cmake
1142+
NAMESPACE neuron::)
1143+
1144+
install(
1145+
EXPORT NeuronTargets
1146+
FILE neuronTargets.cmake
1147+
NAMESPACE neuron::
1148+
DESTINATION "${NRN_INSTALL_DATA_PREFIX}lib/cmake/neuron")
1149+
1150+
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/neuronConfig.cmake.in
1151+
${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/neuron/neuronConfig.cmake @ONLY)
1152+
1153+
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/neuron/neuronConfig.cmake
1154+
DESTINATION "${NRN_INSTALL_DATA_PREFIX}lib/cmake/neuron")
1155+
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/neuronMechMaker.cmake
1156+
DESTINATION "${NRN_INSTALL_DATA_PREFIX}lib/cmake/neuron")
1157+
1158+
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/mod_reg_nrn.cpp.in
1159+
${CMAKE_CURRENT_SOURCE_DIR}/cmake/mod_reg_corenrn.cpp.in
1160+
DESTINATION "${NRN_INSTALL_DATA_PREFIX}share/nrn")
1161+
1162+
# configuration and installation of nrnivmodl
1163+
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/nrnivmodl.cmake
1164+
${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/neuron/nrnivmodl/CMakeLists.txt @ONLY)
1165+
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lib/cmake/neuron/nrnivmodl/CMakeLists.txt
1166+
DESTINATION "${NRN_INSTALL_DATA_PREFIX}lib/cmake/neuron/nrnivmodl")
1167+
11201168
# =============================================================================
11211169
# Copy bash executable for windows
11221170
# =============================================================================

bin/CMakeLists.txt

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nrnpyenv.sh.in ${PROJECT_BINARY_DIR}/
4747
# Make sure nrnivmodl and neurondemo are executable in the build folder, so we can execute it to
4848
# prepare test files. This can be done more elegantly in newer CMake versions; v3.19+ have
4949
# file(CHMOD ...) and v3.20+ support setting permissions directly in configure_file(...).
50-
set(NRN_CONFIG_EXE_FILES "nrnivmodl" "neurondemo")
50+
set(NRNIVMODL_NEURON ON)
51+
set(NRN_CONFIG_EXE_FILES "nrnivmodl" "neurondemo" "nrnivmodl-cmake")
5152
foreach(NRN_CONFIG_EXE_FILE ${NRN_CONFIG_EXE_FILES})
5253
configure_file("${NRN_CONFIG_EXE_FILE}.in" "tmp/${NRN_CONFIG_EXE_FILE}" @ONLY)
5354
file(
@@ -62,13 +63,39 @@ foreach(NRN_CONFIG_EXE_FILE ${NRN_CONFIG_EXE_FILES})
6263
WORLD_READ
6364
WORLD_EXECUTE)
6465
endforeach()
66+
unset(NRNIVMODL_NEURON)
67+
if(NRN_ENABLE_CORENEURON)
68+
# nrnivmodl-all-cmake builds mechanisms for both NEURON and coreNEURON
69+
set(NRNIVMODL_NEURON ON)
70+
set(NRNIVMODL_CORENEURON ON)
71+
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/nrnivmodl-cmake.in
72+
"${CMAKE_CURRENT_BINARY_DIR}/tmp/nrnivmodl-all-cmake" @ONLY)
73+
file(
74+
COPY "${CMAKE_CURRENT_BINARY_DIR}/tmp/nrnivmodl-all-cmake"
75+
DESTINATION "${CMAKE_CURRENT_BINARY_DIR}"
76+
FILE_PERMISSIONS
77+
OWNER_READ
78+
OWNER_WRITE
79+
OWNER_EXECUTE
80+
GROUP_READ
81+
GROUP_EXECUTE
82+
WORLD_READ
83+
WORLD_EXECUTE)
84+
unset(NRNIVMODL_NEURON)
85+
unset(NRNIVMODL_CORENEURON)
86+
endif()
6587
file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/bin/tmp")
6688

6789
# =============================================================================
6890
# Install targets
6991
# =============================================================================
7092
install(PROGRAMS ${PROJECT_BINARY_DIR}/bin/nrngui ${PROJECT_BINARY_DIR}/bin/neurondemo
71-
${PROJECT_BINARY_DIR}/bin/nrnivmodl DESTINATION ${NRN_INSTALL_DATA_PREFIX}bin)
93+
${PROJECT_BINARY_DIR}/bin/nrnivmodl ${PROJECT_BINARY_DIR}/bin/nrnivmodl-cmake
94+
DESTINATION ${NRN_INSTALL_DATA_PREFIX}bin)
95+
if(NRN_ENABLE_CORENEURON)
96+
install(PROGRAMS ${PROJECT_BINARY_DIR}/bin/nrnivmodl-all-cmake
97+
DESTINATION ${NRN_INSTALL_DATA_PREFIX}bin)
98+
endif()
7299

73100
install(FILES ${PROJECT_BINARY_DIR}/bin/nrnmech_makefile DESTINATION ${NRN_INSTALL_DATA_PREFIX}bin)
74101
install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/sortspike ${CMAKE_CURRENT_SOURCE_DIR}/mkthreadsafe

bin/nrnivmodl-cmake.in

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env bash
2+
3+
# =============================================================================
4+
# Simple wrapper for CMake that builds mechanisms from mod files
5+
# =============================================================================
6+
7+
set -eu
8+
9+
# Where the output files will be placed
10+
bindir="$(uname -m)"
11+
12+
# The name of the current program
13+
program_name="$(basename "${0}")"
14+
15+
# Where the `*.cmake` files are located
16+
NRN_CMAKE_PREFIX_PATH_DEFAULT="$(readlink -f "$(dirname "${0}")/../lib/cmake/")"
17+
if [ -n "${CMAKE_PREFIX_PATH:-}" ]; then
18+
CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}:${NRN_CMAKE_PREFIX_PATH_DEFAULT}"
19+
else
20+
CMAKE_PREFIX_PATH="${NRN_CMAKE_PREFIX_PATH_DEFAULT}"
21+
fi
22+
export CMAKE_PREFIX_PATH
23+
24+
# On MacOS we need to set the deployment target to be equal to the one of NEURON
25+
if command -v xcrun >& /dev/null; then
26+
@NRN_OSX_BUILD_TRUE@export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"
27+
@NRN_OSX_BUILD_TRUE@export MACOSX_DEPLOYMENT_TARGET="@CMAKE_OSX_DEPLOYMENT_TARGET@"
28+
if [ -z "${MACOSX_DEPLOYMENT_TARGET}" ]; then
29+
unset MACOSX_DEPLOYMENT_TARGET
30+
fi
31+
fi
32+
33+
# We use the CMakeLists.txt from the NEURON installation directory to not have
34+
# to deal with any pre-existing CMakeLists.txt in the current directory
35+
srcdir="${NRN_CMAKE_PREFIX_PATH_DEFAULT}/neuron/nrnivmodl/"
36+
37+
# In case of no files, default to using the files in the current dir
38+
if [ $# -lt 1 ]; then
39+
set -- ./*.mod
40+
# In case of a single input, check if it's a directory; if it is, collect all mod files in it
41+
elif [ $# -eq 1 ] && [ -d "${1}" ]; then
42+
printf "[%s] Collecting mod files under %s\n" "${program_name}" "$(readlink -f "${1}")"
43+
set -- "${1}"/*.mod
44+
fi
45+
46+
# After collecting the mod files, check each mod file actually exists
47+
for mod_file in "$@"; do
48+
if [ ! -e "${mod_file}" ]; then
49+
printf "[%s] ERROR: Mod file %s does not exist!\n" "${program_name}" "${mod_file}" >&2
50+
exit 4
51+
fi
52+
done
53+
54+
# Convert all mod file paths to absolute paths because the source dir is in the NEURON install
55+
args=()
56+
for f in "$@"; do
57+
resolved=$(readlink -f "$f") || exit 1
58+
args+=("$resolved")
59+
done
60+
set -- "${args[@]}"
61+
62+
# TODO what if the mod filenames contain semicolons?
63+
modfiles=$(IFS=";"; echo "$*")
64+
65+
# Configure the mod files
66+
cmake \
67+
-S "${srcdir}" \
68+
-B "${bindir}" \
69+
-DNRNIVMODL_MOD_FILES="${modfiles}" \
70+
-DNRNIVMODL_NEURON=@NRNIVMODL_NEURON@ \
71+
-DNRNIVMODL_CORENEURON=@NRNIVMODL_CORENEURON@
72+
73+
# Actually build them
74+
cmake --build "${bindir}"

ci/azure-wheel-test-upload.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ steps:
1818

1919
- script: |
2020
export SKIP_EMBEDED_PYTHON_TEST=true
21+
export CMAKE_BUILD_PARALLEL_LEVEL=4
2122
packaging/python/test_wheels.sh $(which python) $(ls -t wheelhouse/*.whl)
2223
displayName: 'Test with System Python'
2324

ci/requirements.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2708,6 +2708,7 @@ sphinx==7.3.7 \
27082708
# sphinx-rtd-theme
27092709
# sphinxcontrib-jquery
27102710
# sphinxcontrib-mermaid
2711+
# sphinxcontrib-moderncmakedomain
27112712
sphinx-design==0.6.1 \
27122713
--hash=sha256:b11f37db1a802a183d61b159d9a202314d4d2fe29c163437001324fe2f19549c \
27132714
--hash=sha256:b44eea3719386d04d765c1a8257caca2b3e6f8421d7b3a5e742c0fd45f84e632
@@ -2750,6 +2751,10 @@ sphinxcontrib-mermaid==1.0.0 \
27502751
--hash=sha256:2e8ab67d3e1e2816663f9347d026a8dee4a858acdd4ad32dd1c808893db88146 \
27512752
--hash=sha256:60b72710ea02087f212028feb09711225fbc2e343a10d34822fe787510e1caa3
27522753
# via -r docs/docs_requirements.txt
2754+
sphinxcontrib-moderncmakedomain==3.29.0 \
2755+
--hash=sha256:22385d9956e9565311eca41a09f26688312e4997a7366cb965fa610d1fd1436c \
2756+
--hash=sha256:3587def241ff2577d0bbef11810a082e9dec1b78a3d4b4a066240b5f3dc1b5b2
2757+
# via -r docs/docs_requirements.txt
27532758
sphinxcontrib-qthelp==2.0.0 \
27542759
--hash=sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab \
27552760
--hash=sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb

cmake/mod_reg_corenrn.cpp.in

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <cstdio>
2+
namespace coreneuron {
3+
extern int nrnmpi_myid;
4+
extern int nrn_nobanner_;
5+
6+
@MECH_DECLARE@
7+
8+
void modl_reg() {
9+
if (!nrn_nobanner_) if (nrnmpi_myid < 1) {
10+
fprintf(stderr, " Additional mechanisms from files\n");
11+
@MECH_PRINT@
12+
fprintf(stderr, "\n\n");
13+
}
14+
@MECH_REGISTRE@
15+
}
16+
} //namespace coreneuron

cmake/mod_reg_nrn.cpp.in

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <stdio.h>
2+
#include "hocdec.h"
3+
extern int nrnmpi_myid;
4+
extern int nrn_nobanner_;
5+
6+
@MECH_DECLARE@
7+
8+
extern "C" void modl_reg() {
9+
if (!nrn_nobanner_) if (nrnmpi_myid < 1) {
10+
fprintf(stderr, "Additional mechanisms from files\n");
11+
@MECH_PRINT@
12+
fprintf(stderr, "\n");
13+
}
14+
@MECH_REGISTRE@
15+
}
16+

cmake/neuronConfig.cmake.in

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
include(CMakeFindDependencyMacro)
2+
3+
find_dependency(Threads)
4+
5+
include("${CMAKE_CURRENT_LIST_DIR}/neuronTargets.cmake")
6+
7+
get_filename_component(_dir "${CMAKE_CURRENT_LIST_FILE}" PATH)
8+
get_filename_component(_prefix "${_dir}/../../.." ABSOLUTE)
9+
10+
set(NRN_ENABLE_CORENEURON @NRN_ENABLE_CORENEURON@)
11+
12+
set(_NEURON_MAIN "${_prefix}/share/nrn/nrnmain.cpp")
13+
set(_NEURON_MAIN_INCLUDE_DIR "${_prefix}/include/nrncvode" "${_prefix}/include")
14+
set(_NEURON_MECH_REG "${_prefix}/share/nrn/mod_reg_nrn.cpp.in")
15+
16+
set(_CORENEURON_BASE_MOD "${_prefix}/share/modfile")
17+
set(_CORENEURON_MAIN "${_prefix}/share/coreneuron/coreneuron.cpp")
18+
set(_CORENEURON_MECH_REG "${_prefix}/share/nrn/mod_reg_corenrn.cpp.in")
19+
set(_CORENEURON_MECH_ENG "${_prefix}/share/coreneuron/enginemech.cpp")
20+
set(_CORENEURON_RANDOM_INCLUDE "${_prefix}/include/coreneuron/utils/randoms" "${_prefix}/include")
21+
set(_CORENEURON_FLAGS @CORENRN_CXX_FLAGS@)
22+
23+
set(_CORENEURON_MIN_CUDA_TOOLKIT_VERSION @CORENRN_CUDA_VERSION_SHORT@)
24+
25+
include("${CMAKE_CURRENT_LIST_DIR}/neuronMechMaker.cmake")

0 commit comments

Comments
 (0)