Skip to content

Commit

Permalink
Merge pull request #2304 from ComputeCanada/neuron_cmake
Browse files Browse the repository at this point in the history
update NEURON easyblock to use CMakeMake for recent versions
  • Loading branch information
boegel authored Jun 3, 2022
2 parents b0fd184 + c950c13 commit d4179f0
Showing 1 changed file with 123 additions and 57 deletions.
180 changes: 123 additions & 57 deletions easybuild/easyblocks/n/neuron.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@
EasyBuild support for building and installing NEURON, implemented as an easyblock
@author: Kenneth Hoste (Ghent University)
@author: Maxime Boissonneault (Universite Laval, Compute Canada)
"""
import os
import re

from easybuild.easyblocks.generic.configuremake import ConfigureMake
from easybuild.easyblocks.generic.cmakemake import CMakeMake
from easybuild.easyblocks.generic.pythonpackage import det_pylibdir
from easybuild.framework.easyconfig import CUSTOM
from easybuild.tools.build_log import EasyBuildError
Expand All @@ -42,7 +44,7 @@
from distutils.version import LooseVersion


class EB_NEURON(ConfigureMake):
class EB_NEURON(CMakeMake):
"""Support for building/installing NEURON."""

def __init__(self, *args, **kwargs):
Expand All @@ -60,85 +62,143 @@ def extra_options():
extra_vars = {
'paranrn': [True, "Enable support for distributed simulations.", CUSTOM],
}
return ConfigureMake.extra_options(extra_vars)
return CMakeMake.extra_options(extra_vars)

def configure_step(self):
"""Custom configuration procedure for NEURON."""
if LooseVersion(self.version) < LooseVersion('7.8.1'):

# enable support for distributed simulations if desired
if self.cfg['paranrn']:
self.cfg.update('configopts', '--with-paranrn')
# make sure we're using the correct configure command
# (required because custom easyconfig parameters from CMakeMake are picked up)
self.cfg['configure_cmd'] = "./configure"

# specify path to InterViews if it is available as a dependency
interviews_root = get_software_root('InterViews')
if interviews_root:
self.cfg.update('configopts', "--with-iv=%s" % interviews_root)
else:
self.cfg.update('configopts', "--without-iv")
# enable support for distributed simulations if desired
if self.cfg['paranrn']:
self.cfg.update('configopts', '--with-paranrn')

# optionally enable support for Python as alternative interpreter
python_root = get_software_root('Python')
if python_root:
self.with_python = True
self.cfg.update('configopts', "--with-nrnpython=%s/bin/python" % python_root)
# specify path to InterViews if it is available as a dependency
interviews_root = get_software_root('InterViews')
if interviews_root:
self.cfg.update('configopts', "--with-iv=%s" % interviews_root)
else:
self.cfg.update('configopts', "--without-iv")

# determine host CPU type
cmd = "./config.guess"
(out, ec) = run_cmd(cmd, simple=False)
# optionally enable support for Python as alternative interpreter
python_root = get_software_root('Python')
if python_root:
self.with_python = True
self.cfg.update('configopts', "--with-nrnpython=%s/bin/python" % python_root)

self.hostcpu = out.split('\n')[0].split('-')[0]
self.log.debug("Determined host CPU type as %s" % self.hostcpu)
# determine host CPU type
cmd = "./config.guess"
(out, ec) = run_cmd(cmd, simple=False)

# determine Python lib dir
self.pylibdir = det_pylibdir()
self.hostcpu = out.split('\n')[0].split('-')[0]
self.log.debug("Determined host CPU type as %s" % self.hostcpu)

# complete configuration with configure_method of parent
super(EB_NEURON, self).configure_step()
# determine Python lib dir
self.pylibdir = det_pylibdir()

# complete configuration with configure_method of parent
ConfigureMake.configure_step(self)
else:
# enable support for distributed simulations if desired
if self.cfg['paranrn']:
self.cfg.update('configopts', '-DNRN_ENABLE_MPI=ON')
else:
self.cfg.update('configopts', '-DNRN_ENABLE_MPI=OFF')

# specify path to InterViews if it is available as a dependency
interviews_root = get_software_root('InterViews')
if interviews_root:
self.cfg.update('configopts', "-DIV_DIR=%s -DNRN_ENABLE_INTERVIEWS=ON" % interviews_root)
else:
self.cfg.update('configopts', "-DNRN_ENABLE_INTERVIEWS=OFF")

# no longer used it seems
self.hostcpu = ''

# optionally enable support for Python as alternative interpreter
python_root = get_software_root('Python')
if python_root:
self.with_python = True
self.cfg.update('configopts', "-DNRN_ENABLE_PYTHON=ON -DPYTHON_EXECUTABLE=%s/bin/python" % python_root)
self.cfg.update('configopts', "-DNRN_ENABLE_MODULE_INSTALL=ON "
"-DNRN_MODULE_INSTALL_OPTIONS='--prefix=%s'" % self.installdir)
else:
self.cfg.update('configopts', "-DNRN_ENABLE_PYTHON=OFF")

# determine Python lib dir
self.pylibdir = det_pylibdir()

# complete configuration with configure_method of parent
CMakeMake.configure_step(self)

def install_step(self):
"""Custom install procedure for NEURON."""

super(EB_NEURON, self).install_step()

if self.with_python:
pypath = os.path.join('src', 'nrnpython')
try:
pwd = os.getcwd()
os.chdir(pypath)
except OSError as err:
raise EasyBuildError("Failed to change to %s: %s", pypath, err)
# with the CMakeMake, the python module is installed automatically
if LooseVersion(self.version) < LooseVersion('7.8.1'):
if self.with_python:
pypath = os.path.join('src', 'nrnpython')
try:
pwd = os.getcwd()
os.chdir(pypath)
except OSError as err:
raise EasyBuildError("Failed to change to %s: %s", pypath, err)

cmd = "python setup.py install --prefix=%s" % self.installdir
run_cmd(cmd, simple=True, log_all=True, log_ok=True)
cmd = "python setup.py install --prefix=%s" % self.installdir
run_cmd(cmd, simple=True, log_all=True, log_ok=True)

try:
os.chdir(pwd)
except OSError as err:
raise EasyBuildError("Failed to change back to %s: %s", pwd, err)
try:
os.chdir(pwd)
except OSError as err:
raise EasyBuildError("Failed to change back to %s: %s", pwd, err)

def sanity_check_step(self):
"""Custom sanity check for NEURON."""
shlib_ext = get_shared_lib_ext()
binpath = os.path.join(self.hostcpu, 'bin')
libpath = os.path.join(self.hostcpu, 'lib', 'lib%s.' + shlib_ext)
binaries = ["bbswork.sh", "hel2mos1.sh", "ivoc", "memacs", "mkthreadsafe", "modlunit", "mos2nrn",
"mos2nrn2.sh", "neurondemo", "nocmodl", "oc"]
binaries += ["nrn%s" % x for x in ["gui", "iv", "iv_makefile", "ivmodl", "mech_makefile", "oc", "oc_makefile",
"ocmodl"]]
libs = ["ivoc", "ivos", "memacs", "meschach", "neuron_gnu", "nrniv", "nrnmpi", "nrnoc", "nrnpython", "oc",
"ocxt", "scopmath", "sparse13", "sundials"]

# hoc_ed is not included in the sources of 7.4. However, it is included in the binary distribution.
# Nevertheless, the binary has a date old enough (June 2014, instead of November 2015 like all the
# others) to be considered a mistake in the distribution
if LooseVersion(self.version) < LooseVersion('7.4'):
binaries.append("hoc_ed")
binaries = ["neurondemo", "nrngui", "nrniv", "nrnivmodl", "nocmodl", "modlunit", "nrnmech_makefile",
"mkthreadsafe"]
libs = ["nrniv"]
sanity_check_dirs = ['share/nrn']

custom_paths = {
'files': [os.path.join(binpath, x) for x in binaries] + [libpath % x for x in libs],
'dirs': ['include/nrn', 'share/nrn'],
if LooseVersion(self.version) < LooseVersion('7.4'):
binaries += ["hoc_ed"]

if LooseVersion(self.version) < LooseVersion('7.8.1'):
binaries += ["bbswork.sh", "hel2mos1.sh", "ivoc", "memacs", "mos2nrn", "mos2nrn2.sh", "oc"]
binaries += ["nrn%s" % x for x in ["iv_makefile", "oc", "oc_makefile", "ocmodl"]]
libs += ["ivoc", "ivos", "memacs", "meschach", "neuron_gnu", "nrnmpi", "nrnoc", "nrnpython",
"oc", "ocxt", "scopmath", "sparse13", "sundials"]
sanity_check_dirs += ['include/nrn']
# list of included binaries changed with cmake. See
# https://github.com/neuronsimulator/nrn/issues/899
else:
binaries += ["nrnpyenv.sh", "set_nrnpyenv.sh", "sortspike"]
libs += ["rxdmath"]
sanity_check_dirs += ['include']
if self.with_python:
sanity_check_dirs += [os.path.join("lib", "python"),
os.path.join("lib", "python%(pyshortver)s", "site-packages")]

# this is relevant for installations of Python packages for multiple Python versions (via multi_deps)
# (we can not pass this via custom_paths, since then the %(pyshortver)s template value will not be resolved)
# ensure that we only add to paths specified in the EasyConfig
sanity_check_files = [os.path.join(binpath, x) for x in binaries] + [libpath % x for x in libs]
self.cfg['sanity_check_paths'] = {
'files': sanity_check_files,
'dirs': sanity_check_dirs,
}
super(EB_NEURON, self).sanity_check_step(custom_paths=custom_paths)

super(EB_NEURON, self).sanity_check_step()

try:
fake_mod_data = self.load_fake_module()
Expand Down Expand Up @@ -190,6 +250,10 @@ def sanity_check_step(self):
else:
self.log.info("Skipping MPI testing of NEURON since MPI testing is disabled")

if self.with_python:
cmd = "python -c 'import neuron; neuron.test()'"
(out, ec) = run_cmd(cmd, simple=False, log_all=True, log_output=True)

# cleanup
self.clean_up_fake_module(fake_mod_data)

Expand All @@ -202,11 +266,6 @@ def make_module_req_guess(self):
'PATH': [os.path.join(self.hostcpu, 'bin')],
})

if self.with_python:
guesses.update({
'PYTHONPATH': [self.pylibdir],
})

return guesses

def make_module_extra(self):
Expand All @@ -216,12 +275,19 @@ def make_module_extra(self):

# we need to make sure the correct compiler is set in the environment,
# since NEURON features compilation at runtime
for var in ['CC', 'MPICH_CC']:
for var in ['CC', 'CXX', 'MPICC', 'MPICXX', 'MPICH_CC', 'MPICH_CXX']:
val = os.getenv(var)
if val:
txt += self.module_generator.set_environment(var, val)
self.log.debug("%s set to %s, adding it to module" % (var, val))
else:
self.log.debug("%s not set: %s" % (var, os.environ.get(var, None)))

if self.with_python:
if self.cfg['multi_deps'] and 'Python' in self.cfg['multi_deps']:
txt += self.module_generator.prepend_paths('EBPYTHONPREFIXES', '')
else:
txt += self.module_generator.prepend_paths('PYTHONPATH', [self.pylibdir])
# also adds lib/python to PYTHONPATH
txt += self.module_generator.prepend_paths('PYTHONPATH', ['lib/python'])
return txt

0 comments on commit d4179f0

Please sign in to comment.