From f4b2e0348ba8f8f3104579e5ada1c827240dc990 Mon Sep 17 00:00:00 2001
From: Sam Yates <yates@cscs.ch>
Date: Thu, 31 Jan 2019 16:16:08 +0100
Subject: [PATCH] Split out 'arborenv' as an installable library from the sup
 library. (#679)

Make a new installed library `libarborenv.a` covering a subset of the `sup` library functionality, with corresponding installed CMake target `arbor::arborenv`.

* Move NVML or CUDA 10 API decision for GPU UUID discovery to top level CMake.
* Move affinity, concurrency, MPI init guard, and gpu detection and negotiation functionality out of `sup` and into new library `arborenv`.
* Move `include/arbor` in project tree to `arbor/include/arbor` (for consistency across `sup`, `arbor`, and `arborenv` subdirectories.)
* Wrangle more explicit library dependency adding CMake code into the installed `arbor-config.cmake`, to help mitigate [CMake issue #18614](https://gitlab.kitware.com/cmake/cmake/issues/18614).
* Have `arborenv` code throw `std::runtime_exception` instead of `arb::arbor_error`. (We are still using `arb::mpi_error` though for a failure in `with_mpi`.)
* Move `scope_exit` into the `arb::util` namespace.
* Merge `affinity.hpp` into `concurrency.hpp`.
* Rename `gpu.hpp` to `gpu_env.hpp` in `arborenv` includes.

Fixes #647.
---
 CMakeLists.txt                                | 75 +++++++++++++++----
 {include => arbor/include}/CMakeLists.txt     |  0
 .../include}/arbor/arbexcept.hpp              |  0
 {include => arbor/include}/arbor/assert.hpp   |  0
 .../include}/arbor/assert_macro.hpp.disabled  |  0
 .../include}/arbor/assert_macro.hpp.enabled   |  0
 .../include}/arbor/benchmark_cell.hpp         |  0
 .../include}/arbor/common_types.hpp           |  0
 .../arbor/communication/mpi_error.hpp         |  0
 .../include}/arbor/constants.hpp              |  0
 {include => arbor/include}/arbor/context.hpp  |  0
 .../include}/arbor/domain_decomposition.hpp   |  0
 .../include}/arbor/event_generator.hpp        |  0
 .../include}/arbor/fvm_types.hpp              |  0
 .../include}/arbor/generic_event.hpp          |  0
 {include => arbor/include}/arbor/ion.hpp      |  0
 {include => arbor/include}/arbor/lif_cell.hpp |  0
 .../include}/arbor/load_balance.hpp           |  0
 {include => arbor/include}/arbor/math.hpp     |  0
 {include => arbor/include}/arbor/mc_cell.hpp  |  0
 .../include}/arbor/mc_segment.hpp             |  0
 .../include}/arbor/mechanism.hpp              |  0
 {include => arbor/include}/arbor/mechcat.hpp  |  0
 {include => arbor/include}/arbor/mechinfo.hpp |  0
 .../include}/arbor/morphology.hpp             |  0
 {include => arbor/include}/arbor/point.hpp    |  0
 .../include}/arbor/profile/clock.hpp          |  0
 .../include}/arbor/profile/meter.hpp          |  0
 .../include}/arbor/profile/meter_manager.hpp  |  0
 .../include}/arbor/profile/profiler.hpp       |  0
 .../include}/arbor/profile/timer.hpp          |  0
 {include => arbor/include}/arbor/recipe.hpp   |  0
 {include => arbor/include}/arbor/sampling.hpp |  0
 {include => arbor/include}/arbor/schedule.hpp |  0
 .../include}/arbor/simd/approx.hpp            |  0
 {include => arbor/include}/arbor/simd/avx.hpp |  0
 .../include}/arbor/simd/avx512.hpp            |  0
 .../include}/arbor/simd/generic.hpp           |  0
 .../include}/arbor/simd/implbase.hpp          |  0
 .../include}/arbor/simd/native.hpp            |  0
 .../include}/arbor/simd/simd.hpp              |  0
 .../include}/arbor/simd/simd_io.hpp           |  0
 .../include}/arbor/simple_sampler.hpp         |  0
 .../include}/arbor/simulation.hpp             |  0
 {include => arbor/include}/arbor/spike.hpp    |  0
 .../include}/arbor/spike_event.hpp            |  0
 .../include}/arbor/spike_source_cell.hpp      |  0
 {include => arbor/include}/arbor/swcio.hpp    |  0
 .../include}/arbor/symmetric_recipe.hpp       |  0
 {include => arbor/include}/arbor/util/any.hpp |  0
 .../include}/arbor/util/any_ptr.hpp           |  0
 .../include}/arbor/util/compat.hpp            |  0
 .../include}/arbor/util/handle_set.hpp        |  0
 .../include}/arbor/util/lexcmp_def.hpp        |  0
 .../include}/arbor/util/optional.hpp          |  0
 .../include}/arbor/util/pp_util.hpp           |  0
 .../include/arbor/util}/scope_exit.hpp        |  6 +-
 .../include}/arbor/util/uninitialized.hpp     |  0
 .../include}/arbor/util/unique_any.hpp        |  0
 {include => arbor/include}/git-source-id      |  0
 arborenv/CMakeLists.txt                       | 32 ++++++++
 {sup => arborenv}/affinity.cpp                |  8 +-
 {sup => arborenv}/concurrency.cpp             | 13 ++--
 {sup => arborenv}/default_gpu.cpp             |  8 +-
 {sup => arborenv}/gpu_uuid.cpp                | 21 ++++--
 {sup => arborenv}/gpu_uuid.hpp                |  4 +-
 .../include/arborenv}/concurrency.hpp         | 17 ++++-
 .../include/arborenv/gpu_env.hpp              |  6 +-
 .../include/arborenv}/with_mpi.hpp            |  6 +-
 {sup => arborenv}/private_gpu.cpp             | 39 +++++-----
 cmake/arbor-config.cmake.in                   | 27 ++++---
 example/bench/CMakeLists.txt                  |  2 +-
 example/bench/bench.cpp                       | 19 +++--
 example/brunel/CMakeLists.txt                 |  2 +-
 example/brunel/brunel_miniapp.cpp             | 18 +++--
 example/dryrun/CMakeLists.txt                 |  2 +-
 example/dryrun/dryrun.cpp                     |  4 +-
 example/miniapp/CMakeLists.txt                |  2 +-
 example/miniapp/miniapp.cpp                   | 18 +++--
 example/ring/CMakeLists.txt                   |  2 +-
 example/ring/ring.cpp                         | 17 +++--
 sup/CMakeLists.txt                            | 32 --------
 sup/glob.cpp                                  |  4 +-
 sup/include/sup/affinity.hpp                  | 19 -----
 test/unit-distributed/CMakeLists.txt          |  4 +-
 test/unit-distributed/test.cpp                |  5 +-
 test/unit/test_scope_exit.cpp                 |  4 +-
 87 files changed, 241 insertions(+), 175 deletions(-)
 rename {include => arbor/include}/CMakeLists.txt (100%)
 rename {include => arbor/include}/arbor/arbexcept.hpp (100%)
 rename {include => arbor/include}/arbor/assert.hpp (100%)
 rename {include => arbor/include}/arbor/assert_macro.hpp.disabled (100%)
 rename {include => arbor/include}/arbor/assert_macro.hpp.enabled (100%)
 rename {include => arbor/include}/arbor/benchmark_cell.hpp (100%)
 rename {include => arbor/include}/arbor/common_types.hpp (100%)
 rename {include => arbor/include}/arbor/communication/mpi_error.hpp (100%)
 rename {include => arbor/include}/arbor/constants.hpp (100%)
 rename {include => arbor/include}/arbor/context.hpp (100%)
 rename {include => arbor/include}/arbor/domain_decomposition.hpp (100%)
 rename {include => arbor/include}/arbor/event_generator.hpp (100%)
 rename {include => arbor/include}/arbor/fvm_types.hpp (100%)
 rename {include => arbor/include}/arbor/generic_event.hpp (100%)
 rename {include => arbor/include}/arbor/ion.hpp (100%)
 rename {include => arbor/include}/arbor/lif_cell.hpp (100%)
 rename {include => arbor/include}/arbor/load_balance.hpp (100%)
 rename {include => arbor/include}/arbor/math.hpp (100%)
 rename {include => arbor/include}/arbor/mc_cell.hpp (100%)
 rename {include => arbor/include}/arbor/mc_segment.hpp (100%)
 rename {include => arbor/include}/arbor/mechanism.hpp (100%)
 rename {include => arbor/include}/arbor/mechcat.hpp (100%)
 rename {include => arbor/include}/arbor/mechinfo.hpp (100%)
 rename {include => arbor/include}/arbor/morphology.hpp (100%)
 rename {include => arbor/include}/arbor/point.hpp (100%)
 rename {include => arbor/include}/arbor/profile/clock.hpp (100%)
 rename {include => arbor/include}/arbor/profile/meter.hpp (100%)
 rename {include => arbor/include}/arbor/profile/meter_manager.hpp (100%)
 rename {include => arbor/include}/arbor/profile/profiler.hpp (100%)
 rename {include => arbor/include}/arbor/profile/timer.hpp (100%)
 rename {include => arbor/include}/arbor/recipe.hpp (100%)
 rename {include => arbor/include}/arbor/sampling.hpp (100%)
 rename {include => arbor/include}/arbor/schedule.hpp (100%)
 rename {include => arbor/include}/arbor/simd/approx.hpp (100%)
 rename {include => arbor/include}/arbor/simd/avx.hpp (100%)
 rename {include => arbor/include}/arbor/simd/avx512.hpp (100%)
 rename {include => arbor/include}/arbor/simd/generic.hpp (100%)
 rename {include => arbor/include}/arbor/simd/implbase.hpp (100%)
 rename {include => arbor/include}/arbor/simd/native.hpp (100%)
 rename {include => arbor/include}/arbor/simd/simd.hpp (100%)
 rename {include => arbor/include}/arbor/simd/simd_io.hpp (100%)
 rename {include => arbor/include}/arbor/simple_sampler.hpp (100%)
 rename {include => arbor/include}/arbor/simulation.hpp (100%)
 rename {include => arbor/include}/arbor/spike.hpp (100%)
 rename {include => arbor/include}/arbor/spike_event.hpp (100%)
 rename {include => arbor/include}/arbor/spike_source_cell.hpp (100%)
 rename {include => arbor/include}/arbor/swcio.hpp (100%)
 rename {include => arbor/include}/arbor/symmetric_recipe.hpp (100%)
 rename {include => arbor/include}/arbor/util/any.hpp (100%)
 rename {include => arbor/include}/arbor/util/any_ptr.hpp (100%)
 rename {include => arbor/include}/arbor/util/compat.hpp (100%)
 rename {include => arbor/include}/arbor/util/handle_set.hpp (100%)
 rename {include => arbor/include}/arbor/util/lexcmp_def.hpp (100%)
 rename {include => arbor/include}/arbor/util/optional.hpp (100%)
 rename {include => arbor/include}/arbor/util/pp_util.hpp (100%)
 rename {sup/include/sup => arbor/include/arbor/util}/scope_exit.hpp (96%)
 rename {include => arbor/include}/arbor/util/uninitialized.hpp (100%)
 rename {include => arbor/include}/arbor/util/unique_any.hpp (100%)
 rename {include => arbor/include}/git-source-id (100%)
 create mode 100644 arborenv/CMakeLists.txt
 rename {sup => arborenv}/affinity.cpp (89%)
 rename {sup => arborenv}/concurrency.cpp (87%)
 rename {sup => arborenv}/default_gpu.cpp (85%)
 rename {sup => arborenv}/gpu_uuid.cpp (97%)
 rename {sup => arborenv}/gpu_uuid.hpp (96%)
 rename {sup/include/sup => arborenv/include/arborenv}/concurrency.hpp (61%)
 rename sup/include/sup/gpu.hpp => arborenv/include/arborenv/gpu_env.hpp (59%)
 rename {sup/include/sup => arborenv/include/arborenv}/with_mpi.hpp (94%)
 rename {sup => arborenv}/private_gpu.cpp (93%)
 delete mode 100644 sup/include/sup/affinity.hpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0930bf88..95c94a11 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -75,6 +75,7 @@ set(THREADS_PREFER_PTHREAD_FLAG OFF)
 # expressions.)
 if(ARB_WITH_GPU)
     enable_language(CUDA)
+
     # Despite native CUDA support, the CUDA package is still required to find
     # the NVML library and to export the cuda library dependencies from the
     # installed target.
@@ -111,12 +112,27 @@ set(CMAKE_CXX_STANDARD 14)
 # to the 'export set', even the private ones, and this must be done
 # in the same CMakeLists.txt in which the target is defined.
 
+# Interface library `arbor-config-defs` collects configure-time defines
+# for arbor and arborenv, of the form ARB_HAVE_XXX. These defines should
+# _not_ be used in any installed public headers.
+
+add_library(arbor-config-defs INTERFACE)
+install(TARGETS arbor-config-defs EXPORT arbor-targets)
+
 # Interface library `arbor-private-deps` collects dependencies, options etc.
 # for the arbor library.
 
 add_library(arbor-private-deps INTERFACE)
+target_link_libraries(arbor-private-deps INTERFACE arbor-config-defs)
 install(TARGETS arbor-private-deps EXPORT arbor-targets)
 
+# Interface library `arborenv-private-deps` collects dependencies, options etc.
+# for the arborenv library.
+
+add_library(arborenv-private-deps INTERFACE)
+target_link_libraries(arborenv-private-deps INTERFACE arbor-config-defs)
+install(TARGETS arborenv-private-deps EXPORT arbor-targets)
+
 # Interface library `arbor-public-deps` collects requirements for the
 # users of the arbor library (e.g. mpi) that will become part
 # of arbor's PUBLIC interface.
@@ -129,11 +145,6 @@ install(TARGETS arbor-public-deps EXPORT arbor-targets)
 
 add_subdirectory(ext)
 
-# Support utiliies in `sup` are common across test executables
-# and examples. Creates interface library `arbor-sup`.
-
-add_subdirectory(sup)
-
 # Keep track of packages we need to add to the generated CMake config
 # file for arbor.
 
@@ -149,16 +160,17 @@ set(arbor_supported_components)
 if(ARB_ARCH)
     set_arch_target(ARB_CXXOPT_ARCH "${ARB_ARCH}")
     target_compile_options(arbor-private-deps INTERFACE ${ARB_CXXOPT_ARCH})
+    target_compile_options(arborenv-private-deps INTERFACE ${ARB_CXXOPT_ARCH})
 endif()
 
 # Profiling and test features
 #-----------------------------
 
 if(ARB_WITH_PROFILING)
-    target_compile_definitions(arbor-private-deps INTERFACE ARB_HAVE_PROFILING)
+    target_compile_definitions(arbor-config-defs INTERFACE ARB_HAVE_PROFILING)
 endif()
 if(ARB_WITH_ASSERTIONS)
-    target_compile_definitions(arbor-private-deps INTERFACE ARB_HAVE_ASSERTIONS)
+    target_compile_definitions(arbor-config-defs INTERFACE ARB_HAVE_ASSERTIONS)
 endif()
 
 # Threading model
@@ -175,7 +187,7 @@ list(APPEND arbor_export_dependencies "Threads")
 
 if(ARB_WITH_MPI)
     find_package(MPI REQUIRED CXX)
-    target_compile_definitions(arbor-private-deps INTERFACE ARB_HAVE_MPI)
+    target_compile_definitions(arbor-config-defs INTERFACE ARB_HAVE_MPI)
 
     # target_compile_definitions(MPI::MPI_CXX INTERFACE MPICH_SKIP_MPICXX=1 OMPI_SKIP_MPICXX=1)
     # target_link_libraries(arbor-public-deps INTERFACE MPI::MPI_CXX)
@@ -186,6 +198,7 @@ if(ARB_WITH_MPI)
     add_library(mpi-wrap INTERFACE)
     target_link_libraries(mpi-wrap INTERFACE MPI::MPI_CXX)
     target_compile_definitions(mpi-wrap INTERFACE MPICH_SKIP_MPICXX=1 OMPI_SKIP_MPICXX=1)
+
     target_link_libraries(arbor-public-deps INTERFACE mpi-wrap)
     install(TARGETS mpi-wrap EXPORT arbor-targets)
 
@@ -198,14 +211,38 @@ endif()
 
 if(ARB_WITH_GPU)
     set(ARB_WITH_CUDA TRUE)
+    target_compile_definitions(arbor-config-defs INTERFACE ARB_HAVE_GPU)
+    if(ARB_WITH_GPU_FINE_MATRIX)
+        target_compile_definitions(arbor-config-defs INTERFACE ARB_HAVE_GPU_FINE_MATRIX)
+    endif()
+
+    target_include_directories(arborenv-private-deps INTERFACE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
+
+    # The arborenv lib needs to use the CUDA NVML library for CUDA 9.
+
+    if (${CUDA_VERSION_MAJOR} LESS 10)
+        set(arborenv_requires_nvml TRUE)
+    endif()
+
+    if(arborenv_requires_nvml)
+        set(nvml_names nvidia-ml) # Note: platform dependent? e.g. nvml.lib on Windows.
+        find_library(CUDA_NVML
+                     NAMES ${nvml_names}
+                     NO_DEFAULT_PATH
+                     PATHS ${CMAKE_CUDA_IMPLICIT_DIRECTORIES} ${CUDA_TOOLKIT_ROOT_DIR}
+                     PATH_SUFFIXES lib64/stubs lib/stubs)
+
+        if (NOT CUDA_NVML)
+            message(FATAL_ERROR "Unable to find CUDA NVML library by: ${nvml_names}")
+        endif()
+
+        target_link_libraries(arborenv-private-deps INTERFACE ${CUDA_NVML})
+        target_compile_definitions(arborenv-private-deps INTERFACE ARBENV_USE_NVML)
+    endif()
 
     add_compile_options(
         "$<$<COMPILE_LANGUAGE:CUDA>:-Xcudafe=--diag_suppress=integer_sign_change>"
         "$<$<COMPILE_LANGUAGE:CUDA>:-Xcudafe=--diag_suppress=unsigned_compare_with_zero>")
-    target_compile_definitions(arbor-private-deps INTERFACE ARB_HAVE_GPU)
-    if(ARB_WITH_GPU_FINE_MATRIX)
-        target_compile_definitions(arbor-private-deps INTERFACE ARB_HAVE_GPU_FINE_MATRIX)
-    endif()
 
     target_compile_options(arbor-private-deps INTERFACE
         $<$<COMPILE_LANGUAGE:CUDA>:-gencode=arch=compute_35,code=sm_35>)
@@ -294,7 +331,10 @@ endforeach()
 #----------------------------------------------------------
 
 # arbor-public-headers:
-add_subdirectory(include)
+add_subdirectory(arbor/include)
+
+# arbor-sup:
+add_subdirectory(sup)
 
 # modcc, libmodcc:
 add_subdirectory(modcc)
@@ -302,6 +342,9 @@ add_subdirectory(modcc)
 # arbor, arbor-private-headers:
 add_subdirectory(arbor)
 
+# arborenv, arborenv-public-headers:
+add_subdirectory(arborenv)
+
 # unit, unit-mpi, unit-local, unit-modcc, validate
 add_subdirectory(test)
 
@@ -353,10 +396,16 @@ endif()
 
 set(arbor_override_import_lang)
 set(arbor_add_import_libs)
+set(arborenv_add_import_libs)
 
 if(ARB_WITH_GPU)
     set(arbor_override_import_lang CXX)
     set(arbor_add_import_libs ${CUDA_LIBRARIES})
+    set(arborenv_add_import_libs ${CUDA_LIBRARIES})
+
+    if(arborenv_requires_nvml)
+        list(APPEND arborenv_add_import_libs ${CUDA_NVML})
+    endif()
 endif()
 
 configure_file(
diff --git a/include/CMakeLists.txt b/arbor/include/CMakeLists.txt
similarity index 100%
rename from include/CMakeLists.txt
rename to arbor/include/CMakeLists.txt
diff --git a/include/arbor/arbexcept.hpp b/arbor/include/arbor/arbexcept.hpp
similarity index 100%
rename from include/arbor/arbexcept.hpp
rename to arbor/include/arbor/arbexcept.hpp
diff --git a/include/arbor/assert.hpp b/arbor/include/arbor/assert.hpp
similarity index 100%
rename from include/arbor/assert.hpp
rename to arbor/include/arbor/assert.hpp
diff --git a/include/arbor/assert_macro.hpp.disabled b/arbor/include/arbor/assert_macro.hpp.disabled
similarity index 100%
rename from include/arbor/assert_macro.hpp.disabled
rename to arbor/include/arbor/assert_macro.hpp.disabled
diff --git a/include/arbor/assert_macro.hpp.enabled b/arbor/include/arbor/assert_macro.hpp.enabled
similarity index 100%
rename from include/arbor/assert_macro.hpp.enabled
rename to arbor/include/arbor/assert_macro.hpp.enabled
diff --git a/include/arbor/benchmark_cell.hpp b/arbor/include/arbor/benchmark_cell.hpp
similarity index 100%
rename from include/arbor/benchmark_cell.hpp
rename to arbor/include/arbor/benchmark_cell.hpp
diff --git a/include/arbor/common_types.hpp b/arbor/include/arbor/common_types.hpp
similarity index 100%
rename from include/arbor/common_types.hpp
rename to arbor/include/arbor/common_types.hpp
diff --git a/include/arbor/communication/mpi_error.hpp b/arbor/include/arbor/communication/mpi_error.hpp
similarity index 100%
rename from include/arbor/communication/mpi_error.hpp
rename to arbor/include/arbor/communication/mpi_error.hpp
diff --git a/include/arbor/constants.hpp b/arbor/include/arbor/constants.hpp
similarity index 100%
rename from include/arbor/constants.hpp
rename to arbor/include/arbor/constants.hpp
diff --git a/include/arbor/context.hpp b/arbor/include/arbor/context.hpp
similarity index 100%
rename from include/arbor/context.hpp
rename to arbor/include/arbor/context.hpp
diff --git a/include/arbor/domain_decomposition.hpp b/arbor/include/arbor/domain_decomposition.hpp
similarity index 100%
rename from include/arbor/domain_decomposition.hpp
rename to arbor/include/arbor/domain_decomposition.hpp
diff --git a/include/arbor/event_generator.hpp b/arbor/include/arbor/event_generator.hpp
similarity index 100%
rename from include/arbor/event_generator.hpp
rename to arbor/include/arbor/event_generator.hpp
diff --git a/include/arbor/fvm_types.hpp b/arbor/include/arbor/fvm_types.hpp
similarity index 100%
rename from include/arbor/fvm_types.hpp
rename to arbor/include/arbor/fvm_types.hpp
diff --git a/include/arbor/generic_event.hpp b/arbor/include/arbor/generic_event.hpp
similarity index 100%
rename from include/arbor/generic_event.hpp
rename to arbor/include/arbor/generic_event.hpp
diff --git a/include/arbor/ion.hpp b/arbor/include/arbor/ion.hpp
similarity index 100%
rename from include/arbor/ion.hpp
rename to arbor/include/arbor/ion.hpp
diff --git a/include/arbor/lif_cell.hpp b/arbor/include/arbor/lif_cell.hpp
similarity index 100%
rename from include/arbor/lif_cell.hpp
rename to arbor/include/arbor/lif_cell.hpp
diff --git a/include/arbor/load_balance.hpp b/arbor/include/arbor/load_balance.hpp
similarity index 100%
rename from include/arbor/load_balance.hpp
rename to arbor/include/arbor/load_balance.hpp
diff --git a/include/arbor/math.hpp b/arbor/include/arbor/math.hpp
similarity index 100%
rename from include/arbor/math.hpp
rename to arbor/include/arbor/math.hpp
diff --git a/include/arbor/mc_cell.hpp b/arbor/include/arbor/mc_cell.hpp
similarity index 100%
rename from include/arbor/mc_cell.hpp
rename to arbor/include/arbor/mc_cell.hpp
diff --git a/include/arbor/mc_segment.hpp b/arbor/include/arbor/mc_segment.hpp
similarity index 100%
rename from include/arbor/mc_segment.hpp
rename to arbor/include/arbor/mc_segment.hpp
diff --git a/include/arbor/mechanism.hpp b/arbor/include/arbor/mechanism.hpp
similarity index 100%
rename from include/arbor/mechanism.hpp
rename to arbor/include/arbor/mechanism.hpp
diff --git a/include/arbor/mechcat.hpp b/arbor/include/arbor/mechcat.hpp
similarity index 100%
rename from include/arbor/mechcat.hpp
rename to arbor/include/arbor/mechcat.hpp
diff --git a/include/arbor/mechinfo.hpp b/arbor/include/arbor/mechinfo.hpp
similarity index 100%
rename from include/arbor/mechinfo.hpp
rename to arbor/include/arbor/mechinfo.hpp
diff --git a/include/arbor/morphology.hpp b/arbor/include/arbor/morphology.hpp
similarity index 100%
rename from include/arbor/morphology.hpp
rename to arbor/include/arbor/morphology.hpp
diff --git a/include/arbor/point.hpp b/arbor/include/arbor/point.hpp
similarity index 100%
rename from include/arbor/point.hpp
rename to arbor/include/arbor/point.hpp
diff --git a/include/arbor/profile/clock.hpp b/arbor/include/arbor/profile/clock.hpp
similarity index 100%
rename from include/arbor/profile/clock.hpp
rename to arbor/include/arbor/profile/clock.hpp
diff --git a/include/arbor/profile/meter.hpp b/arbor/include/arbor/profile/meter.hpp
similarity index 100%
rename from include/arbor/profile/meter.hpp
rename to arbor/include/arbor/profile/meter.hpp
diff --git a/include/arbor/profile/meter_manager.hpp b/arbor/include/arbor/profile/meter_manager.hpp
similarity index 100%
rename from include/arbor/profile/meter_manager.hpp
rename to arbor/include/arbor/profile/meter_manager.hpp
diff --git a/include/arbor/profile/profiler.hpp b/arbor/include/arbor/profile/profiler.hpp
similarity index 100%
rename from include/arbor/profile/profiler.hpp
rename to arbor/include/arbor/profile/profiler.hpp
diff --git a/include/arbor/profile/timer.hpp b/arbor/include/arbor/profile/timer.hpp
similarity index 100%
rename from include/arbor/profile/timer.hpp
rename to arbor/include/arbor/profile/timer.hpp
diff --git a/include/arbor/recipe.hpp b/arbor/include/arbor/recipe.hpp
similarity index 100%
rename from include/arbor/recipe.hpp
rename to arbor/include/arbor/recipe.hpp
diff --git a/include/arbor/sampling.hpp b/arbor/include/arbor/sampling.hpp
similarity index 100%
rename from include/arbor/sampling.hpp
rename to arbor/include/arbor/sampling.hpp
diff --git a/include/arbor/schedule.hpp b/arbor/include/arbor/schedule.hpp
similarity index 100%
rename from include/arbor/schedule.hpp
rename to arbor/include/arbor/schedule.hpp
diff --git a/include/arbor/simd/approx.hpp b/arbor/include/arbor/simd/approx.hpp
similarity index 100%
rename from include/arbor/simd/approx.hpp
rename to arbor/include/arbor/simd/approx.hpp
diff --git a/include/arbor/simd/avx.hpp b/arbor/include/arbor/simd/avx.hpp
similarity index 100%
rename from include/arbor/simd/avx.hpp
rename to arbor/include/arbor/simd/avx.hpp
diff --git a/include/arbor/simd/avx512.hpp b/arbor/include/arbor/simd/avx512.hpp
similarity index 100%
rename from include/arbor/simd/avx512.hpp
rename to arbor/include/arbor/simd/avx512.hpp
diff --git a/include/arbor/simd/generic.hpp b/arbor/include/arbor/simd/generic.hpp
similarity index 100%
rename from include/arbor/simd/generic.hpp
rename to arbor/include/arbor/simd/generic.hpp
diff --git a/include/arbor/simd/implbase.hpp b/arbor/include/arbor/simd/implbase.hpp
similarity index 100%
rename from include/arbor/simd/implbase.hpp
rename to arbor/include/arbor/simd/implbase.hpp
diff --git a/include/arbor/simd/native.hpp b/arbor/include/arbor/simd/native.hpp
similarity index 100%
rename from include/arbor/simd/native.hpp
rename to arbor/include/arbor/simd/native.hpp
diff --git a/include/arbor/simd/simd.hpp b/arbor/include/arbor/simd/simd.hpp
similarity index 100%
rename from include/arbor/simd/simd.hpp
rename to arbor/include/arbor/simd/simd.hpp
diff --git a/include/arbor/simd/simd_io.hpp b/arbor/include/arbor/simd/simd_io.hpp
similarity index 100%
rename from include/arbor/simd/simd_io.hpp
rename to arbor/include/arbor/simd/simd_io.hpp
diff --git a/include/arbor/simple_sampler.hpp b/arbor/include/arbor/simple_sampler.hpp
similarity index 100%
rename from include/arbor/simple_sampler.hpp
rename to arbor/include/arbor/simple_sampler.hpp
diff --git a/include/arbor/simulation.hpp b/arbor/include/arbor/simulation.hpp
similarity index 100%
rename from include/arbor/simulation.hpp
rename to arbor/include/arbor/simulation.hpp
diff --git a/include/arbor/spike.hpp b/arbor/include/arbor/spike.hpp
similarity index 100%
rename from include/arbor/spike.hpp
rename to arbor/include/arbor/spike.hpp
diff --git a/include/arbor/spike_event.hpp b/arbor/include/arbor/spike_event.hpp
similarity index 100%
rename from include/arbor/spike_event.hpp
rename to arbor/include/arbor/spike_event.hpp
diff --git a/include/arbor/spike_source_cell.hpp b/arbor/include/arbor/spike_source_cell.hpp
similarity index 100%
rename from include/arbor/spike_source_cell.hpp
rename to arbor/include/arbor/spike_source_cell.hpp
diff --git a/include/arbor/swcio.hpp b/arbor/include/arbor/swcio.hpp
similarity index 100%
rename from include/arbor/swcio.hpp
rename to arbor/include/arbor/swcio.hpp
diff --git a/include/arbor/symmetric_recipe.hpp b/arbor/include/arbor/symmetric_recipe.hpp
similarity index 100%
rename from include/arbor/symmetric_recipe.hpp
rename to arbor/include/arbor/symmetric_recipe.hpp
diff --git a/include/arbor/util/any.hpp b/arbor/include/arbor/util/any.hpp
similarity index 100%
rename from include/arbor/util/any.hpp
rename to arbor/include/arbor/util/any.hpp
diff --git a/include/arbor/util/any_ptr.hpp b/arbor/include/arbor/util/any_ptr.hpp
similarity index 100%
rename from include/arbor/util/any_ptr.hpp
rename to arbor/include/arbor/util/any_ptr.hpp
diff --git a/include/arbor/util/compat.hpp b/arbor/include/arbor/util/compat.hpp
similarity index 100%
rename from include/arbor/util/compat.hpp
rename to arbor/include/arbor/util/compat.hpp
diff --git a/include/arbor/util/handle_set.hpp b/arbor/include/arbor/util/handle_set.hpp
similarity index 100%
rename from include/arbor/util/handle_set.hpp
rename to arbor/include/arbor/util/handle_set.hpp
diff --git a/include/arbor/util/lexcmp_def.hpp b/arbor/include/arbor/util/lexcmp_def.hpp
similarity index 100%
rename from include/arbor/util/lexcmp_def.hpp
rename to arbor/include/arbor/util/lexcmp_def.hpp
diff --git a/include/arbor/util/optional.hpp b/arbor/include/arbor/util/optional.hpp
similarity index 100%
rename from include/arbor/util/optional.hpp
rename to arbor/include/arbor/util/optional.hpp
diff --git a/include/arbor/util/pp_util.hpp b/arbor/include/arbor/util/pp_util.hpp
similarity index 100%
rename from include/arbor/util/pp_util.hpp
rename to arbor/include/arbor/util/pp_util.hpp
diff --git a/sup/include/sup/scope_exit.hpp b/arbor/include/arbor/util/scope_exit.hpp
similarity index 96%
rename from sup/include/sup/scope_exit.hpp
rename to arbor/include/arbor/util/scope_exit.hpp
index acba9da5..f353c1e2 100644
--- a/sup/include/sup/scope_exit.hpp
+++ b/arbor/include/arbor/util/scope_exit.hpp
@@ -6,7 +6,8 @@
 
 // Convenience class for RAII control of resources.
 
-namespace sup {
+namespace arb {
+namespace util {
 
 // `scope_exit` guard object will call provided functional object
 // on destruction. The provided functional object must be nothrow
@@ -73,4 +74,5 @@ auto on_scope_exit(std::function<R ()> f) {
     return on_scope_exit(impl::wrap_std_function<R>(std::move(f)));
 }
 
-} // namespace sup
+} // namespace util
+} // namespace arb
diff --git a/include/arbor/util/uninitialized.hpp b/arbor/include/arbor/util/uninitialized.hpp
similarity index 100%
rename from include/arbor/util/uninitialized.hpp
rename to arbor/include/arbor/util/uninitialized.hpp
diff --git a/include/arbor/util/unique_any.hpp b/arbor/include/arbor/util/unique_any.hpp
similarity index 100%
rename from include/arbor/util/unique_any.hpp
rename to arbor/include/arbor/util/unique_any.hpp
diff --git a/include/git-source-id b/arbor/include/git-source-id
similarity index 100%
rename from include/git-source-id
rename to arbor/include/git-source-id
diff --git a/arborenv/CMakeLists.txt b/arborenv/CMakeLists.txt
new file mode 100644
index 00000000..921622c0
--- /dev/null
+++ b/arborenv/CMakeLists.txt
@@ -0,0 +1,32 @@
+set(arborenv-sources
+    affinity.cpp
+    concurrency.cpp
+    default_gpu.cpp
+    private_gpu.cpp
+)
+
+if(ARB_WITH_GPU)
+    list(APPEND arborenv-sources gpu_uuid.cpp)
+endif()
+
+add_library(arborenv ${arborenv-sources})
+
+add_library(arborenv-public-headers INTERFACE)
+target_include_directories(arborenv-public-headers INTERFACE
+    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+    $<INSTALL_INTERFACE:include>
+)
+
+# A public arbor dependency is required for the use of arb::mpi_error,
+# which in turn requires the arbor libary's singleton for the mpi_error_category
+# singleton.
+
+target_link_libraries(arborenv PUBLIC arbor arborenv-public-headers)
+target_link_libraries(arborenv PRIVATE arbor-config-defs arborenv-private-deps)
+
+install(DIRECTORY include/arborenv
+    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+    FILES_MATCHING PATTERN "*.hpp")
+
+install(TARGETS arborenv-public-headers EXPORT arbor-targets)
+install(TARGETS arborenv EXPORT arbor-targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
diff --git a/sup/affinity.cpp b/arborenv/affinity.cpp
similarity index 89%
rename from sup/affinity.cpp
rename to arborenv/affinity.cpp
index 7454b30d..61502cc6 100644
--- a/sup/affinity.cpp
+++ b/arborenv/affinity.cpp
@@ -12,7 +12,7 @@ extern "C" {
 #include <sched.h>
 }
 
-namespace sup {
+namespace arbenv {
 
 std::vector<int> get_affinity() {
     std::vector<int> cores;
@@ -32,17 +32,17 @@ std::vector<int> get_affinity() {
     return cores;
 }
 
-} // namespace sup
+} // namespace arbenv
 
 #else // def __linux__
 
 // No support for non-linux systems.
-namespace sup {
+namespace arbenv {
 
 std::vector<int> get_affinity() {
     return {};
 }
 
-} // namespace sup
+} // namespace arbenv
 
 #endif // def __linux__
diff --git a/sup/concurrency.cpp b/arborenv/concurrency.cpp
similarity index 87%
rename from sup/concurrency.cpp
rename to arborenv/concurrency.cpp
index 6635ae37..fd0e5919 100644
--- a/sup/concurrency.cpp
+++ b/arborenv/concurrency.cpp
@@ -3,20 +3,18 @@
 #include <string>
 #include <thread>
 
-#include <arbor/arbexcept.hpp>
-
-#include <sup/affinity.hpp>
-#include <sup/concurrency.hpp>
+#include <arborenv/concurrency.hpp>
 
 // TODO: C++17 use __has_include(<unistd.h>)
 #if defined(__unix__) || defined(__APPLE__) && defined(__MACH__)
 #include <unistd.h>
 #endif
 
-namespace sup {
+namespace arbenv {
 
 // Test environment variables for user-specified count of threads.
 unsigned get_env_num_threads() {
+    using namespace std::literals;
     const char* str;
 
     // select variable to use:
@@ -43,8 +41,7 @@ unsigned get_env_num_threads() {
         !std::regex_match(str, std::regex("\\s*\\d*[0-9]\\d*\\s*")))
     {
         errno = 0;
-        throw arb::arbor_exception(
-            std::string("Requested number of threads \"") + str + "\" is not a valid value");
+        throw std::runtime_error("Requested number of threads \""s + str + "\" is not a valid value"s);
     }
     errno = 0;
 
@@ -78,4 +75,4 @@ unsigned thread_concurrency() {
     return n;
 }
 
-} // namespace sup
+} // namespace arbenv
diff --git a/sup/default_gpu.cpp b/arborenv/default_gpu.cpp
similarity index 85%
rename from sup/default_gpu.cpp
rename to arborenv/default_gpu.cpp
index 80f3f4b9..d13e8dd0 100644
--- a/sup/default_gpu.cpp
+++ b/arborenv/default_gpu.cpp
@@ -2,7 +2,7 @@
 
 #include <cuda_runtime.h>
 
-namespace sup {
+namespace arbenv {
 
 // When arbor does not have CUDA support, return -1, which always
 // indicates that no GPU is available.
@@ -16,17 +16,17 @@ int default_gpu() {
     return -1;
 }
 
-} // namespace sup
+} // namespace arbenv
 
 #else // ifdef ARB_HAVE_GPU
 
-namespace sup {
+namespace arbenv {
 
 int default_gpu() {
     return -1;
 }
 
-} // namespace sup
+} // namespace arbenv
 
 #endif // ifdef ARB_HAVE_GPU
 
diff --git a/sup/gpu_uuid.cpp b/arborenv/gpu_uuid.cpp
similarity index 97%
rename from sup/gpu_uuid.cpp
rename to arborenv/gpu_uuid.cpp
index 08c87be5..b31c1ae4 100644
--- a/sup/gpu_uuid.cpp
+++ b/arborenv/gpu_uuid.cpp
@@ -9,20 +9,25 @@
 #include <stdexcept>
 #include <vector>
 
-#include <sup/scope_exit.hpp>
+#include <cuda_runtime.h>
 
+#include <arbor/util/scope_exit.hpp>
 #include "gpu_uuid.hpp"
 
-#include <cuda_runtime.h>
 
 // CUDA 10 allows GPU uuid to be queried via cudaGetDeviceProperties.
 // Previous versions require the CUDA NVML library to get uuid.
-#if CUDART_VERSION < 10000
+//
+// ARBENV_USE_NVML will be defined at configuration time if using
+// CUDA version 9.
+
+#ifdef ARBENV_USE_NVML
     #include <nvml.h>
-    #define ARB_USE_NVML
 #endif
 
-namespace sup {
+using arb::util::on_scope_exit;
+
+namespace arbenv {
 
 // Test GPU uids for equality
 bool operator==(const uuid& lhs, const uuid& rhs) {
@@ -65,7 +70,7 @@ std::runtime_error make_runtime_error(cudaError_t error_code) {
         + cudaGetErrorName(error_code) + ": " + cudaGetErrorString(error_code));
 }
 
-#ifndef ARB_USE_NVML
+#ifndef ARBENV_USE_NVML
 
 // For CUDA 10 and later the uuid of all available GPUs is straightforward
 // to obtain by querying cudaGetDeviceProperties for each visible device.
@@ -237,7 +242,7 @@ std::vector<uuid> get_gpu_uuids() {
 
     return uuids;
 }
-#endif
+#endif // ndef ARBENV_USE_NVML
 
 // Compare two sets of uuids
 //   1: both sets are identical
@@ -308,4 +313,4 @@ gpu_rank assign_gpu(const std::vector<uuid>& uids,
     return pos_in_group<ngpu_in_group? gpu_rank(pos_in_group): gpu_rank(-1);
 }
 
-} // namespace sup
+} // namespace arbenv
diff --git a/sup/gpu_uuid.hpp b/arborenv/gpu_uuid.hpp
similarity index 96%
rename from sup/gpu_uuid.hpp
rename to arborenv/gpu_uuid.hpp
index f6f34a17..3226d694 100644
--- a/sup/gpu_uuid.hpp
+++ b/arborenv/gpu_uuid.hpp
@@ -4,7 +4,7 @@
 #include <ostream>
 #include <vector>
 
-namespace sup {
+namespace arbenv {
 
 // Store cudaUUID_t in a byte array for easy type punning and comparison.
 // 128 bit uuids are not just for GPUs: they are used in many applications,
@@ -38,4 +38,4 @@ struct gpu_rank {
 
 gpu_rank assign_gpu(const std::vector<uuid>& uids, const std::vector<int>&  uid_part, int rank);
 
-} // namespace sup
+} // namespace arbenv
diff --git a/sup/include/sup/concurrency.hpp b/arborenv/include/arborenv/concurrency.hpp
similarity index 61%
rename from sup/include/sup/concurrency.hpp
rename to arborenv/include/arborenv/concurrency.hpp
index c7a082f3..2aa0b770 100644
--- a/sup/include/sup/concurrency.hpp
+++ b/arborenv/include/arborenv/concurrency.hpp
@@ -1,8 +1,8 @@
 #pragma once
 
-#include <arbor/util/optional.hpp>
+#include <vector>
 
-namespace sup {
+namespace arbenv {
 
 // Test environment variables for user-specified count of threads.
 // Potential environment variables are tested in this order:
@@ -26,4 +26,15 @@ unsigned get_env_num_threads();
 // Will return at least 1.
 unsigned thread_concurrency();
 
-} // namespace sup
+// The list of logical processors for which the calling thread has affinity.
+// If calling from the main thread at application start up, before
+// attempting to change thread affinity, may produce unreliable
+// results.
+//  - beware thread pinning or custom job scheduler affinity
+//    flags that assign threads to specific cores.
+//
+// Returns an empty vector if unable to determine the number of
+// available cores.
+std::vector<int> get_affinity();
+
+} // namespace arbenv
diff --git a/sup/include/sup/gpu.hpp b/arborenv/include/arborenv/gpu_env.hpp
similarity index 59%
rename from sup/include/sup/gpu.hpp
rename to arborenv/include/arborenv/gpu_env.hpp
index 21b95a89..9db867af 100644
--- a/sup/include/sup/gpu.hpp
+++ b/arborenv/include/arborenv/gpu_env.hpp
@@ -1,13 +1,11 @@
 #pragma once
 
-#include <arbor/version.hpp>
-
-namespace sup {
+namespace arbenv {
 
 int default_gpu();
 
 template <typename Comm>
 int find_private_gpu(Comm comm);
 
-} // namespace sup
+} // namespace arbenv
 
diff --git a/sup/include/sup/with_mpi.hpp b/arborenv/include/arborenv/with_mpi.hpp
similarity index 94%
rename from sup/include/sup/with_mpi.hpp
rename to arborenv/include/arborenv/with_mpi.hpp
index 027b45df..ee9e5f15 100644
--- a/sup/include/sup/with_mpi.hpp
+++ b/arborenv/include/arborenv/with_mpi.hpp
@@ -6,14 +6,14 @@
 
 #include <arbor/communication/mpi_error.hpp>
 
-namespace sup {
+namespace arbenv {
 
 struct with_mpi {
     with_mpi(int& argc, char**& argv, bool fatal_errors = true) {
         init(&argc, &argv, fatal_errors);
     }
 
-    with_mpi(bool fatal_errors = true) {
+    explicit with_mpi(bool fatal_errors = true) {
         init(nullptr, nullptr, fatal_errors);
     }
 
@@ -48,4 +48,4 @@ private:
     }
 };
 
-} // namespace sup
+} // namespace arbenv
diff --git a/sup/private_gpu.cpp b/arborenv/private_gpu.cpp
similarity index 93%
rename from sup/private_gpu.cpp
rename to arborenv/private_gpu.cpp
index 88c36043..f0291571 100644
--- a/sup/private_gpu.cpp
+++ b/arborenv/private_gpu.cpp
@@ -1,24 +1,16 @@
-#ifndef ARB_HAVE_GPU
-
-#include <sup/gpu.hpp>
-#include <mpi.h>
-
-namespace sup {
-// return -1 -> "no gpu" when compiled without GPU support.
-template <>
-int find_private_gpu(MPI_Comm comm) {
-    return -1;
-}
-}
-
-#else
+#ifdef ARB_HAVE_MPI
 
+#include <stdexcept>
 #include <numeric>
+
 #include <mpi.h>
-#include <sup/gpu.hpp>
+
+#include <arborenv/gpu_env.hpp>
 #include "gpu_uuid.hpp"
 
-namespace sup {
+namespace arbenv {
+
+#ifdef ARB_HAVE_GPU
 
 template <>
 int find_private_gpu(MPI_Comm comm) {
@@ -99,7 +91,18 @@ int find_private_gpu(MPI_Comm comm) {
     return gpu.id;
 }
 
-} // namespace sup
+#else
+
+// return -1 -> "no gpu" when compiled without GPU support.
+template <>
+int find_private_gpu(MPI_Comm comm) {
+    return -1;
+}
+
+#endif // def ARB_HAVE_GPU
+
+} // namespace arbenv
+
+#endif // def ARB_HAVE_MPI
 
-#endif
 
diff --git a/cmake/arbor-config.cmake.in b/cmake/arbor-config.cmake.in
index 2d8a3cb6..e5bc0caf 100644
--- a/cmake/arbor-config.cmake.in
+++ b/cmake/arbor-config.cmake.in
@@ -19,16 +19,25 @@ endforeach()
 
 set(_override_lang @arbor_override_import_lang@)
 if(_override_lang)
-    set_target_properties(arbor::arbor PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_@arbor_build_config@ "${_override_lang}")
+    foreach(target arbor::arbor arbor::arborenv)
+        set_target_properties(${target} PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_@arbor_build_config@ "${_override_lang}")
+    endforeach()
 endif()
 
-set(_add_libs @arbor_add_import_libs@)
-if(_add_libs)
-    get_target_property(_arbor_interface_libs arbor::arbor INTERFACE_LINK_LIBRARIES)
-    if(_arbor_interface_libs)
-        list(APPEND _arbor_interface_libs ${_add_libs})
+# Explicitly add extra link libraries not covered by dependencies above.
+# (See though arbor-sim/arbor issue #678).
+
+function(_append_property target property)
+    set(p_append ${ARGN})
+    get_target_property(p ${target} ${property})
+    if(p)
+        list(APPEND p ${p_append})
     else()
-        set(_arbor_interface_libs ${_add_libs})
+        set(interface_libs ${p_append})
     endif()
-    set_target_properties(arbor::arbor PROPERTIES INTERFACE_LINK_LIBRARIES "${_arbor_interface_libs}")
-endif()
+    set_target_properties(${target} PROPERTIES ${property} "${p}")
+endfunction()
+
+_append_property(arbor::arbor INTERFACE_LINK_LIBRARIES @arbor_add_import_libs@)
+_append_property(arbor::arborenv INTERFACE_LINK_LIBRARIES @arborenv_add_import_libs@)
+
diff --git a/example/bench/CMakeLists.txt b/example/bench/CMakeLists.txt
index 5f07e0c7..aa23ea36 100644
--- a/example/bench/CMakeLists.txt
+++ b/example/bench/CMakeLists.txt
@@ -1,4 +1,4 @@
 add_executable(bench EXCLUDE_FROM_ALL bench.cpp recipe.cpp parameters.cpp)
 add_dependencies(examples bench)
 
-target_link_libraries(bench PRIVATE arbor arbor-sup ext-tclap ext-json)
+target_link_libraries(bench PRIVATE arbor arborenv arbor-sup ext-tclap ext-json)
diff --git a/example/bench/bench.cpp b/example/bench/bench.cpp
index 4ab37a5e..af985c66 100644
--- a/example/bench/bench.cpp
+++ b/example/bench/bench.cpp
@@ -18,12 +18,15 @@
 #include <arbor/version.hpp>
 
 
-#include <sup/concurrency.hpp>
-#include <sup/gpu.hpp>
+#include <arborenv/concurrency.hpp>
+#include <arborenv/gpu_env.hpp>
+
 #include <sup/ioutil.hpp>
 #include <sup/json_meter.hpp>
+
 #ifdef ARB_MPI_ENABLED
-#include <sup/with_mpi.hpp>
+#include <mpi.h>
+#include <arborenv/with_mpi.hpp>
 #endif
 
 #include "parameters.hpp"
@@ -36,20 +39,20 @@ int main(int argc, char** argv) {
 
     try {
         arb::proc_allocation resources;
-        if (auto nt = sup::get_env_num_threads()) {
+        if (auto nt = arbenv::get_env_num_threads()) {
             resources.num_threads = nt;
         }
         else {
-            resources.num_threads = sup::thread_concurrency();
+            resources.num_threads = arbenv::thread_concurrency();
         }
 
 #ifdef ARB_MPI_ENABLED
-        sup::with_mpi guard(argc, argv, false);
-        resources.gpu_id = sup::find_private_gpu(MPI_COMM_WORLD);
+        arbenv::with_mpi guard(argc, argv, false);
+        resources.gpu_id = arbenv::find_private_gpu(MPI_COMM_WORLD);
         auto context = arb::make_context(resources, MPI_COMM_WORLD);
         is_root = arb::rank(context) == 0;
 #else
-        resources.gpu_id = sup::default_gpu();
+        resources.gpu_id = arbenv::default_gpu();
         auto context = arb::make_context(resources);
 #endif
 #ifdef ARB_PROFILE_ENABLED
diff --git a/example/brunel/CMakeLists.txt b/example/brunel/CMakeLists.txt
index d94f5f3e..99b26743 100644
--- a/example/brunel/CMakeLists.txt
+++ b/example/brunel/CMakeLists.txt
@@ -3,4 +3,4 @@ add_executable(brunel-miniapp EXCLUDE_FROM_ALL
     io.cpp)
 add_dependencies(examples brunel-miniapp)
 
-target_link_libraries(brunel-miniapp PRIVATE arbor arbor-sup ext-tclap)
+target_link_libraries(brunel-miniapp PRIVATE arbor arborenv arbor-sup ext-tclap)
diff --git a/example/brunel/brunel_miniapp.cpp b/example/brunel/brunel_miniapp.cpp
index b988c6df..2041b7bf 100644
--- a/example/brunel/brunel_miniapp.cpp
+++ b/example/brunel/brunel_miniapp.cpp
@@ -18,16 +18,18 @@
 #include <arbor/simulation.hpp>
 #include <arbor/version.hpp>
 
-#include <sup/concurrency.hpp>
-#include <sup/gpu.hpp>
+#include <arborenv/concurrency.hpp>
+#include <arborenv/gpu_env.hpp>
+
 #include <sup/ioutil.hpp>
 #include <sup/json_meter.hpp>
 #include <sup/path.hpp>
 #include <sup/spike_emitter.hpp>
 #include <sup/strsub.hpp>
+
 #ifdef ARB_MPI_ENABLED
-#include <sup/with_mpi.hpp>
 #include <mpi.h>
+#include <arborenv/with_mpi.hpp>
 #endif
 
 #include "io.hpp"
@@ -189,21 +191,21 @@ int main(int argc, char** argv) {
 
     try {
         arb::proc_allocation resources;
-        if (auto nt = sup::get_env_num_threads()) {
+        if (auto nt = arbenv::get_env_num_threads()) {
             resources.num_threads = nt;
         }
         else {
-            resources.num_threads = sup::thread_concurrency();
+            resources.num_threads = arbenv::thread_concurrency();
         }
 
 #ifdef ARB_MPI_ENABLED
-        sup::with_mpi guard(argc, argv, false);
-        resources.gpu_id = sup::find_private_gpu(MPI_COMM_WORLD);
+        arbenv::with_mpi guard(argc, argv, false);
+        resources.gpu_id = arbenv::find_private_gpu(MPI_COMM_WORLD);
         auto context = arb::make_context(resources, MPI_COMM_WORLD);
         rank = arb::rank(context);
         root = rank==0;
 #else
-        resources.gpu_id = sup::default_gpu();
+        resources.gpu_id = arbenv::default_gpu();
         auto context = arb::make_context(resources);
 #endif
 
diff --git a/example/dryrun/CMakeLists.txt b/example/dryrun/CMakeLists.txt
index 99ed83f0..9d9cb559 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 arbor-sup ext-json)
+target_link_libraries(dryrun PRIVATE arbor arborenv arbor-sup ext-json)
diff --git a/example/dryrun/dryrun.cpp b/example/dryrun/dryrun.cpp
index 0077a91b..92203545 100644
--- a/example/dryrun/dryrun.cpp
+++ b/example/dryrun/dryrun.cpp
@@ -29,7 +29,7 @@
 
 #ifdef ARB_MPI_ENABLED
 #include <mpi.h>
-#include <sup/with_mpi.hpp>
+#include <arborenv/with_mpi.hpp>
 #endif
 
 using arb::cell_gid_type;
@@ -189,7 +189,7 @@ struct cell_stats {
 int main(int argc, char** argv) {
     try {
 #ifdef ARB_MPI_ENABLED
-        sup::with_mpi guard(argc, argv, false);
+        arbenv::with_mpi guard(argc, argv, false);
 #endif
         bool root = true;
         auto params = read_options(argc, argv);
diff --git a/example/miniapp/CMakeLists.txt b/example/miniapp/CMakeLists.txt
index d642cabf..ad78e825 100644
--- a/example/miniapp/CMakeLists.txt
+++ b/example/miniapp/CMakeLists.txt
@@ -7,4 +7,4 @@ add_executable(miniapp EXCLUDE_FROM_ALL
 )
 add_dependencies(examples miniapp)
 
-target_link_libraries(miniapp PRIVATE arbor arbor-sup ext-tclap ext-json)
+target_link_libraries(miniapp PRIVATE arbor arborenv arbor-sup ext-tclap ext-json)
diff --git a/example/miniapp/miniapp.cpp b/example/miniapp/miniapp.cpp
index a85a408c..5fe1cc5f 100644
--- a/example/miniapp/miniapp.cpp
+++ b/example/miniapp/miniapp.cpp
@@ -17,16 +17,18 @@
 #include <arbor/version.hpp>
 
 
-#include <sup/concurrency.hpp>
-#include <sup/gpu.hpp>
+#include <arborenv/concurrency.hpp>
+#include <arborenv/gpu_env.hpp>
+
 #include <sup/ioutil.hpp>
 #include <sup/json_meter.hpp>
 #include <sup/path.hpp>
 #include <sup/spike_emitter.hpp>
 #include <sup/strsub.hpp>
+
 #ifdef ARB_MPI_ENABLED
-#include <sup/with_mpi.hpp>
 #include <mpi.h>
+#include <arborenv/with_mpi.hpp>
 #endif
 
 #include "io.hpp"
@@ -49,20 +51,20 @@ int main(int argc, char** argv) {
 
     try {
         arb::proc_allocation resources;
-        if (auto nt = sup::get_env_num_threads()) {
+        if (auto nt = arbenv::get_env_num_threads()) {
             resources.num_threads = nt;
         }
         else {
-            resources.num_threads = sup::thread_concurrency();
+            resources.num_threads = arbenv::thread_concurrency();
         }
 
 #ifdef ARB_MPI_ENABLED
-        sup::with_mpi guard(argc, argv, false);
-        resources.gpu_id = sup::find_private_gpu(MPI_COMM_WORLD);
+        arbenv::with_mpi guard(argc, argv, false);
+        resources.gpu_id = arbenv::find_private_gpu(MPI_COMM_WORLD);
         auto context = arb::make_context(resources, MPI_COMM_WORLD);
         root = arb::rank(context) == 0;
 #else
-        resources.gpu_id = sup::default_gpu();
+        resources.gpu_id = arbenv::default_gpu();
         auto context = arb::make_context(resources);
 #endif
 
diff --git a/example/ring/CMakeLists.txt b/example/ring/CMakeLists.txt
index 48978586..f35cba80 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 arbor-sup ext-tclap ext-json)
+target_link_libraries(ring PRIVATE arbor arborenv arbor-sup ext-tclap ext-json)
diff --git a/example/ring/ring.cpp b/example/ring/ring.cpp
index 9df416d8..09d6e78b 100644
--- a/example/ring/ring.cpp
+++ b/example/ring/ring.cpp
@@ -21,8 +21,9 @@
 #include <arbor/recipe.hpp>
 #include <arbor/version.hpp>
 
-#include <sup/concurrency.hpp>
-#include <sup/gpu.hpp>
+#include <arborenv/concurrency.hpp>
+#include <arborenv/gpu_env.hpp>
+
 #include <sup/ioutil.hpp>
 #include <sup/json_meter.hpp>
 
@@ -30,7 +31,7 @@
 
 #ifdef ARB_MPI_ENABLED
 #include <mpi.h>
-#include <sup/with_mpi.hpp>
+#include <arborenv/with_mpi.hpp>
 #endif
 
 using arb::cell_gid_type;
@@ -161,20 +162,20 @@ int main(int argc, char** argv) {
         bool root = true;
 
         arb::proc_allocation resources;
-        if (auto nt = sup::get_env_num_threads()) {
+        if (auto nt = arbenv::get_env_num_threads()) {
             resources.num_threads = nt;
         }
         else {
-            resources.num_threads = sup::thread_concurrency();
+            resources.num_threads = arbenv::thread_concurrency();
         }
 
 #ifdef ARB_MPI_ENABLED
-        sup::with_mpi guard(argc, argv, false);
-        resources.gpu_id = sup::find_private_gpu(MPI_COMM_WORLD);
+        arbenv::with_mpi guard(argc, argv, false);
+        resources.gpu_id = arbenv::find_private_gpu(MPI_COMM_WORLD);
         auto context = arb::make_context(resources, MPI_COMM_WORLD);
         root = arb::rank(context) == 0;
 #else
-        resources.gpu_id = sup::default_gpu();
+        resources.gpu_id = arbenv::default_gpu();
         auto context = arb::make_context(resources);
 #endif
 
diff --git a/sup/CMakeLists.txt b/sup/CMakeLists.txt
index 6ea4decf..9e014f07 100644
--- a/sup/CMakeLists.txt
+++ b/sup/CMakeLists.txt
@@ -1,22 +1,11 @@
 set(sup-sources
-    affinity.cpp
-    concurrency.cpp
     glob.cpp
-    default_gpu.cpp
     ioutil.cpp
     json_meter.cpp
     path.cpp
     spike_emitter.cpp
 )
 
-if(ARB_WITH_GPU)
-    list(APPEND sup-sources gpu_uuid.cpp)
-endif()
-
-if(ARB_WITH_MPI)
-    list(APPEND sup-sources private_gpu.cpp)
-endif()
-
 add_library(arbor-sup ${sup-sources})
 
 # Compile sup library with the same optimization flags as libarbor.
@@ -27,26 +16,5 @@ target_link_libraries(arbor-sup PUBLIC ext-json arbor)
 
 target_include_directories(arbor-sup PUBLIC include)
 
-if(ARB_WITH_MPI)
-    target_compile_definitions(arbor-sup PRIVATE ARB_HAVE_MPI)
-endif()
-if(ARB_WITH_GPU)
-    target_compile_definitions(arbor-sup PRIVATE ARB_HAVE_GPU)
-    # So that cpp and hpp files can use cuda.h and cuda_runtime.h
-    target_include_directories(arbor-sup PRIVATE ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES})
-
-    # The sup lib needs to use the CUDA NVML library for CUDA 9
-    if (${CUDA_VERSION_MAJOR} LESS 10)
-        find_library(nvml_lib_path
-                     NAMES nvidia-ml
-                     PATHS ${CUDA_TOOLKIT_ROOT_DIR}
-                     PATH_SUFFIXES lib64/stubs)
-        if (NOT nvml_lib_path)
-            message(FATAL_ERROR "Unable to find CUDA NVML library: libnvida-ml.so")
-        endif()
-        target_link_libraries(arbor-sup PUBLIC ${nvml_lib_path})
-    endif()
-endif()
-
 set_target_properties(arbor-sup PROPERTIES OUTPUT_NAME arborsup)
 
diff --git a/sup/glob.cpp b/sup/glob.cpp
index 185cfc92..ecec061e 100644
--- a/sup/glob.cpp
+++ b/sup/glob.cpp
@@ -16,8 +16,10 @@ extern "C" {
 
 #include <cerrno>
 
+#include <arbor/util/scope_exit.hpp>
 #include <sup/path.hpp>
-#include <sup/scope_exit.hpp>
+
+using arb::util::on_scope_exit;
 
 namespace sup {
 
diff --git a/sup/include/sup/affinity.hpp b/sup/include/sup/affinity.hpp
deleted file mode 100644
index 49707c86..00000000
--- a/sup/include/sup/affinity.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#pragma once
-
-#include <cstdint>
-#include <vector>
-
-namespace sup {
-
-// The list of logical processors for which the calling thread has affinity.
-// If calling from the main thread at application start up, before
-// attempting to change thread affinity, may produce unreliable
-// results.
-//  - beware thread pinning or custom job scheduler affinity
-//    flags that assign threads to specific cores.
-//
-// Returns an empty vector if unable to determine the number of
-// available cores.
-std::vector<int> get_affinity();
-
-} // namespace sup
diff --git a/test/unit-distributed/CMakeLists.txt b/test/unit-distributed/CMakeLists.txt
index 6b84f561..d93748c9 100644
--- a/test/unit-distributed/CMakeLists.txt
+++ b/test/unit-distributed/CMakeLists.txt
@@ -13,7 +13,7 @@ add_dependencies(tests unit-local)
 
 target_compile_options(unit-local PRIVATE ${ARB_CXXOPT_ARCH})
 target_compile_definitions(unit-local PRIVATE TEST_LOCAL)
-target_link_libraries(unit-local PRIVATE gtest arbor arbor-sup arbor-private-headers)
+target_link_libraries(unit-local PRIVATE gtest arbor arborenv arbor-sup arbor-private-headers)
 
 if(ARB_WITH_MPI)
     add_executable(unit-mpi EXCLUDE_FROM_ALL ${unit-distributed_sources})
@@ -21,6 +21,6 @@ if(ARB_WITH_MPI)
 
     target_compile_options(unit-mpi PRIVATE ${ARB_CXXOPT_ARCH})
     target_compile_definitions(unit-mpi PRIVATE TEST_MPI)
-    target_link_libraries(unit-mpi PRIVATE gtest arbor arbor-sup arbor-private-headers)
+    target_link_libraries(unit-mpi PRIVATE gtest arbor arborenv arbor-sup arbor-private-headers)
 endif()
 
diff --git a/test/unit-distributed/test.cpp b/test/unit-distributed/test.cpp
index 6dbbd018..cc5a52d4 100644
--- a/test/unit-distributed/test.cpp
+++ b/test/unit-distributed/test.cpp
@@ -9,8 +9,9 @@
 
 #include <sup/ioutil.hpp>
 #include <sup/tinyopt.hpp>
+
 #ifdef TEST_MPI
-#include <sup/with_mpi.hpp>
+#include <arborenv/with_mpi.hpp>
 #endif
 
 #include "distributed_context.hpp"
@@ -35,7 +36,7 @@ int main(int argc, char **argv) {
     alloc.gpu_id = -1;
 
 #ifdef TEST_MPI
-    sup::with_mpi guard(argc, argv, false);
+    arbenv::with_mpi guard(argc, argv, false);
     g_context = arb::make_context(alloc, MPI_COMM_WORLD);
 #elif defined(TEST_LOCAL)
     g_context = arb::make_context(alloc);
diff --git a/test/unit/test_scope_exit.cpp b/test/unit/test_scope_exit.cpp
index 12b8c140..7f1890f0 100644
--- a/test/unit/test_scope_exit.cpp
+++ b/test/unit/test_scope_exit.cpp
@@ -2,9 +2,9 @@
 
 #include "../gtest.h"
 
-#include <sup/scope_exit.hpp>
+#include <arbor/util/scope_exit.hpp>
 
-using sup::on_scope_exit;
+using arb::util::on_scope_exit;
 
 TEST(scope_exit, basic) {
     bool a = false;
-- 
GitLab