Refactor Ceres threading option configuration.
- Previously we had separate variables for each of the threading
backends, each of which were made mutually exclusive via
cmake_dependent_option(). This has unfortunate side-effects when
trying to disable options if they are not currently enabled, in which
case they are not defined.
- As all the threading options are mutually exclusive, this replaces
all threading option variables with a single variable: CERES_THREADS,
which is constrained to take the value of only the available threading
backends.
Change-Id: I0822eefbac9a30772907b7732add365b37cc8ca0
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f1f59ff..1ffcb2b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -115,7 +115,22 @@
enable_testing()
-include(CMakeDependentOption)
+include(CeresThreadingModels)
+include(PrettyPrintCMakeList)
+find_available_ceres_threading_models(CERES_THREADING_MODELS_AVAILABLE)
+pretty_print_cmake_list(PRETTY_CERES_THREADING_MODELS_AVAILABLE
+ ${CERES_THREADING_MODELS_AVAILABLE})
+message("-- Detected available Ceres threading models: "
+ "${PRETTY_CERES_THREADING_MODELS_AVAILABLE}")
+set(CERES_THREADING_MODEL "${CERES_THREADING_MODEL}" CACHE STRING
+ "Ceres threading back-end" FORCE)
+if (NOT CERES_THREADING_MODEL)
+ list(GET CERES_THREADING_MODELS_AVAILABLE 0 DEFAULT_THREADING_MODEL)
+ update_cache_variable(CERES_THREADING_MODEL ${DEFAULT_THREADING_MODEL})
+endif()
+set_property(CACHE CERES_THREADING_MODEL PROPERTY STRINGS
+ ${CERES_THREADING_MODELS_AVAILABLE})
+
option(MINIGLOG "Use a stripped down version of glog." OFF)
option(GFLAGS "Enable Google Flags." ON)
option(SUITESPARSE "Enable SuiteSparse." ON)
@@ -128,18 +143,6 @@
option(CUSTOM_BLAS
"Use handcoded BLAS routines (usually faster) instead of Eigen."
ON)
-# Multithreading using OpenMP
-cmake_dependent_option(
- OPENMP "Enable threaded solving in Ceres (requires OpenMP)" ON
- "NOT TBB;NOT CXX11_THREADS" OFF)
-# Multithreading using TBB
-cmake_dependent_option(
- TBB "Enable threaded solving in Ceres with TBB (requires TBB and C++11)" OFF
- "NOT OPENMP;NOT CXX11_THREADS" OFF)
-# Multithreading using C++11 primitives.
-cmake_dependent_option(
- CXX11_THREADS "Enable threaded solving in Ceres with C++11 primitives" OFF
- "NOT OPENMP;NOT TBB" OFF)
# Enable the use of Eigen as a sparse linear algebra library for
# solving the nonlinear least squares problems.
option(EIGENSPARSE "Enable Eigen as a sparse linear algebra library." ON)
@@ -412,46 +415,7 @@
message("-- Disabling custom blas")
endif (NOT CUSTOM_BLAS)
-if (OPENMP)
- # Find quietly, as we can continue without OpenMP if it is not found.
- find_package(OpenMP QUIET)
- if (OPENMP_FOUND)
- message("-- Building with OpenMP.")
- list(APPEND CERES_COMPILE_OPTIONS CERES_USE_OPENMP)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
- else (OPENMP_FOUND)
- message("-- Failed to find OpenMP, disabling. This is expected on "
- "Clang < 3.8, and at least Xcode <= 8. See Ceres documentation for "
- "instructions to build with LLVM from Homebrew to enable OpenMP on OS X.")
- update_cache_variable(OPENMP OFF)
- endif (OPENMP_FOUND)
-else (OPENMP)
- message("-- Building without OpenMP, disabling.")
-endif (OPENMP)
-
-if (TBB)
- find_package(TBB QUIET)
- if (TBB_FOUND)
- message("-- Building with TBB (version: ${TBB_VERSION}).")
- list(APPEND CERES_COMPILE_OPTIONS CERES_USE_TBB)
- include_directories(${TBB_INCLUDE_DIRS})
- else (TBB_FOUND)
- message("-- Failed to find TBB, disabling.")
- update_cache_variable(TBB OFF)
- endif (TBB_FOUND)
-endif()
-
-if (CXX11_THREADS)
- message("-- Building with C++11 threads.")
- list(APPEND CERES_COMPILE_OPTIONS CERES_USE_CXX11_THREADS)
-endif()
-
-if (NOT OPENMP AND NOT TBB AND NOT CXX11_THREADS)
- message("-- Neither OpenMP, TBB or C++11 threads is enabled, "
- "disabling multithreading.")
- list(APPEND CERES_COMPILE_OPTIONS CERES_NO_THREADS)
-endif (NOT OPENMP AND NOT TBB AND NOT CXX11_THREADS)
+set_ceres_threading_model("${CERES_THREADING_MODEL}")
if (BUILD_BENCHMARKS)
find_package(benchmark QUIET)
diff --git a/cmake/CeresThreadingModels.cmake b/cmake/CeresThreadingModels.cmake
new file mode 100644
index 0000000..910f4a8
--- /dev/null
+++ b/cmake/CeresThreadingModels.cmake
@@ -0,0 +1,94 @@
+# Ceres Solver - A fast non-linear least squares minimizer
+# Copyright 2018 Google Inc. All rights reserved.
+# http://ceres-solver.org/
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Google Inc. nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# Author: alexs.mac@gmail.com (Alex Stewart)
+
+# Ordered by expected preference.
+set(CERES_THREADING_MODELS "CXX11_THREADS;OPENMP;TBB;NO_THREADS")
+
+function(find_available_ceres_threading_models CERES_THREADING_MODELS_AVAILABLE_VAR)
+ set(CERES_THREADING_MODELS_AVAILABLE ${CERES_THREADING_MODELS})
+ # Remove any threading models for which the dependencies are not available.
+ find_package(OpenMP QUIET)
+ if (NOT OPENMP_FOUND)
+ list(REMOVE_ITEM CERES_THREADING_MODELS_AVAILABLE "OPENMP")
+ endif()
+ find_package(TBB QUIET)
+ if (NOT TBB_FOUND)
+ list(REMOVE_ITEM CERES_THREADING_MODELS_AVAILABLE "TBB")
+ endif()
+ if (NOT CERES_THREADING_MODELS_AVAILABLE)
+ # At least NO_THREADS should never be removed. This check is purely
+ # protective against future threading model updates.
+ message(FATAL_ERROR "Ceres bug: Removed all threading models.")
+ endif()
+ set(${CERES_THREADING_MODELS_AVAILABLE_VAR}
+ ${CERES_THREADING_MODELS_AVAILABLE} PARENT_SCOPE)
+endfunction()
+
+macro(set_ceres_threading_model_to_cxx11_threads)
+ list(APPEND CERES_COMPILE_OPTIONS CERES_USE_CXX11_THREADS)
+endmacro()
+
+macro(set_ceres_threading_model_to_openmp)
+ find_package(OpenMP REQUIRED)
+ list(APPEND CERES_COMPILE_OPTIONS CERES_USE_OPENMP)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
+endmacro()
+
+macro(set_ceres_threading_model_to_tbb)
+ find_package(TBB REQUIRED)
+ list(APPEND CERES_COMPILE_OPTIONS CERES_USE_TBB)
+ include_directories(${TBB_INCLUDE_DIRS})
+endmacro()
+
+macro(set_ceres_threading_model_to_no_threads)
+ list(APPEND CERES_COMPILE_OPTIONS CERES_NO_THREADS)
+endmacro()
+
+macro(set_ceres_threading_model CERES_THREADING_MODEL_TO_SET)
+ if ("${CERES_THREADING_MODEL_TO_SET}" STREQUAL "CXX11_THREADS")
+ set_ceres_threading_model_to_cxx11_threads()
+ elseif ("${CERES_THREADING_MODEL_TO_SET}" STREQUAL "OPENMP")
+ set_ceres_threading_model_to_openmp()
+ elseif ("${CERES_THREADING_MODEL_TO_SET}" STREQUAL "TBB")
+ set_ceres_threading_model_to_tbb()
+ elseif ("${CERES_THREADING_MODEL_TO_SET}" STREQUAL "NO_THREADS")
+ set_ceres_threading_model_to_no_threads()
+ else()
+ include(PrettyPrintCMakeList)
+ find_available_ceres_threading_models(_AVAILABLE_THREADING_MODELS)
+ pretty_print_cmake_list(
+ _AVAILABLE_THREADING_MODELS ${_AVAILABLE_THREADING_MODELS})
+ message(FATAL_ERROR "Unknown threading model specified: "
+ "'${CERES_THREADING_MODEL_TO_SET}'. Available threading models for "
+ "this platform are: ${_AVAILABLE_THREADING_MODELS}")
+ endif()
+ message("-- Using Ceres threading model: ${CERES_THREADING_MODEL_TO_SET}")
+endmacro()
diff --git a/cmake/PrettyPrintCMakeList.cmake b/cmake/PrettyPrintCMakeList.cmake
new file mode 100644
index 0000000..067883c
--- /dev/null
+++ b/cmake/PrettyPrintCMakeList.cmake
@@ -0,0 +1,39 @@
+# Ceres Solver - A fast non-linear least squares minimizer
+# Copyright 2018 Google Inc. All rights reserved.
+# http://ceres-solver.org/
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Google Inc. nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# Authors: alexs.mac@gmail.com (Alex Stewart)
+
+# pretty_print_cmake_list( OUTPUT_VAR [item1 [item2 ... ]] )
+#
+# Sets ${OUTPUT_VAR} in the caller's scope to a human-readable string
+# representation of the list passed as the remaining arguments formed
+# as: "[item1, item2, ..., itemN]".
+function(pretty_print_cmake_list OUTPUT_VAR)
+ string(REPLACE ";" ", " PRETTY_LIST_STRING "[${ARGN}]")
+ set(${OUTPUT_VAR} "${PRETTY_LIST_STRING}" PARENT_SCOPE)
+endfunction()
diff --git a/docs/source/installation.rst b/docs/source/installation.rst
index e7951bf..b80dc25 100644
--- a/docs/source/installation.rst
+++ b/docs/source/installation.rst
@@ -664,13 +664,10 @@
gains in the ``SPARSE_SCHUR`` solver, you can disable some of the
template specializations by turning this ``OFF``.
-#. ``OPENMP [Default: ON]``: On certain platforms like Android,
- multi-threading with ``OpenMP`` is not supported. Turn this ``OFF``
- to disable multi-threading.
-
-#. ``TBB [Default: OFF]``: An alternative to ``OpenMP`` threading library that
- uses Intel's Thread Building Blocks. This option is mutually
- exclusive to ``OPENMP`` and ``CXX11_THREADS``.
+#. ``CERES_THREADING_MODEL [Default: CXX11_THREADS > OPENMP > TBB > NO_THREADS]``:
+ Multi-threading backend Ceres should be compiled with. This will
+ automatically be set to only accept the available subset of threading
+ options in the CMake GUI.
.. NOTE::
@@ -678,10 +675,6 @@
GPL/Commercial terms. From 2017.x versions onwards, TBB is licensed under
the Apache 2.0 license (and commerical terms).
-#. ``CXX11_THREADS [Default: OFF]``: An alternative to ``OpenMP``
- threading library that uses a C++11 thread-pool. This option
- is mutually exclusive to ``OPENMP`` and ``TBB``.
-
#. ``BUILD_SHARED_LIBS [Default: OFF]``: By default Ceres is built as
a static library, turn this ``ON`` to instead build Ceres as a
shared library.
@@ -859,12 +852,13 @@
#. ``SchurSpecializations``: Ceres built with Schur specializations
(``SCHUR_SPECIALIZATIONS=ON``).
-#. ``OpenMP``: Ceres built with OpenMP (``OPENMP=ON``).
+#. ``OpenMP``: Ceres built with OpenMP (``CERES_THREADING_MODEL=OPENMP``).
-#. ``TBB``: Ceres built with Intel Thread Building Blocks (TBB) (``TBB=ON``).
+#. ``TBB``: Ceres built with Intel Thread Building Blocks (TBB)
+ (``CERES_THREADING_MODEL=TBB``).
#. ``Multithreading``: Ceres built with *a* multithreading library.
- This is equivalent to ``OpenMP`` **OR** ``TBB``.
+ This is equivalent to (``CERES_THREAD != NO_THREADS``).
#. ``C++11``: Ceres built with C++11.
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt
index 0532073..71996b6 100644
--- a/internal/ceres/CMakeLists.txt
+++ b/internal/ceres/CMakeLists.txt
@@ -30,13 +30,27 @@
# Avoid 'xxx.cc has no symbols' warnings from source files which are 'empty'
# when their enclosing #ifdefs are disabled.
-if (CXX11_THREADS)
+find_package(Threads QUIET)
+if (CERES_THREADING_MODEL STREQUAL "CXX11_THREADS")
set(CERES_PARALLEL_FOR_SRC parallel_for_cxx.cc thread_pool.cc)
-elseif (OPENMP)
+elseif (CERES_THREADING_MODEL STREQUAL "OPENMP")
set(CERES_PARALLEL_FOR_SRC parallel_for_openmp.cc)
-elseif (TBB)
+ if (CMAKE_COMPILER_IS_GNUCXX)
+ # OpenMP in GCC requires the GNU OpenMP library.
+ list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES gomp)
+ endif()
+ if (NOT MSVC)
+ # Add thread library dependencies for OpenMP to the Ceres target.
+ list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES ${CMAKE_THREAD_LIBS_INIT})
+ endif()
+elseif (CERES_THREADING_MODEL STREQUAL "TBB")
set(CERES_PARALLEL_FOR_SRC parallel_for_tbb.cc)
-else()
+ list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES ${TBB_LIBRARIES})
+ if (NOT MSVC)
+ # Add thread library dependencies for OpenMP to the Ceres target.
+ list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES ${CMAKE_THREAD_LIBS_INIT})
+ endif (NOT MSVC)
+elseif (CERES_THREADING_MODEL STREQUAL "NO_THREADS")
set(CERES_PARALLEL_FOR_SRC parallel_for_nothreads.cc)
endif()
@@ -193,23 +207,6 @@
list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES ${LAPACK_LIBRARIES})
endif ()
-if (OPENMP_FOUND)
- # OpenMP support in Clang requires a non-GNU OpenMP library.
- if (CMAKE_COMPILER_IS_GNUCXX)
- list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES gomp)
- endif()
- if (NOT MSVC)
- list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES ${CMAKE_THREAD_LIBS_INIT})
- endif()
-endif (OPENMP_FOUND)
-
-if (TBB_FOUND)
- list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES ${TBB_LIBRARIES})
- if (NOT MSVC)
- list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES ${CMAKE_THREAD_LIBS_INIT})
- endif (NOT MSVC)
-endif (TBB_FOUND)
-
set(CERES_LIBRARY_SOURCE
${CERES_INTERNAL_SRC}
${CERES_INTERNAL_HDRS}