diff --git a/CMakeLists.txt b/CMakeLists.txt index bc40de3ba7..508f9acd63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -305,7 +305,9 @@ endif() # Enable MPI # ============================================================================= if(NRN_ENABLE_MPI) - find_package(MPI REQUIRED) + + # find_package(MPI REQUIRED) has a CMAKE_OSX_ARCHITECTURES edge case + nrn_mpi_find_package() set(NRNMPI 1) set(PARANEURON 1) # avoid linking to C++ bindings @@ -442,7 +444,7 @@ else() endif() # ============================================================================= -# Add helpder CMake modules AFTER setting options +# Add helper CMake modules AFTER setting options # ============================================================================= include(NeuronFileLists) include(MPIDynamicHelper) @@ -652,6 +654,11 @@ file(COPY ${NRN_HEADERS_PATHS} ${PROJECT_BINARY_DIR}/src/oc/nrnpthread.h DESTINATION ${PROJECT_BINARY_DIR}/include) install(DIRECTORY ${PROJECT_BINARY_DIR}/include DESTINATION ${CMAKE_INSTALL_PREFIX}) +if (NRN_MACOS_BUILD) + # universal build for neurondemo needs to be after, or at end of, install + nrn_macos_after_install() +endif() + # ============================================================================= # Copy bash executable for windows # ============================================================================= @@ -794,6 +801,9 @@ if(NRN_ENABLE_CORENEURON) message(STATUS " Legacy Units| ${CORENRN_ENABLE_LEGACY_UNITS}") endif() endif() +if (NRN_UNIVERSAL2_BUILD) + message(STATUS "CMAKE_OSX_ARCH| ${CMAKE_OSX_ARCHITECTURES}") +endif() message(STATUS "Tests | ${NRN_ENABLE_TESTS}") if(NRN_ENABLE_COVERAGE) message(STATUS "Coverage | Enabled") diff --git a/bldnrnmacpkgcmake.sh b/bldnrnmacpkgcmake.sh old mode 100755 new mode 100644 index 5a90fa272a..e29c3f7738 --- a/bldnrnmacpkgcmake.sh +++ b/bldnrnmacpkgcmake.sh @@ -1,25 +1,53 @@ #!/usr/bin/env bash set -ex + +default_pythons="python3.8 python3.9 python3.10" # distribution built with -# bash bldnrnmacpkgcmake.sh python3.6 python3.7 python3.8 python3.9 -# without args, default is the 5 pythons above. +# bash bldnrnmacpkgcmake.sh +# without args, default are the pythons above. + +# If all the pythons are universal, then so is NEURON. +# Otherwise $CPU +# All pythons must have the same macos version and that will become +# the MACOSX_DEPLOYMENT_TARGET CPU=`uname -m` +universal="yes" # changes to "no" if any python not universal + args="$*" if test "$args" = "" ; then - if test "$CPU" = "x86_64" ; then - args="python3.6 python3.7 python3.8 python3.9" - else # arm64 - args="python3 python3.9" - fi + args="$default_pythons" fi -#10.7 possible if one builds with pythons that are consistent with that. -if test "$CPU" = "x86_64" ; then - export MACOSX_DEPLOYMENT_TARGET=10.9 + +# sysconfig.get_platform() looks like, e.g. "macosx-12.2-arm64" or +# "macosx-11-universal2". I.e. encodes MACOSX_DEPLOYMENT_TARGET and archs. +# Demand all pythons we are building against have same platform. +mac_platform="" +for i in $args ; do + last_py=$i + mplat=`$i -c 'import sysconfig; print(sysconfig.get_platform())'` + echo "platform for $i is $mplat" + if test "$mac_platform" = "" ; then + mac_platform=$mplat + fi + if test "$mac_platform" != "$mplat" ; then + echo "$i platform \"$mplat\" differs from previous python \"$mac_platform\"." + exit 1 + fi +done + +# extract MACOSX_DEPLOYMENT_TARGET and archs from mac_platform +macosver=`$last_py -c 'import sysconfig; print(sysconfig.get_platform().split("-")[1])'` +archs=`$last_py -c 'import sysconfig; print(sysconfig.get_platform().split("-")[2])'` +if test "$archs" != "universal2" ; then + universal=no fi +export MACOSX_DEPLOYMENT_TARGET=$macosver +echo "MACOSX_DEPLOYMENT_TARGET=$MACOSX_DEPLOYMENT_TARGET" + if test "$NRN_SRC" == "" ; then NRN_SRC=$HOME/neuron/nrn fi @@ -36,7 +64,7 @@ mkdir -p $NRN_BLD rm -r -f $NRN_BLD/* cd $NRN_BLD -PYVS="py" # will be part of package file name, eg. py-27-35-36-37-38 +PYVS="py" # will be part of package file name, eg. py-37-38-39-310 pythons="" # will be arg value of NRN_PYTHON_DYNAMIC for i in $args ; do PYVER=`$i -c 'from sys import version_info as v ; print (str(v.major) + str(v.minor)); quit()'` @@ -44,6 +72,11 @@ for i in $args ; do pythons="${pythons}${i};" done +archs_cmake="" # arg for CMAKE_OSX_ARCHITECTURES, eg. arm64;x86_64 +if test "$universal" = "yes" ; then + archs_cmake='-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64' +fi + # The reason for the "-DCMAKE_PREFIX_PATH=/usr/X11" below # is to definitely link against the xquartz.org installation instead # of the one under /opt/homebrew/ (which I think came @@ -57,11 +90,19 @@ cmake .. -DCMAKE_INSTALL_PREFIX=$NRN_INSTALL \ -DIV_ENABLE_X11_DYNAMIC=ON \ -DNRN_ENABLE_CORENEURON=OFF \ -DNRN_RX3D_OPT_LEVEL=2 \ - -DCMAKE_OSX_ARCHITECTURES="$CPU" \ + $archs_cmake \ -DCMAKE_PREFIX_PATH=/usr/X11 \ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ make -j install + +if test "$universal" = "yes" ; then + _temp="`lipo -archs $NRN_INSTALL/share/nrn/demo/release/$CPU/special`" + if test "$_temp" != "x86_64 arm64" ; then + echo "universal build failed. lipo -archs .../special is \"$_temp\" instead of \"x86_64 arm64\"" + exit 1 + fi +fi $NRN_INSTALL/bin/neurondemo -c 'quit()' chk () { @@ -99,7 +140,8 @@ done # upload package to neuron.yale.edu ALPHADIR='hines@neuron.yale.edu:/home/htdocs/ftp/neuron/versions/alpha' describe="`sh $NRN_SRC/nrnversion.sh describe`" -PACKAGE_FULL_NAME=nrn-${describe}-osx-${CPU}-${PYVS}.pkg +macos=macos${MACOSX_DEPLOYMENT_TARGET} +PACKAGE_FULL_NAME=nrn-${describe}-${mac_platform}-${PYVS}.pkg PACKATE_DOWNLOAD_NAME=$ALPHADIR/$PACKAGE_FULL_NAME PACKAGE_FILE_NAME=$NRN_BLD/src/mac/build/NEURON.pkg echo " diff --git a/cmake/MacroHelper.cmake b/cmake/MacroHelper.cmake index 8017eba812..adf7e3a749 100644 --- a/cmake/MacroHelper.cmake +++ b/cmake/MacroHelper.cmake @@ -257,3 +257,23 @@ macro(nrn_install_dir_symlink source_dir symlink_dir) # create symbolic link install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${source_dir} ${symlink_dir})") endmacro(nrn_install_dir_symlink) + +# ======================================================================== +# There is an edge case to 'find_package(MPI REQUIRED)' in that we can +# still build a universal2 macos package on an arm64 architecture even if +# the mpi library has no slice for x86_64. +# ======================================================================== +macro(nrn_mpi_find_package) + if ("arm64" IN_LIST CMAKE_OSX_ARCHITECTURES + AND "x86_64" IN_LIST CMAKE_OSX_ARCHITECTURES + AND NRN_ENABLE_MPI_DYNAMIC) + set(_temp ${CMAKE_OSX_ARCHITECTURES}) + unset(CMAKE_OSX_ARCHITECTURES CACHE) + find_package(MPI REQUIRED) + set(CMAKE_OSX_ARCHITECTURES ${_temp} CACHE INTERNAL "" FORCE) + set(NRN_UNIVERSAL2_BUILD ON) + else() + find_package(MPI REQUIRED) + endif() +endmacro() + diff --git a/docs/install/install_instructions.md b/docs/install/install_instructions.md index 2825446b90..5207e32bab 100644 --- a/docs/install/install_instructions.md +++ b/docs/install/install_instructions.md @@ -17,24 +17,94 @@ pip3 install neuron Python wheels are provided via [pypi.org](https://pypi.org/project/NEURON/). Note that Python2 wheels are provided for the 8.0.x release series exclusively. -Like Windows, you can also use a binary installer to install NEURON. You can download alpha or recent -releases from below URLs: +Like Windows, you can also use a binary installer to install NEURON. +Recent releases are at -* [Alpha releases](https://neuron.yale.edu/ftp/neuron/versions/alpha/) -* [Recent Releases](https://neuron.yale.edu/ftp/neuron/versions/) +* [Recent Releases](https://github.com/neuronsimulator/nrn/releases) + +You can download legacy versions from: -Mac OS installers have name in the format of `nrn--osx-36-37-38-39.pkg`. Like windows -installer, `py-36-37-38-39` string in the installer name indicates that the given installer is +* [Legacy Versions](https://neuron.yale.edu/ftp/neuron/versions/) + +Earlier Mac OS pkg installers have name in the format of +`nrn--osx-36-37-38-39.pkg`. +Like windows installers, +the, `py-36-37-38-39` string in the installer name indicates that the given installer is compatible with Python versions 3.6, 3.7, 3.8 and 3.9. Note that if you double-click the installer then you might see warning like below. In this case you have to right-click on the installer and then click `Open`. You can then see an option to `Open` installer: +The latest Mac OS pkg installers (as of 2022-01-01) are universal2 installers +(for arm64 and x86_64) and extend the name convention to specify which +architectures they run on and the minimum macosx version using the same +style as the python `sysconfig.get_platform()` +`nrn--macosx---py-.pkg` +e.g. +`nrn-8.0a-726-gb9a811a32-macosx-11-universal2-py-38-39-310.pkg` + ![Installer Warning](../_static/osx_installer_warning_solution.png "Mac OS Warning") -This will install NEURON under directory `/Applications/NEURON-/` directory. For GUI support you +The latest pkg installers will install NEURON under the directory `/Applications/NEURON/` directory. +Uninstallng consists of dragging that folder to the trash. For GUI support you have to install [XQuartz](https://www.xquartz.org/) separately. Once you start Terminal application, NEURON binaries (`nrniv`, `neurondemo` etc.) should be available to start. +* Universal2 installers generally "just work" on either an x86_64 or arm64 +architecture. + + ``` + python + from neuron import h + ``` + and ```nrnivmodl``` will by default create an nmodl mechanism library + specifically for the architecture you run on. + + But it may be the case on Apple M1 that you install a python that can + only run as an x86_64 program under Rosetta2. + E.g. The latest Anaconda Python3.9 (though it seems likely that the next + distribution will be universal2). + In this case, if you wish to launch nrniv, force nrniv to launch as an x86_64 + program. E.g. + ``` + arch -arch x86_64 nrniv -python + from neuron import h + ``` + Furthermore, be sure to run nrnivmodl in such a way that it compiles as an + x86_64 library. e.g. + ``` + arch -arch x86_64 nrnivmodl + ``` + although this constructs an arm64 folder, it will compile and link as x86_64. + ``` + % lipo -archs arm64/libnrnmech.dylib + x86_64 + ``` + Alternatively, one can get a universal2 result with + ``` + nrnivmodl -incflags '-arch x86_64 -arch arm64' -loadflags '-arch x86_64 -arch arm64' + ``` + + If, at runtime there is architecture mismatch between python, MPI, + libnrnmech.so, or libnrniv.dylib, you will see an error message similar + to + ``` + Could not dlopen NRN_PYLIB: /Users/hines/opt/anaconda3/lib/libpython3.9.dylib + libnrniv.dylib running as arm64 + but /Users/hines/opt/anaconda3/lib/libpython3.9.dylib can only run as x86_64 + ``` + + Note: there is an environment variable called `ARCHPREFERENCE`. See + `man arch`. + + It may be the case on an x86_64 that if you have an older x86_64 + version of python3.8 or 3.9 installed that those versions of python + are for macos earlier than 11. In that case, you may get a warning of + the form `warning: ... built for newer macOS version (11.0) than being linked (10.9)` + One work around is to install a more recent python or a python with a + more recent build. For example, the package installers at [python.org](http://python.org) + contain the "macos11" string within the package name for the universal2 + installers for python 3.8, 3.9, and 3.10. + #### Linux Like Mac OS, since 7.8.1 release python wheels are provided and you can use `pip` to install NEURON as: @@ -162,7 +232,7 @@ export PATH=/usr/local/bin/:$PATH ``` If the desired python version is not installed, you can install it using -[official distribution](https://www.python.org/downloads/mac-osx/). Also, note that +[official distribution](https://www.python.org/downloads/macos/). Also, note that [Xcode Command Line Tools](https://stackoverflow.com/questions/9329243/how-to-install-xcode-command-line-tools) needs to be installed for development. diff --git a/docs/install/mac_pkg.md b/docs/install/mac_pkg.md index 62b89735ee..38d77d1609 100644 --- a/docs/install/mac_pkg.md +++ b/docs/install/mac_pkg.md @@ -1,22 +1,34 @@ Mac Binary Package (Apple M1 and Mac x86_64) ------------------ -Binary packages are built with the ```nrnmacpkgcmake.sh``` script which, -near the end of its operation, +Macos binary packages are built with the +[bldnrnmacpkgcmake.sh](https://github.com/neuronsimulator/nrn/blob/master/bldnrnmacpkgcmake.sh) +script which, near the end of its operation, involves code signing, package signing, and notarization. +The build will be universal2 and work on arm64 and x86_64 architectures +(if the pythons used are themselves, universal2). Preparing your Mac development environment for correct functioning of the script requires installing a few extra [Dependencies](#Dependencies) beyond the -[normal user source build](./install_instructions.html#Mac-OS-Depend), obtaining an Apple Developer Program membership, +[normal user source build](./install_instructions.html#Mac-OS-Depend), +obtaining an Apple Developer Program membership, and requesting two signing certificates from Apple. Those actions are described in separate sections below. -On an Apple M1, +On an Apple M1 or x86_64, the script, by default, creates, e.g., -```nrn-8.0a-427-g1a80b2cc-osx-arm64-py-38-39.pkg``` -where the information between nrn and osx comes from ```git describe``` +```nrn-8.0a-726-gb9a811a32-macosx-11-universal2-py-38-39-310.pkg``` +where the information between nrn and macosx comes from ```git describe```, +the number after macos refers to the ```MACOSX_DEPLOYMENT_TARGET=11``` +the next item(s) before py indicate the architectures on which +the program can run (i.e. ```arm64```, ```x86_64```, or ```universal2``` +for both) and the numbers after the py indicate the python versions that are compatible with this package. Those python versions must be installed on -the developer machine. On a Mac x86_64 architecture the script, by default, -creates, e.g., ```nrn-8.0a-427-g1a80b2cc-osx-x86_64-py-27-36-37-38-39.pkg``` +the developer machine. +The script will build a universal pkg only +if all the Python's are themselves universal. +Presently, one must manually make sure that all the python builds used +the same MACOSX_DEPLOYMENT_TARGET. You can check both of these with +```python3 -c 'import sysconfig; print(sysconfig.get_platform())'``` A space separated list of python executable arguments can be used in place of the internal default lists. ```$NRN_SRC``` is the location of the @@ -28,22 +40,27 @@ At time of writing, the cmake command in the script that is used to configure the build is ``` cmake .. -DCMAKE_INSTALL_PREFIX=$NRN_INSTALL \ - -DNRN_ENABLE_MPI_DYNAMIC=ON \ + -DNRN_ENABLE_MPI_DYNAMIC=ON \ -DPYTHON_EXECUTABLE=`which python3` -DNRN_ENABLE_PYTHON_DYNAMIC=ON \ -DNRN_PYTHON_DYNAMIC="$pythons" \ -DIV_ENABLE_X11_DYNAMIC=ON \ -DNRN_ENABLE_CORENEURON=OFF \ -DNRN_RX3D_OPT_LEVEL=2 \ - -DCMAKE_OSX_ARCHITECTURES="$CPU" \ + $archs_cmake \ + -DCMAKE_PREFIX_PATH=/usr/X11 \ -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ ``` +The default variables above will be +``` +pythons="python3.8 python3.9 python3.10" +archs_cmake='-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64' +``` A universal build is possible with ```-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"``` -but the installations for openmpi and python on your machine must also be universal. -I.e. configure with ```-DNRN_ENABLE_MPI_DYNAMIC=OFF -DNRN_PYTHON_DYNAMIC=/usr/bin/python3```. -As we have been unable to find or build a universal openmpi, and -only the Big Sur default Python 3.8 installaion is universal, we are -currently building separate installers for arm64 and x86_64. +but the installations of python on your machine must also be universal. +The MPI library does not have to be universal if configuring with +```-DNRN_ENABLE_MPI_DYNAMIC=ON``` as the library is not linked against +during the build. ```make -j install``` is used to build and install in ```/Applications/NEURON``` @@ -73,6 +90,29 @@ currently building separate installers for arm64 and x86_64. - request Apple to notarize NEURON.pkg ```src/macnrn_notarize.sh``` + If notarizaton fails, it is occasionally due to Apple + changing the contracts and demanding that + "You must first sign the relevant contracts online. (1048)". In that + case, go to [appstoreconnect.apple.com](https://appstoreconnect.apple.com) + to accept the legal docs. For other notarization failures, one must consult + the LogFileURL which can be obtained with + ``` + % xcrun altool --notarization-info $RequestIdentifier \ + --username "michael.hines@yale.edu" \ + --password "`cat ~/.ssh/notarization-password`" + No errors getting notarization info. + + Date: 2022-01-02 23:38:12 +0000 + Hash: 7254157952d4f3573c2804879cf6da8d... + LogFileURL: https://osxapps-ssl.itunes.apple.com/itunes-assets... + RequestUUID: 152f0f0e-af58-4d22-b291-6a441825dd20 + Status: invalid + Status Code: 2 + Status Message: Package Invalid + ``` + where RequestIdentifer (the RequestUUID) appears in the email sent + back in response to the notarization request. + The script ends by printing: ``` Until we figure out how to automatically staple the notarization @@ -100,45 +140,51 @@ x86_64 arm64 ``` which is generally the case for all Big Sur mac software. On the other hand, ```brew install ...``` executables and libraries are -generally only for the architecture indicated by -``` -uname -m -``` +generally only for the architecture indicated by ```uname -m```. That is +ok for openmpi but since the various python libraries are linked against +during build to create the version specific neuron modules, those python +installers also have to be universal. Fortunately, universal python versions +can be found at [python.org](http://python.org/Downloads/macOS) at least for +(as of 2022-01-01) python3.8, python3.9, and python3.10. - ```xcode-select --install```: - Sadly, notarization requires ```altool``` which is not distributed with the command line tools. So one needs to install the full xcode (at least version 12) from the App Store. That is very large and may - take an hour or so to download. + take an hour or so to download. (should be checked to see if this is + still the case.) - Install latest [XQuartz](http://xquartz.org) release. At least XQuartz-2.8.0_rc1 - Install [PackagesBuild](http://s.sudre.free.fr/Software/Packages/about.html) -- ```brew install cmake open-mpi python3.9``` +- ```brew install coreutils cmake bison flex open-mpi``` - - Python 3.8 is already installed as /usr/bin/python3 + - Python 3.8 is already installed as /usr/bin/python3 and is universal2. - The [normal source build](./install_instructions.html#Mac-OS-Depend) explains how to install brew and add it to the PATH. - ``` - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/H$ + ```bash + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" echo 'eval $(/opt/homebrew/bin/brew shellenv)' >> $HOME/.zprofile eval $(/opt/homebrew/bin/brew shellenv) ``` - - Both pythons need numpy installed + - Default build uses universal pythons from python.org. I also redundantly + include their python3.8 as I find it more convenient to be able to + explicitly specify the full name as python3.8. + + - All pythons used for building need numpy installed, e.g. ``` pip3.9 install --user --upgrade pip pip3.9 install --user numpy - python3 -m pip install --user --upgrade pip - python3 -m pip install --user numpy ``` - - At least the first python3 in your PATH needs cython installed + - At least one python3 in your PATH needs cython installed. + And, to avoid compile errors, the minimum version is 0.29.26 ``` - python3 -m pip install --user cython + python3.10 -m pip install --user cython ``` #### Signing and Notarization diff --git a/mingw_files/nrnmingwenv.sh b/mingw_files/nrnmingwenv.sh index 2d0493ee69..9e7fe60437 100644 --- a/mingw_files/nrnmingwenv.sh +++ b/mingw_files/nrnmingwenv.sh @@ -149,7 +149,11 @@ unistd.h vadefs.h ' -copy mingw64/x86_64-w64-mingw32/lib ' +mlib=mingw64/x86_64-w64-mingw32/lib # gcc 11.2.0 Rev 1 +if test -f /mingw64/lib/dllcrt2.o ; then # gcc 11.2.0 Rev 9 + mlib=mingw64/lib +fi +copy $mlib ' crtbegin.o crtend.o dllcrt2.o diff --git a/src/mac/CMakeLists.txt b/src/mac/CMakeLists.txt index 048f0059fd..44be774b01 100644 --- a/src/mac/CMakeLists.txt +++ b/src/mac/CMakeLists.txt @@ -4,8 +4,13 @@ if (NRN_MACOS_BUILD) # Sets up icons, apps, and activates them - install(CODE - "execute_process(COMMAND sh ${CMAKE_CURRENT_SOURCE_DIR}/activate-apps-cmake.sh \"x86_64\" \"${CMAKE_INSTALL_PREFIX}\" \"${PROJECT_SOURCE_DIR}\" \"${CMAKE_INSTALL_PREFIX}/lib\")") + # If universal build, definitely needs to run after (or at end of) + # install, so we create a macro and run at end of + # top level CMakeLists.txt + macro(nrn_macos_after_install) + install(CODE + "execute_process(COMMAND sh ${PROJECT_SOURCE_DIR}/src/mac/activate-apps-cmake.sh \"x86_64\" \"${CMAKE_INSTALL_PREFIX}\" \"${PROJECT_SOURCE_DIR}\" \"${CMAKE_INSTALL_PREFIX}/lib\" \"${NRN_UNIVERSAL2_BUILD}\")") + endmacro() set(MACPKG_INSTALL_PATH "/Applications/NEURON") if (${CMAKE_INSTALL_PREFIX} STREQUAL ${MACPKG_INSTALL_PATH}) diff --git a/src/mac/activate-apps-cmake.sh b/src/mac/activate-apps-cmake.sh index 0046776e22..84125501cb 100755 --- a/src/mac/activate-apps-cmake.sh +++ b/src/mac/activate-apps-cmake.sh @@ -26,6 +26,7 @@ CPU=`uname -m` NRN_INSTALL=$2 NRN_SRC=$3 ivlibdir=$4 +NRN_UNIVERSAL_BUILD=$5 export CPU export NRN_SRC NSRC=$NRN_SRC @@ -63,9 +64,17 @@ osascript -e 'tell application "Finder"'\ -e 'set target of Finder window 1 to folder "bin" of folder '"$lst"' of startup disk'\ -e 'end tell' -# force rebuild of the neurondemo +# force rebuild of the neurondemo (perhaps as universal2) DEMO="${NRN_INSTALL}/share/nrn/demo" rm -f -r ${DEMO}/neuron ${DEMO}/release/${CPU} +if test "${NRN_UNIVERSAL_BUILD}" = "ON" ; then + ( + cd ${DEMO}/release + archs='-arch arm64 -arch x86_64' + $NRN_INSTALL/bin/nrnivmodl -incflags "$archs" -loadflags "$archs" + rm -f ${DEMO}/neuron ; touch ${DEMO}/neuron + ) +fi $NRN_INSTALL/bin/neurondemo << here quit() here diff --git a/src/nrniv/nrnpy.cpp b/src/nrniv/nrnpy.cpp index a132c5c1dd..05fd4f53b6 100644 --- a/src/nrniv/nrnpy.cpp +++ b/src/nrniv/nrnpy.cpp @@ -27,6 +27,9 @@ static void (*p_nrnpython_real)(); static void (*p_nrnpython_reg_real)(); extern "C" char* hoc_back2forward(char* s); char* hoc_forward2back(char* s); +#if DARWIN +extern void nrn_possible_mismatched_arch(const char*); +#endif // following is undefined or else has the value of sys.api_version // at time of configure (using the python first in the PATH). @@ -233,6 +236,9 @@ void nrnpython_reg() { handle = dlopen(nrnpy_pylib, RTLD_NOW|RTLD_GLOBAL); if (!handle) { fprintf(stderr, "Could not dlopen NRN_PYLIB: %s\n", nrnpy_pylib); +#if DARWIN + nrn_possible_mismatched_arch(nrnpy_pylib); +#endif exit(1); } } diff --git a/src/nrnmpi/nrnmpi_dynam.cpp b/src/nrnmpi/nrnmpi_dynam.cpp index 533ba6f757..a8b0ba2a6d 100644 --- a/src/nrnmpi/nrnmpi_dynam.cpp +++ b/src/nrnmpi/nrnmpi_dynam.cpp @@ -28,6 +28,10 @@ extern char* dlerror(); extern char* cxx_char_alloc(size_t); extern std::string corenrn_mpi_library; +#if DARWIN +extern void nrn_possible_mismatched_arch(const char*); +#endif + #if DARWIN || defined(__linux__) extern const char* path_prefix_to_libnrniv(); #endif @@ -44,6 +48,9 @@ static void* load_mpi(const char* name, char* mes) { int flag = RTLD_NOW | RTLD_GLOBAL; void* handle = dlopen(name, flag); if (!handle) { +#if DARWIN + nrn_possible_mismatched_arch(name); +#endif sprintf(mes, "load_mpi: %s\n", dlerror()); }else{ sprintf(mes, "load_mpi: %s successful\n", name); @@ -99,7 +106,7 @@ char* nrnmpi_load(int is_python) { if (mpi_lib_path) { handle = load_mpi(mpi_lib_path, pmes+strlen(pmes)); if (!handle) { - sprintf(pmes, "Can not load libmpi.dylib and %s", mpi_lib_path); + sprintf(pmes, "Can not load libmpi.dylib and %s\n", mpi_lib_path); } } } diff --git a/src/nrnoc/init.cpp b/src/nrnoc/init.cpp index a078eeb314..7d74fe5cf5 100644 --- a/src/nrnoc/init.cpp +++ b/src/nrnoc/init.cpp @@ -57,6 +57,35 @@ extern int nrn_noauto_dlopen_nrnmech; /* default 0 declared in hoc_init.cpp */ #define DLL_DEFAULT_FNAME "libnrnmech.dylib" #endif +// error message hint with regard to mismatched arch +void nrn_possible_mismatched_arch(const char* libname) { + if (strncmp(NRNHOSTCPU, "arm64", 5) == 0) { + // what arch are we running on +#if __arm64__ + const char* we_are{"arm64"}; +#elif __x86_64__ + const char* we_are{"x86_64"}; +#endif + + // what arch did we try to dlopen + char* cmd; + cmd = new char[strlen(libname) + 100]; + sprintf(cmd, "lipo -archs %s 2> /dev/null", libname); + char libname_arch[20]{0}; + FILE* p = popen(cmd, "r"); + delete [] cmd; + if (!p) { return; } + fgets(libname_arch, 18, p); + if (strlen(libname_arch) == 0) {return;} + pclose(p); + + if (strstr(libname_arch, we_are) == NULL) { + fprintf(stderr, "libnrniv.dylib running as %s\n", we_are); + fprintf(stderr, "but %s can only run as %s\n", libname, libname_arch); + } + } +} + #if __GNUC__ < 4 #include "osxdlfcn.h" #include "osxdlfcn.cpp" @@ -230,12 +259,20 @@ void* nrn_realpath_dlopen(const char* relpath, int flags) { #endif /* not HAVE_REALPATH */ if (abspath) { handle = dlopen(abspath, flags); +#if DARWIN + if (!handle) { + nrn_possible_mismatched_arch(abspath); + } +#endif free(abspath); }else{ int patherr = errno; handle = dlopen(relpath, flags); if (!handle) { Fprintf(stderr, "realpath failed errno=%d (%s) and dlopen failed with %s\n", patherr, strerror(patherr), relpath); +#if DARWIN + nrn_possible_mismatched_arch(abspath); +#endif } } return handle;