Replace include_directories() with target_include_directories().

- Now that we require CMake >= 3.x, we can roll the definition of the
  include directories for clients linking against Ceres into the
  exported Ceres CMake target via target_include_directories().
- This removes the requirement for CERES_INCLUDE_DIRS.

Change-Id: Ibe3bbf796339871d66138fc9520053d1766f09a8
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a1fe27f..e37384d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -472,39 +472,6 @@
   mark_as_advanced(benchmark_DIR)
 endif()
 
-include_directories(
-  include
-  internal
-  internal/ceres
-  ${GLOG_INCLUDE_DIRS})
-
-# Eigen SparseQR generates various compiler warnings related to unused and
-# uninitialised local variables.  To avoid having to individually suppress these
-# warnings around the #include statments for Eigen headers across all GCC/Clang
-# versions, we tell CMake to treat Eigen headers as system headers.  This
-# results in all compiler warnings from them being suppressed.
-#
-# Note that this is *not* propagated to clients, ie CERES_INCLUDE_DIRS
-# used by clients after find_package(Ceres) does not identify Eigen as
-# as system headers.
-include_directories(SYSTEM ${EIGEN_INCLUDE_DIRS})
-
-if (SUITESPARSE)
-  include_directories(${SUITESPARSE_INCLUDE_DIRS})
-endif (SUITESPARSE)
-
-if (CXSPARSE)
-  include_directories(${CXSPARSE_INCLUDE_DIRS})
-endif (CXSPARSE)
-
-if (ACCELERATESPARSE)
-  include_directories(${AccelerateSparse_INCLUDE_DIRS})
-endif()
-
-if (GFLAGS)
-  include_directories(${GFLAGS_INCLUDE_DIRS})
-endif (GFLAGS)
-
 if (BUILD_SHARED_LIBS)
   message("-- Building Ceres as a shared library.")
   # The CERES_BUILDING_SHARED_LIBRARY compile definition is NOT stored in
@@ -651,12 +618,6 @@
 include(CreateCeresConfig)
 create_ceres_config("${CERES_COMPILE_OPTIONS}"
   ${Ceres_BINARY_DIR}/config/ceres/internal)
-# Force the location containing the configured config.h to the front of the
-# include_directories list (by default it is appended to the back) to ensure
-# that if the user has an installed version of Ceres in the same location as one
-# of the dependencies (e.g. /usr/local) that we find the config.h we just
-# configured, not the (older) installed config.h.
-include_directories(BEFORE ${Ceres_BINARY_DIR}/config)
 
 add_subdirectory(internal/ceres)
 
diff --git a/cmake/CeresConfig.cmake.in b/cmake/CeresConfig.cmake.in
index 957b99d..009e32f 100644
--- a/cmake/CeresConfig.cmake.in
+++ b/cmake/CeresConfig.cmake.in
@@ -50,21 +50,15 @@
 #
 # CERES_VERSION: Version of Ceres found.
 #
-# CERES_INCLUDE_DIRS: Include directories for Ceres and the
-#                     dependencies which appear in the Ceres public
-#                     API and are thus required to use Ceres.
-#
 # CERES_LIBRARIES: Libraries for Ceres and all
 #                  dependencies against which Ceres was
 #                  compiled. This will not include any optional
 #                  dependencies that were disabled when Ceres was
 #                  compiled.
 #
-# The following variables are also defined for legacy compatibility
-# only.  Any new code should not use them as they do not conform to
-# the standard CMake FindPackage naming conventions.
-#
-# CERES_INCLUDES = ${CERES_INCLUDE_DIRS}.
+# NOTE: There is no equivalent of CERES_INCLUDE_DIRS as the exported
+#       CMake target already includes the definition of its public
+#       include directories.
 
 # Called if we failed to find Ceres or any of its required dependencies,
 # unsets all public (designed to be used externally) variables and reports
@@ -74,6 +68,7 @@
   # explicitly set FALSE to denote not found (not merely undefined).
   set(Ceres_FOUND FALSE)
   set(CERES_FOUND FALSE)
+  unset(CERES_INCLUDE_DIR)
   unset(CERES_INCLUDE_DIRS)
   unset(CERES_LIBRARIES)
 
@@ -143,18 +138,6 @@
       "outside of CMake after Ceres was built.")
   endif (NOT EXISTS ${CURRENT_ROOT_INSTALL_DIR})
 
-  # Set the include directories for Ceres (itself).
-  set(CERES_INCLUDE_DIR "${CURRENT_ROOT_INSTALL_DIR}/include")
-  if (NOT EXISTS ${CERES_INCLUDE_DIR}/ceres/ceres.h)
-    ceres_report_not_found(
-      "Ceres install root: ${CURRENT_ROOT_INSTALL_DIR}, "
-      "determined from relative path from CeresConfig.cmake install location: "
-      "${CERES_CURRENT_CONFIG_DIR}, does not contain Ceres headers. "
-      "Either the install directory was deleted, or the install tree was only "
-      "partially relocated outside of CMake after Ceres was built.")
-  endif (NOT EXISTS ${CERES_INCLUDE_DIR}/ceres/ceres.h)
-  list(APPEND CERES_INCLUDE_DIRS ${CERES_INCLUDE_DIR})
-
 else(CERES_WAS_INSTALLED)
   # Ceres was exported from the build tree.
   set(CERES_EXPORTED_BUILD_DIR ${CERES_CURRENT_CONFIG_DIR})
@@ -173,28 +156,6 @@
   # with Ceres to find Ceres' dependencies, even if the user has equivalently
   # named FindPackage() scripts in their project.
   set(CMAKE_MODULE_PATH ${CERES_EXPORTED_SOURCE_DIR}/cmake)
-
-  # Set the include directories for Ceres (itself).
-  set(CERES_INCLUDE_DIR "${CERES_EXPORTED_SOURCE_DIR}/include")
-  if (NOT EXISTS ${CERES_INCLUDE_DIR}/ceres/ceres.h)
-    ceres_report_not_found(
-      "Ceres exported source directory: ${CERES_EXPORTED_SOURCE_DIR}, "
-      "determined from relative path from CeresConfig.cmake exported build "
-      "directory: ${CERES_EXPORTED_BUILD_DIR}, does not contain Ceres headers.")
-  endif (NOT EXISTS ${CERES_INCLUDE_DIR}/ceres/ceres.h)
-  list(APPEND CERES_INCLUDE_DIRS ${CERES_INCLUDE_DIR})
-
-  # Append the path to the configured config.h in the exported build directory
-  # to the Ceres include directories.
-  set(CERES_CONFIG_FILE
-    ${CERES_EXPORTED_BUILD_DIR}/config/ceres/internal/config.h)
-  if (NOT EXISTS ${CERES_CONFIG_FILE})
-    ceres_report_not_found(
-      "Ceres exported build directory: ${CERES_EXPORTED_BUILD_DIR}, "
-      "does not contain required configured Ceres config.h, it is not here: "
-      "${CERES_CONFIG_FILE}.")
-  endif (NOT EXISTS ${CERES_CONFIG_FILE})
-  list(APPEND CERES_INCLUDE_DIRS ${CERES_EXPORTED_BUILD_DIR}/config)
 endif(CERES_WAS_INSTALLED)
 
 # Set the version.
@@ -234,33 +195,24 @@
     "dependency: Eigen version ${CERES_EIGEN_VERSION}, please set "
     "EIGEN_INCLUDE_DIR.")
 endif (EIGEN_FOUND)
-list(APPEND CERES_INCLUDE_DIRS ${EIGEN_INCLUDE_DIRS})
 
 # Glog.
 # Flag set during configuration and build of Ceres.
 set(CERES_USES_MINIGLOG @MINIGLOG@)
 set(CERES_USES_GFLAGS @GFLAGS@)
 if (CERES_USES_MINIGLOG)
-  set(MINIGLOG_INCLUDE_DIR ${CERES_INCLUDE_DIR}/ceres/internal/miniglog)
-  if (NOT CERES_WAS_INSTALLED)
-    # When Ceres was exported from the build tree, the miniglog headers
-    # will be in Ceres internal source directory, not in the public headers
-    # directory (they are copied with the public headers when installed).
-    set(MINIGLOG_INCLUDE_DIR
-      ${CERES_EXPORTED_SOURCE_DIR}/internal/ceres/miniglog)
-  endif()
-  if (NOT EXISTS ${MINIGLOG_INCLUDE_DIR})
-    ceres_report_not_found(
-      "Failed to find miniglog headers in expected include directory: "
-      "${MINIGLOG_INCLUDE_DIR}, but Ceres was compiled with MINIGLOG enabled "
-      "(in place of glog).")
-  endif (NOT EXISTS ${MINIGLOG_INCLUDE_DIR})
-  list(APPEND CERES_INCLUDE_DIRS ${MINIGLOG_INCLUDE_DIR})
   # Output message at standard log level (not the lower STATUS) so that
   # the message is output in GUI during configuration to warn user.
   message("-- Found Ceres compiled with miniglog substitute "
     "for glog, beware this will likely cause problems if glog is later linked.")
-else (CERES_USES_MINIGLOG)
+else(CERES_USES_MINIGLOG)
+  # As imported CMake targets are not re-exported when a dependent target is
+  # exported, we must invoke find_package(XXX) here to reload the definition
+  # of their targets.  Without this, the dependency target names (e.g.
+  # 'gflags-shared') which will be present in the ceres target would not be
+  # defined, and so CMake will assume that they refer to a library name and
+  # fail to link correctly.
+
   # Append the locations of glog when Ceres was built to the search path hints.
   set(GLOG_WAS_BUILT_WITH_CMAKE @FOUND_INSTALLED_GLOG_CMAKE_CONFIGURATION@)
   if (GLOG_WAS_BUILT_WITH_CMAKE)
@@ -271,29 +223,19 @@
     get_filename_component(CERES_BUILD_GLOG_LIBRARY_DIR @GLOG_LIBRARY@ PATH)
     list(APPEND GLOG_LIBRARY_DIR_HINTS ${CERES_BUILD_GLOG_LIBRARY_DIR})
   endif()
-
   # Search quietly s/t we control the timing of the error message if not found.
   find_package(Glog QUIET)
   if (GLOG_FOUND)
     message(STATUS "Found required Ceres dependency: glog")
-  else (GLOG_FOUND)
+  else()
     ceres_report_not_found("Missing required Ceres "
       "dependency: glog. Searched using GLOG_INCLUDE_DIR_HINTS: "
       "${GLOG_INCLUDE_DIR_HINTS} and glog_DIR: ${glog_DIR}.")
-  endif (GLOG_FOUND)
-  list(APPEND CERES_INCLUDE_DIRS ${GLOG_INCLUDE_DIRS})
+  endif()
 
   # gflags is only a public dependency of Ceres via glog, thus is not required
   # if Ceres was built with MINIGLOG.
   if (CERES_USES_GFLAGS)
-    # If gflags was found as an imported CMake target, we need to call
-    # find_packge(Gflags) again here, as imported CMake targets are not
-    # re-exported.  Without this, the 'gflags-shared' target name which is
-    # present in CERES_LIBRARIES in this case would not be defined, and so
-    # CMake will assume it is a library name (which it is not) and fail to link.
-    #
-    # Append the locations of gflags when Ceres was built to the search path
-    # hints.
     set(GFLAGS_WAS_BUILT_WITH_CMAKE @FOUND_INSTALLED_GFLAGS_CMAKE_CONFIGURATION@)
     if (GFLAGS_WAS_BUILT_WITH_CMAKE)
       set(gflags_DIR @gflags_DIR@)
@@ -303,7 +245,6 @@
       get_filename_component(CERES_BUILD_GFLAGS_LIBRARY_DIR @GFLAGS_LIBRARY@ PATH)
       list(APPEND GFLAGS_LIBRARY_DIR_HINTS ${CERES_BUILD_GFLAGS_LIBRARY_DIR})
     endif()
-
     # Search quietly s/t we control the timing of the error message if not found.
     find_package(Gflags QUIET)
     if (GFLAGS_FOUND)
@@ -313,9 +254,8 @@
         "dependency: gflags. Searched using GFLAGS_INCLUDE_DIR_HINTS: "
         "${GFLAGS_INCLUDE_DIR_HINTS} and gflags_DIR: ${gflags_DIR}.")
     endif()
-    list(APPEND CERES_INCLUDE_DIRS ${GFLAGS_INCLUDE_DIRS})
   endif()
-endif (CERES_USES_MINIGLOG)
+endif(CERES_USES_MINIGLOG)
 
 # Import exported Ceres targets, if they have not already been imported.
 if (NOT TARGET ceres AND NOT Ceres_BINARY_DIR)
@@ -324,9 +264,6 @@
 # Set the expected XX_LIBRARIES variable for FindPackage().
 set(CERES_LIBRARIES ceres)
 
-# Set legacy include directories variable for backwards compatibility.
-set(CERES_INCLUDES ${CERES_INCLUDE_DIRS})
-
 # Reset CMake module path to its state when this script was called.
 set(CMAKE_MODULE_PATH ${CALLERS_CMAKE_MODULE_PATH})
 
diff --git a/cmake/uninstall.cmake.in b/cmake/uninstall.cmake.in
index 06cac80..124deb1 100644
--- a/cmake/uninstall.cmake.in
+++ b/cmake/uninstall.cmake.in
@@ -29,11 +29,6 @@
 # Author: arnaudgelas@gmail.com (Arnaud Gelas)
 #         alexs.mac@gmail.com (Alex Stewart)
 
-if (COMMAND cmake_policy)
-  # Ignore empty elements in LIST() commands.
-  cmake_policy(SET CMP0007 OLD)
-endif (COMMAND cmake_policy)
-
 if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
   message(FATAL_ERROR "Cannot find install manifest: "
                       "\"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"")
diff --git a/docs/source/installation.rst b/docs/source/installation.rst
index 00158cf..24ccc0c 100644
--- a/docs/source/installation.rst
+++ b/docs/source/installation.rst
@@ -867,12 +867,11 @@
 
 .. code-block:: cmake
 
-    cmake_minimum_required(VERSION 2.8)
+    cmake_minimum_required(VERSION 3.5)
 
     project(helloworld)
 
     find_package(Ceres REQUIRED)
-    include_directories(${CERES_INCLUDE_DIRS})
 
     # helloworld
     add_executable(helloworld helloworld.cc)
@@ -886,6 +885,13 @@
 ``Ceres_DIR`` should be the path to the exported Ceres build
 directory.
 
+  .. NOTE ::
+
+     You do not need to call include_directories(${CERES_INCLUDE_DIRS})
+     as the exported Ceres CMake target already contains the definitions
+     of its public include directories which will be automatically
+     included by CMake when compiling a target that links against Ceres.
+
 Specify Ceres components
 -------------------------------------
 
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt
index 38ba9d4..a6999d8 100644
--- a/internal/ceres/CMakeLists.txt
+++ b/internal/ceres/CMakeLists.txt
@@ -271,6 +271,80 @@
   target_link_libraries(ceres ${CERES_LIBRARY_DEPENDENCIES})
 endif (BUILD_SHARED_LIBS)
 
+# Add the Ceres headers to its target.
+#
+# Force the location containing the configured config.h to the front of the
+# include_directories list (by default it is appended to the back) to ensure
+# that if the user has an installed version of Ceres in the same location as one
+# of the dependencies (e.g. /usr/local) that we find the config.h we just
+# configured, not the (older) installed config.h.
+target_include_directories(ceres BEFORE PUBLIC
+  $<BUILD_INTERFACE:${Ceres_BINARY_DIR}/config>)
+target_include_directories(ceres PRIVATE ${Ceres_SOURCE_DIR}/internal)
+target_include_directories(ceres PUBLIC
+  $<BUILD_INTERFACE:${Ceres_SOURCE_DIR}/include>
+  $<INSTALL_INTERFACE:include>)
+
+# Eigen SparseQR generates various compiler warnings related to unused and
+# uninitialised local variables.  To avoid having to individually suppress these
+# warnings around the #include statments for Eigen headers across all GCC/Clang
+# versions, we tell CMake to treat Eigen headers as system headers.  This
+# results in all compiler warnings from them being suppressed.
+target_include_directories(ceres SYSTEM PUBLIC ${EIGEN_INCLUDE_DIRS})
+
+# Gather the list of public & private include locations for all enabled optional
+# dependencies to be added to the Ceres target.
+set(CERES_LIBRARY_PRIVATE_DEPENDENCIES_INCLUDE_DIRS "")
+set(CERES_LIBRARY_PUBLIC_DEPENDENCIES_INCLUDE_DIRS "")
+if (MINIGLOG)
+  # Force the miniglog headers to the front of the public include directories
+  # to protect against the case when the user has glog installed in a standard
+  # location (specifically the same as the Ceres install location) but compiled
+  # Ceres with MINIGLOG anyway.  Otherwise: "glog/logging.h" in the public Ceres
+  # headers used in client code would match the installed version of glog, not
+  # the miniglog headers, and the client application would fail to link.
+  #
+  # Note that this is an imperfect fix, as we cannot control the include
+  # directories in client projects, and they could easily invert this ordering
+  # themselves (intentionally or otherwise) and so break their build.
+  target_include_directories(ceres BEFORE PUBLIC
+    $<BUILD_INTERFACE:${Ceres_SOURCE_DIR}/internal/ceres/miniglog>
+    $<INSTALL_INTERFACE:include/ceres/internal/miniglog>)
+elseif (NOT FOUND_INSTALLED_GLOG_CMAKE_CONFIGURATION)
+  # Only append glog include directories if the glog found was not a CMake
+  # exported target that already includes them.
+  list(APPEND CERES_LIBRARY_PUBLIC_DEPENDENCIES_INCLUDE_DIRS
+    ${GLOG_INCLUDE_DIRS})
+endif()
+if (SUITESPARSE)
+  list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES_INCLUDE_DIRS
+    ${SUITESPARSE_INCLUDE_DIRS})
+endif()
+if (CXSPARSE)
+  list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES_INCLUDE_DIRS
+    ${CXSPARSE_INCLUDE_DIRS})
+endif()
+if (ACCELERATESPARSE)
+  list(APPEND CERES_LIBRARY_PRIVATE_DEPENDENCIES_INCLUDE_DIRS
+    ${AccelerateSparse_INCLUDE_DIRS})
+endif()
+if (GFLAGS AND NOT FOUND_INSTALLED_GFLAGS_CMAKE_CONFIGURATION)
+  # Only append gflags include directories if the gflags found was not a CMake
+  # exported target that already includes them.
+  list(APPEND CERES_LIBRARY_PUBLIC_DEPENDENCIES_INCLUDE_DIRS
+    ${GFLAGS_INCLUDE_DIRS})
+endif()
+# Add include locations for optional dependencies to the Ceres target without
+# duplication.
+list(REMOVE_DUPLICATES CERES_LIBRARY_PRIVATE_DEPENDENCIES_INCLUDE_DIRS)
+foreach(INC_DIR ${CERES_LIBRARY_PRIVATE_DEPENDENCIES_INCLUDE_DIRS})
+  target_include_directories(ceres PRIVATE ${INC_DIR})
+endforeach()
+list(REMOVE_DUPLICATES CERES_LIBRARY_PUBLIC_DEPENDENCIES_INCLUDE_DIRS)
+foreach(INC_DIR ${CERES_LIBRARY_PUBLIC_DEPENDENCIES_INCLUDE_DIRS})
+  target_include_directories(ceres PUBLIC ${INC_DIR})
+endforeach()
+
 install(TARGETS ceres
         EXPORT  CeresExport
         RUNTIME DESTINATION bin
@@ -289,6 +363,7 @@
               evaluator_test_utils.cc
               numeric_diff_test_utils.cc
               test_util.cc)
+  target_include_directories(test_util PUBLIC ${Ceres_SOURCE_DIR}/internal)
 
   if (MINIGLOG)
     # When using miniglog, it is compiled into Ceres, thus Ceres becomes
@@ -302,6 +377,11 @@
 
   macro (CERES_TEST NAME)
     add_executable(${NAME}_test ${NAME}_test.cc)
+    # Pull in local headers from the generated test directories when ceres_test()
+    # is invoked there, as well as the private headers in this directory which
+    # may be referenced without the 'ceres' path prefix.
+    target_include_directories(${NAME}_test PUBLIC ${CMAKE_CURRENT_LIST_DIR})
+    target_include_directories(${NAME}_test PUBLIC ${Ceres_SOURCE_DIR}/internal/ceres)
     target_link_libraries(${NAME}_test test_util ceres gtest)
     if (BUILD_SHARED_LIBS)
       # Define gtest-specific shared library flags for linking.
@@ -403,13 +483,18 @@
 
 endif (BUILD_TESTING AND GFLAGS)
 
+macro(add_dependencies_to_benchmark BENCHMARK_TARGET)
+  target_link_libraries(${BENCHMARK_TARGET} ceres benchmark::benchmark)
+  target_include_directories(${BENCHMARK_TARGET} PUBLIC ${Ceres_SOURCE_DIR}/internal)
+endmacro()
+
 if (BUILD_BENCHMARKS)
   add_executable(autodiff_cost_function_benchmark autodiff_cost_function_benchmark.cc)
-  target_link_libraries(autodiff_cost_function_benchmark ceres benchmark::benchmark)
+  add_dependencies_to_benchmark(autodiff_cost_function_benchmark)
 
   add_executable(small_blas_gemv_benchmark small_blas_gemv_benchmark.cc)
-  target_link_libraries(small_blas_gemv_benchmark ceres benchmark::benchmark)
+  add_dependencies_to_benchmark(small_blas_gemv_benchmark)
 
   add_executable(small_blas_gemm_benchmark small_blas_gemm_benchmark.cc)
-  target_link_libraries(small_blas_gemm_benchmark ceres benchmark::benchmark)
+  add_dependencies_to_benchmark(small_blas_gemm_benchmark)
 endif (BUILD_BENCHMARKS)