From 80218041ccfa1de3f85a3a4944f95aa72720e8d0 Mon Sep 17 00:00:00 2001 From: Sebastian Schmitt <sebastian.schmitt@kip.uni-heidelberg.de> Date: Wed, 6 Jan 2021 14:29:15 +0100 Subject: [PATCH] Option to toggle between external and bundled versions of C++ dependencies (#1198) * Add the ability to use json and pybind11 libraries installed on the system instead of the vesrsions of those libraries bundled as submodules in the Arbor repository * Turned on by default * Toggled using the new `ARB_USE_BUNDLED_LIBS` CMake flag * Update documentation for installation * Update CI and pip workflows to opt in to the bundled libraries --- .github/workflows/basic.yml | 2 +- CMakeLists.txt | 30 ++++++++++++++++++++++------ doc/CMakeLists.txt | 4 ++-- doc/install/build_install.rst | 30 ++++++++++++++++++++++------ example/bench/CMakeLists.txt | 2 +- example/dryrun/CMakeLists.txt | 2 +- example/gap_junctions/CMakeLists.txt | 2 +- example/generators/CMakeLists.txt | 2 +- example/ring/CMakeLists.txt | 2 +- ext/CMakeLists.txt | 7 +++++-- python/CMakeLists.txt | 27 +++++++++++++++---------- scripts/travis/build.sh | 2 +- setup.py | 10 ++++++++-- sup/CMakeLists.txt | 2 +- 14 files changed, 87 insertions(+), 37 deletions(-) diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index cc862780..884634f2 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -74,7 +74,7 @@ jobs: run: | mkdir build cd build - cmake .. -DCMAKE_CXX_COMPILER=$CXX -DCMAKE_C_COMPILER=$CC -DARB_WITH_PYTHON=ON -DPython3_EXECUTABLE=`which python` -DARB_WITH_MPI=${{ matrix.mpi }} + cmake .. -DCMAKE_CXX_COMPILER=$CXX -DCMAKE_C_COMPILER=$CC -DARB_WITH_PYTHON=ON -DPython3_EXECUTABLE=`which python` -DARB_WITH_MPI=${{ matrix.mpi }} -DARB_USE_BUNDLED_LIBS=ON make -j4 tests examples pyarb html cd - - name: Run unit tests diff --git a/CMakeLists.txt b/CMakeLists.txt index e8c755ad..8155f906 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.12) +include(CMakeDependentOption) file(READ VERSION FULL_VERSION_STRING) string(STRIP "${FULL_VERSION_STRING}" FULL_VERSION_STRING) @@ -37,6 +38,10 @@ option(ARB_UNWIND "Use libunwind for stack trace printing if available" OFF) set(ARB_GPU "none" CACHE STRING "GPU backend and compiler configuration") set_property(CACHE PROPERTY STRINGS "none" "cuda" "cuda-clang" "hip") +# Use bundled 3rd party libraries + +option(ARB_USE_BUNDLED_LIBS "Use bundled 3rd party libraries" OFF) + #---------------------------------------------------------- # Configure-time features for Arbor: #---------------------------------------------------------- @@ -198,6 +203,13 @@ install(TARGETS arbornml-public-deps EXPORT arbornml-targets) # External libraries in `ext` sub-directory: json, tinyopt and randon123. # Creates interface libraries `ext-json`, `ext-tinyopt` and `ext-random123` +cmake_dependent_option(ARB_USE_BUNDLED_JSON "Use bundled Niels Lohmann's json library." ON "ARB_USE_BUNDLED_LIBS" OFF) +if(NOT ARB_USE_BUNDLED_JSON) + find_package(nlohmann_json) + set(json_library_name nlohmann_json::nlohmann_json) +else() + unset(nlohmann_json_DIR CACHE) +endif() add_subdirectory(ext) # Keep track of packages we need to add to the generated CMake config @@ -234,10 +246,15 @@ endif() # The minimum version of Python supported by Arbor. set(arb_py_version 3.6.0) + +if(DEFINED PYTHON_EXECUTABLE) + set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE}) +endif() + if(ARB_WITH_PYTHON) + cmake_dependent_option(ARB_USE_BUNDLED_PYBIND11 "Use bundled pybind11" ON "ARB_WITH_PYTHON;ARB_USE_BUNDLED_LIBS" OFF) + find_package(Python3 ${arb_py_version} COMPONENTS Interpreter Development REQUIRED) - set(PYTHON_EXECUTABLE "${Python3_EXECUTABLE}") - message(STATUS "PYTHON_EXECUTABLE: ${PYTHON_EXECUTABLE}") # Required to link the dynamic libraries for python modules. # Effectively adds '-fpic' flag to CXX_FLAGS. @@ -246,10 +263,11 @@ else() # If not building the Python module, the interpreter is still required # to build some targets, e.g. when building the documentation. find_package(Python3 ${arb_py_version} COMPONENTS Interpreter) - if(Python3_FOUND) - set(PYTHON_EXECUTABLE "${Python3_EXECUTABLE}") - message(STATUS "PYTHON_EXECUTABLE: ${PYTHON_EXECUTABLE}") - endif() +endif() + +if(${Python3_FOUND}) + set(PYTHON_EXECUTABLE "${Python3_EXECUTABLE}") + message(STATUS "PYTHON_EXECUTABLE: ${PYTHON_EXECUTABLE}") endif() diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 5889d51a..755550f5 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -20,8 +20,8 @@ string(REPLACE ";" "," theme_path "${theme_path}") add_custom_target(html COMMAND - PYTHONPATH=${CMAKE_BINARY_DIR}/python - ${PYTHON_EXECUTABLE} + PYTHONPATH="${CMAKE_BINARY_DIR}/python:$ENV{PYTHONPATH}" + "${PYTHON_EXECUTABLE}" -m sphinx -b html -d ${doctree_dir} diff --git a/doc/install/build_install.rst b/doc/install/build_install.rst index 2319d0cb..b2802297 100644 --- a/doc/install/build_install.rst +++ b/doc/install/build_install.rst @@ -141,6 +141,24 @@ install `Sphinx <http://www.sphinx-doc.org/en/master/>`_. .. _install-downloading: + +External dependencies +~~~~~~~~~~~~~~~~~~~~~ + +For the (optional) python bindings Arbor uses `pybind11 <https://github.com/pybind/pybind11>`_, and +JSON parsing is faciliated through `nlohmann json <https://github.com/nlohmann/json>`_. + +There are two ways to obtain these libraries. The default way is to use them from the +system, e.g., installed via ``apt install python3-pybind11`` and ``apt install nlohmann-json3-dev`` +for a Debian based distribution. + +The other possiblity is to use versions of these dependencies that are bundled with Arbor +via the CMAKE option `ARB_USE_BUNDLED_LIBS`. +If set, `pybind11 <https://github.com/pybind/pybind11>`_ is retrieved from a Git submodule (see below) +and `nlohmann json <https://github.com/nlohmann/json>`_ from a copy in the checked out sources. + +It is also possible to select only one of the two libraries to be taken from the system or from Arbor. + Getting the code ================ @@ -194,7 +212,7 @@ For more detailed build configuration options, see the `quick start <quickstart_ # 2) Use CMake to configure the build. # By default Arbor builds in release mode, i.e. with optimizations on. # Release mode should be used for installing and benchmarking Arbor. - cmake .. + cmake .. # add -DARB_USE_BUNDLED_LIBS=ON to use bundled/git-submoduled libs # 3.1) Build Arbor library. make -j 4 @@ -414,16 +432,16 @@ CMake ``ARB_WITH_PYTHON`` option: By default ``ARB_WITH_PYTHON=OFF``. When this option is turned on, a Python module called :py:mod:`arbor` is built. A specific version of Python can be set when configuring with CMake using the -``Python3_EXECUTABLE`` variable. For example, to use Python 3.8 installed on a Linux +``PYTHON_EXECUTABLE`` variable. For example, to use Python 3.8 installed on a Linux system with the executable in ``/usr/bin/python3.8``: .. code-block:: bash - cmake .. -DARB_WITH_PYTHON=ON -DPython3_EXECUTABLE=/usr/bin/python3.8 + cmake .. -DARB_WITH_PYTHON=ON -DPYTHON_EXECUTABLE=/usr/bin/python3.8 By default the Python module will be installed in the directory returned by -``${Python3_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_path('platlib'))"``. -This returns the directory where the supplied or found ``Python3_EXECUTABLE`` looks for system packages. +``${PYTHON_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_path('platlib'))"``. +This returns the directory where the supplied or found ``PYTHON_EXECUTABLE`` looks for system packages. `See Python's sysconfig documentation <https://docs.python.org/3/library/sysconfig.html#installation-paths>`_. If CMake is run in a `venv` or Conda environment, this should pick up on the appropriate package directory. To install the module in a different location, set ``ARB_PYTHON_LIB_PATH`` to a custom path. @@ -434,7 +452,7 @@ user site package might look like the following: cmake .. -DARB_WITH_PYTHON=ON \ -DARB_PYTHON_LIB_PATH=${HOME}/.local/lib/python3.8/site-packages/ \ - -DPython3_EXECUTABLE=/usr/bin/python3.8 + -DPYTHON_EXECUTABLE=/usr/bin/python3.8 On the target LINUX system, the Arbor package was installed in ``/home/$USER/.local/lib/python3.8/site-packages``. diff --git a/example/bench/CMakeLists.txt b/example/bench/CMakeLists.txt index 49fb7016..b9a7e120 100644 --- a/example/bench/CMakeLists.txt +++ b/example/bench/CMakeLists.txt @@ -1,4 +1,4 @@ add_executable(bench EXCLUDE_FROM_ALL bench.cpp) add_dependencies(examples bench) -target_link_libraries(bench PRIVATE arbor arborenv arbor-sup ext-json) +target_link_libraries(bench PRIVATE arbor arborenv arbor-sup ${json_library_name}) diff --git a/example/dryrun/CMakeLists.txt b/example/dryrun/CMakeLists.txt index 9d9cb559..4cd7a797 100644 --- a/example/dryrun/CMakeLists.txt +++ b/example/dryrun/CMakeLists.txt @@ -1,4 +1,4 @@ add_executable(dryrun EXCLUDE_FROM_ALL dryrun.cpp) add_dependencies(examples dryrun) -target_link_libraries(dryrun PRIVATE arbor arborenv arbor-sup ext-json) +target_link_libraries(dryrun PRIVATE arbor arborenv arbor-sup ${json_library_name}) diff --git a/example/gap_junctions/CMakeLists.txt b/example/gap_junctions/CMakeLists.txt index abd78ebe..9aee5fbe 100644 --- a/example/gap_junctions/CMakeLists.txt +++ b/example/gap_junctions/CMakeLists.txt @@ -1,4 +1,4 @@ add_executable(gap_junctions EXCLUDE_FROM_ALL gap_junctions.cpp) add_dependencies(examples gap_junctions) -target_link_libraries(gap_junctions PRIVATE arbor arborenv arbor-sup ext-json) +target_link_libraries(gap_junctions PRIVATE arbor arborenv arbor-sup ${json_library_name}) diff --git a/example/generators/CMakeLists.txt b/example/generators/CMakeLists.txt index 7bfaca9f..2cd0c230 100644 --- a/example/generators/CMakeLists.txt +++ b/example/generators/CMakeLists.txt @@ -1,4 +1,4 @@ add_executable(generators EXCLUDE_FROM_ALL generators.cpp) add_dependencies(examples generators) -target_link_libraries(generators PRIVATE arbor arbor-sup ext-json) +target_link_libraries(generators PRIVATE arbor arbor-sup ${json_library_name}) diff --git a/example/ring/CMakeLists.txt b/example/ring/CMakeLists.txt index 76bb5afb..953ca291 100644 --- a/example/ring/CMakeLists.txt +++ b/example/ring/CMakeLists.txt @@ -1,4 +1,4 @@ add_executable(ring EXCLUDE_FROM_ALL ring.cpp) add_dependencies(examples ring) -target_link_libraries(ring PRIVATE arbor arborenv arbor-sup ext-json) +target_link_libraries(ring PRIVATE arbor arborenv arbor-sup ${json_library_name}) diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index b06c0cd2..f613d664 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -1,7 +1,10 @@ # Niels Lohmann's json library (single-header version). -add_library(ext-json INTERFACE) -target_include_directories(ext-json INTERFACE json/single_include) +if(ARB_USE_BUNDLED_JSON) + add_library(ext-json INTERFACE) + target_include_directories(ext-json INTERFACE json/single_include) + set(json_library_name ext-json PARENT_SCOPE) +endif() # tinyopt command line parsing libary (header-only). diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index de34bf47..744a0d18 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,18 +1,23 @@ -include(FindPythonModule) # required for find_python_module +set(PYBIND11_CPP_STANDARD -std=c++17) + +if(ARB_USE_BUNDLED_PYBIND11) + include(FindPythonModule) # required for find_python_module -# Set up pybind11 as an external project. -set(pb11_src_dir "${PROJECT_SOURCE_DIR}/python/pybind11") -check_git_submodule(pybind11 "${pb11_src_dir}") + # Set up pybind11 as an external project. + set(pb11_src_dir "${PROJECT_SOURCE_DIR}/python/pybind11") + check_git_submodule(pybind11 "${pb11_src_dir}") -if(NOT pybind11_avail) + if(NOT pybind11_avail) message(FATAL_ERROR "The git submodule for pybind11 is not available, required for python support") -endif() + endif() -# Set up pybind11, which is used to generate Python bindings. -# Pybind11 has good cmake support, so just add the pybind11 directory, -# instead of using find_package. -set(PYBIND11_CPP_STANDARD -std=c++17) -add_subdirectory(pybind11) + # Set up pybind11, which is used to generate Python bindings. + # Pybind11 has good cmake support, so just add the pybind11 directory, + # instead of using find_package. + add_subdirectory(pybind11) +else() + find_package(pybind11 REQUIRED) +endif() set(pyarb_source cable_probes.cpp diff --git a/scripts/travis/build.sh b/scripts/travis/build.sh index f3e09600..60e6e95b 100755 --- a/scripts/travis/build.sh +++ b/scripts/travis/build.sh @@ -81,7 +81,7 @@ if which xcrun >/dev/null; then typeset -x CMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}":$(xcrun --sdk macosx --show-sdk-path)/usr fi -cmake_flags="-DARB_WITH_ASSERTIONS=ON -DARB_WITH_NEUROML=${WITH_NEUROML} -DARB_WITH_MPI=${WITH_MPI} -DARB_WITH_PYTHON=${ARB_WITH_PYTHON} -DARB_ARCH=${ARCH} ${CXX_FLAGS} ${PY_FLAGS}" +cmake_flags="-DARB_WITH_ASSERTIONS=ON -DARB_USE_BUNDLED_LIBS=ON -DARB_WITH_NEUROML=${WITH_NEUROML} -DARB_WITH_MPI=${WITH_MPI} -DARB_WITH_PYTHON=${ARB_WITH_PYTHON} -DARB_ARCH=${ARCH} ${CXX_FLAGS} ${PY_FLAGS}" echo "cmake flags: ${cmake_flags}" cmake .. ${cmake_flags} || error "unable to configure cmake" diff --git a/setup.py b/setup.py index 02c16f0d..b1eda7ad 100644 --- a/setup.py +++ b/setup.py @@ -52,7 +52,8 @@ class install_command(install): ('gpu=', None, 'enable nvidia cuda support (requires cudaruntime and nvcc) or amd hip support. Supported values: ' 'none, cuda, cuda-clang, hip'), ('vec', None, 'enable vectorization'), - ('arch=', None, 'cpu architecture, e.g. haswell, skylake, armv8-a'), + ('arch=', None, 'cpu architecture, e.g. haswell, skylake, armv8.2-a+sve, znver2 (default native).'), + ('sysdeps', None, 'don\'t use bundled 3rd party C++ dependencies (pybind11 and json). This flag forces use of dependencies installed on the system.') ] def initialize_options(self): @@ -61,6 +62,7 @@ class install_command(install): self.gpu = None self.arch = None self.vec = None + self.sysdeps = None def finalize_options(self): install.finalize_options(self) @@ -76,6 +78,9 @@ class install_command(install): opt['vec'] = self.vec is not None # arch : target CPU micro-architecture (string). opt['arch'] = "native" if self.arch is None else self.arch + # bundled : use bundled/git-submoduled 3rd party libraries. + # By default use bundled libs. + opt['bundled'] = self.sysdeps is None install.run(self) @@ -101,11 +106,12 @@ class cmake_build(build_ext): opt = cl_opt() cmake_args = [ '-DARB_WITH_PYTHON=on', - '-DPython3_EXECUTABLE=' + sys.executable, + '-DPYTHON_EXECUTABLE=' + sys.executable, '-DARB_WITH_MPI={}'.format( 'on' if opt['mpi'] else 'off'), '-DARB_VECTORIZE={}'.format('on' if opt['vec'] else 'off'), '-DARB_ARCH={}'.format(opt['arch']), '-DARB_GPU={}'.format(opt['gpu']), + '-DARB_USE_BUNDLED_LIBS={}'.format('on' if opt['bundled'] else 'off']), '-DCMAKE_BUILD_TYPE=Release' # we compile with debug symbols in release mode. ] diff --git a/sup/CMakeLists.txt b/sup/CMakeLists.txt index 0a9e7306..79627717 100644 --- a/sup/CMakeLists.txt +++ b/sup/CMakeLists.txt @@ -10,7 +10,7 @@ add_library(arbor-sup ${sup-sources}) target_compile_options(arbor-sup PRIVATE ${ARB_CXXOPT_ARCH}) # The sup library uses both the json library and libarbor -target_link_libraries(arbor-sup PUBLIC ext-json arbor) +target_link_libraries(arbor-sup PUBLIC ${json_library_name} arbor) target_include_directories(arbor-sup PUBLIC include) -- GitLab