Add thread dependency & fix namespace detection on Windows for gflags.
- Add missing gflags threading dependency.
- Also add optional gflags Shlwapi dependency on Windows.
- Add check_cxx_source_compiles() with CMAKE_BUILD_TYPE to fix
namespace test builds on Windows with NMake generators.
- Add namespace regex on gflags.h when check_cxx_source_compiles()
fails on Windows using Visual Studio generator.
Change-Id: I6570c566e5a29c665d621a54935c16d16bac7117
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ef881df..9b7c29a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -424,7 +424,8 @@
# made search variables visible in GUI for user to set, but then user disables
# GFLAGS instead of setting them.
MARK_AS_ADVANCED(FORCE GFLAGS_INCLUDE_DIR
- GFLAGS_LIBRARY)
+ GFLAGS_LIBRARY
+ GFLAGS_NAMESPACE)
ENDIF (GFLAGS)
# MiniGLog.
diff --git a/cmake/FindGflags.cmake b/cmake/FindGflags.cmake
index 5d89bb0..99d461e 100644
--- a/cmake/FindGflags.cmake
+++ b/cmake/FindGflags.cmake
@@ -72,11 +72,16 @@
UNSET(GFLAGS_FOUND)
UNSET(GFLAGS_INCLUDE_DIRS)
UNSET(GFLAGS_LIBRARIES)
- UNSET(GFLAGS_NAMESPACE)
+ # Do not use unset, as we want to keep GFLAGS_NAMESPACE in the cache,
+ # but simply clear its value.
+ SET(GFLAGS_NAMESPACE "" CACHE STRING
+ "gflags namespace (google or gflags)" FORCE)
+
# Make results of search visible in the CMake GUI if gflags has not
# been found so that user does not have to toggle to advanced view.
MARK_AS_ADVANCED(CLEAR GFLAGS_INCLUDE_DIR
- GFLAGS_LIBRARY)
+ GFLAGS_LIBRARY
+ GFLAGS_NAMESPACE)
# Note <package>_FIND_[REQUIRED/QUIETLY] variables defined by FindPackage()
# use the camelcase library name, not uppercase.
IF (Gflags_FIND_QUIETLY)
@@ -90,6 +95,88 @@
ENDIF ()
ENDMACRO(GFLAGS_REPORT_NOT_FOUND)
+# A cut down version of CMake's check_cxx_source_compiles() macro, which
+# supports specification of the CMAKE_BUILD_TYPE with which the test should
+# be compiled (but not REGEX matching of the output). This is important
+# on Windows, for at least the NMake generator, which otherwise chooses
+# Debug, when the gflags library will typically be compiled in Release, and
+# MSVC will then fail to build, thus making all check_cxx_source_compiles()
+# tests fail.
+#
+# As per the CMake check_cxx_source_compiles() macro, we assume the following
+# variables can be set before this is invoked:
+#
+# CMAKE_REQUIRED_FLAGS - String of compile command line flags.
+# CMAKE_REQUIRED_DEFINITIONS - List of macros to define (-DFOO=bar).
+# CMAKE_REQUIRED_INCLUDES - List of include directories.
+# CMAKE_REQUIRED_LIBRARIES - List of libraries to link.
+#
+# This macro is a derivative of the CMake check_cxx_source_compiles() macro
+# distributed under the BSD License, Copyright 2005-2009 Kitware, Inc.
+MACRO(CHECK_CXX_SOURCE_COMPILES_WITH_BUILD_TYPE
+ SOURCE BUILD_TYPE VAR)
+ # Do not rerun test if output variable has an assigned value, ie is not
+ # an empty string.
+ IF ("${VAR}" MATCHES "^${VAR}$")
+ # Verify that the BUILD_TYPE is sane.
+ IF (NOT "${BUILD_TYPE}" STREQUAL "Release" AND
+ NOT "${BUILD_TYPE}" STREQUAL "Debug" AND
+ NOT "${BUILD_TYPE}" STREQUAL "RelWithDebInfo" AND
+ NOT "${BUILD_TYPE}" STREQUAL "MinSizeRel" AND
+ NOT "${BUILD_TYPE}" STREQUAL "")
+ MESSAGE(FATAL_ERROR "Invalid BUILD_TYPE: ${BUILD_TYPE}, is not a "
+ "valid CMAKE_BUILD_TYPE option.")
+ ENDIF()
+
+ SET(MACRO_CHECK_FUNCTION_DEFINITIONS
+ "-D${VAR} ${CMAKE_REQUIRED_FLAGS}")
+
+ SET(CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES)
+ IF (CMAKE_REQUIRED_LIBRARIES)
+ SET(CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES
+ LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES})
+ ENDIF()
+
+ SET(CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES)
+ IF (CMAKE_REQUIRED_INCLUDES)
+ SET(CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES
+ "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}")
+ ENDIF()
+
+ FILE(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.cxx"
+ "${SOURCE}\n")
+
+ MESSAGE(STATUS "Performing Test ${VAR}")
+ # Pass the CMAKE_BUILD_TYPE to the CMake test project created to execute
+ # the test (main difference over CMake's check_cxx_source_compiles() macro).
+ TRY_COMPILE(${VAR}
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/src.cxx
+ COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
+ ${CHECK_CXX_SOURCE_COMPILES_ADD_LIBRARIES}
+ CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
+ "-DCMAKE_BUILD_TYPE:STRING=${BUILD_TYPE}"
+ "${CHECK_CXX_SOURCE_COMPILES_ADD_INCLUDES}"
+ OUTPUT_VARIABLE OUTPUT)
+
+ IF (${VAR})
+ MESSAGE(STATUS "Performing Test ${VAR} - Success")
+ SET(${VAR} 1 CACHE INTERNAL "Test ${VAR}")
+ FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log
+ "Performing C++ SOURCE FILE Test ${VAR} succeded with the following output:\n"
+ "${OUTPUT}\n"
+ "Source file was:\n${SOURCE}\n")
+ ELSE()
+ MESSAGE(STATUS "Performing Test ${VAR} - Failed")
+ SET(${VAR} "" CACHE INTERNAL "Test ${VAR}")
+ FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log
+ "Performing C++ SOURCE FILE Test ${VAR} failed with the following output:\n"
+ "${OUTPUT}\n"
+ "Source file was:\n${SOURCE}\n")
+ ENDIF()
+ ENDIF()
+ENDMACRO()
+
# Search user-installed locations first, so that we prefer user installs
# to system installs where both exist.
#
@@ -130,53 +217,156 @@
ENDIF (NOT GFLAGS_LIBRARY OR
NOT EXISTS ${GFLAGS_LIBRARY})
+# gflags typically requires a threading library (which is OS dependent), note
+# that this defines the CMAKE_THREAD_LIBS_INIT variable. If we are able to
+# detect threads, we assume that gflags requires it.
+FIND_PACKAGE(Threads QUIET)
+SET(GFLAGS_LINK_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
+# On Windows, the Shlwapi library is used by gflags if available.
+IF (MSVC)
+ INCLUDE(CheckIncludeFileCXX)
+ CHECK_INCLUDE_FILE_CXX("shlwapi.h" HAVE_SHLWAPI)
+ IF (HAVE_SHLWAPI)
+ LIST(APPEND GFLAGS_LINK_LIBRARIES shlwapi.lib)
+ ENDIF(HAVE_SHLWAPI)
+ENDIF (MSVC)
+
# Mark internally as found, then verify. GFLAGS_REPORT_NOT_FOUND() unsets
# if called.
SET(GFLAGS_FOUND TRUE)
# Identify what namespace gflags was built with.
-IF (GFLAGS_INCLUDE_DIR)
- # First try the (older) google namespace.
- INCLUDE(CheckCXXSourceCompiles)
- # Setup include path & link library for gflags for CHECK_CXX_SOURCE_COMPILES
- SET(CMAKE_REQUIRED_INCLUDES ${GFLAGS_INCLUDE_DIR})
- SET(CMAKE_REQUIRED_LIBRARIES ${GFLAGS_LIBRARY})
- CHECK_CXX_SOURCE_COMPILES(
- "#include <gflags/gflags.h>
+IF (GFLAGS_INCLUDE_DIR AND NOT GFLAGS_NAMESPACE)
+ # To handle Windows peculiarities / CMake bugs on MSVC we try two approaches
+ # to detect the gflags namespace:
+ #
+ # 1) Try to use a custom version of check_cxx_source_compiles():
+ # check_cxx_source_compiles_with_build_type(),
+ # to compile a trivial program with the two choices for the gflags
+ # namespace.
+ #
+ # This works on all OSs except Windows. On Windows, if using NMake
+ # generator the previous method also works, if using Visual Studio, it does
+ # not, because our explicit instruction regarding the CMAKE_BUILD_TYPE is
+ # ignored by msbuild. This breaks detection because MSVC requires that the
+ # build type of the test project used by check_cxx_source_compiles() match
+ # that of gflags. However, msbuild forces the test project to use Debug,
+ # but gflags will be built in Release.
+ #
+ # 2) [In the event 1) fails] Use regex on the gflags.h header file to try to
+ # determine the gflags namespace. Whilst this is less robust than 1),
+ # it does avoid any interaction with msbuild.
+
+ # On Windows, it is required that the build type of the test app match that
+ # of gflags, as CMAKE_BUILD_TYPE may not be defined when this is called, we
+ # try each in turn.
+ LIST(APPEND TEST_BUILD_TYPES Release Debug)
+ FOREACH(BUILD_TYPE ${TEST_BUILD_TYPES})
+ STRING(TOUPPER "${BUILD_TYPE}" BUILD_TYPE_UPPERCASE)
+ # Setup include path & link library for gflags for CHECK_CXX_SOURCE_COMPILES.
+ SET(CMAKE_REQUIRED_INCLUDES ${GFLAGS_INCLUDE_DIR})
+ SET(CMAKE_REQUIRED_LIBRARIES ${GFLAGS_LIBRARY} ${GFLAGS_LINK_LIBRARIES})
+ # First try the (older) google namespace. Note that the output variable
+ # MUST be unique to the build type as otherwise the test is not repeated as
+ # it is assumed to have already been performed.
+ CHECK_CXX_SOURCE_COMPILES_WITH_BUILD_TYPE(
+ "#include <gflags/gflags.h>
int main(int argc, char * argv[]) {
google::ParseCommandLineFlags(&argc, &argv, true);
return 0;
}"
- GFLAGS_IN_GOOGLE_NAMESPACE)
- IF (GFLAGS_IN_GOOGLE_NAMESPACE)
- SET(GFLAGS_NAMESPACE google)
- ELSE (GFLAGS_IN_GOOGLE_NAMESPACE)
- # Try (newer) gflags namespace instead.
- #
- # Setup include path & link library for gflags for CHECK_CXX_SOURCE_COMPILES
- SET(CMAKE_REQUIRED_INCLUDES ${GFLAGS_INCLUDE_DIR})
- SET(CMAKE_REQUIRED_LIBRARIES ${GFLAGS_LIBRARY})
- CHECK_CXX_SOURCE_COMPILES(
- "#include <gflags/gflags.h>
+ ${BUILD_TYPE}
+ GFLAGS_IN_GOOGLE_NAMESPACE_${BUILD_TYPE_UPPERCASE})
+ IF (GFLAGS_IN_GOOGLE_NAMESPACE_${BUILD_TYPE_UPPERCASE})
+ SET(GFLAGS_NAMESPACE google)
+ BREAK()
+ ELSE (GFLAGS_IN_GOOGLE_NAMESPACE_${BUILD_TYPE_UPPERCASE})
+ # Try (newer) gflags namespace instead. Note that the output variable
+ # MUST be unique to the build type as otherwise the test is not repeated as
+ # it is assumed to have already been performed.
+ SET(CMAKE_REQUIRED_INCLUDES ${GFLAGS_INCLUDE_DIR})
+ SET(CMAKE_REQUIRED_LIBRARIES ${GFLAGS_LIBRARY} ${GFLAGS_LINK_LIBRARIES})
+ CHECK_CXX_SOURCE_COMPILES_WITH_BUILD_TYPE(
+ "#include <gflags/gflags.h>
int main(int argc, char * argv[]) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
return 0;
}"
- GFLAGS_IN_GFLAGS_NAMESPACE)
- IF (GFLAGS_IN_GFLAGS_NAMESPACE)
- SET(GFLAGS_NAMESPACE gflags)
- ENDIF (GFLAGS_IN_GFLAGS_NAMESPACE)
- ENDIF (GFLAGS_IN_GOOGLE_NAMESPACE)
+ ${BUILD_TYPE}
+ GFLAGS_IN_GFLAGS_NAMESPACE_${BUILD_TYPE_UPPERCASE})
+ IF (GFLAGS_IN_GFLAGS_NAMESPACE_${BUILD_TYPE_UPPERCASE})
+ SET(GFLAGS_NAMESPACE gflags)
+ BREAK()
+ ENDIF (GFLAGS_IN_GFLAGS_NAMESPACE_${BUILD_TYPE_UPPERCASE})
+ ENDIF (GFLAGS_IN_GOOGLE_NAMESPACE_${BUILD_TYPE_UPPERCASE})
+ ENDFOREACH()
- IF (NOT GFLAGS_NAMESPACE)
- GFLAGS_REPORT_NOT_FOUND(
- "Failed to determine gflags namespace, it is not google or gflags.")
- ENDIF (NOT GFLAGS_NAMESPACE)
-ENDIF (GFLAGS_INCLUDE_DIR)
+ IF (NOT GFLAGS_NAMESPACE)
+ # Failed to determine gflags namespace using
+ # check_cxx_source_compiles_with_build_type() method, try and obtain it
+ # using regex on the gflags header instead.
+ MESSAGE(STATUS "Failed to find gflags namespace using using "
+ "check_cxx_source_compiles(), trying namespace regex instead, "
+ "this is expected on Windows.")
+ # Scan gflags.h to identify what namespace gflags was built with.
+ SET(GFLAGS_HEADER_FILE ${GFLAGS_INCLUDE_DIR}/gflags/gflags.h)
+ IF (NOT EXISTS ${GFLAGS_HEADER_FILE})
+ GFLAGS_REPORT_NOT_FOUND(
+ "Could not find file: ${GFLAGS_HEADER_FILE} "
+ "containing namespace information in gflags install located at: "
+ "${GFLAGS_INCLUDE_DIR}.")
+ ELSE (NOT EXISTS ${GFLAGS_HEADER_FILE})
+ FILE(READ ${GFLAGS_HEADER_FILE} GFLAGS_HEADER_FILE_CONTENTS)
+
+ STRING(REGEX MATCH "namespace [A-Za-z]+"
+ GFLAGS_NAMESPACE "${GFLAGS_HEADER_FILE_CONTENTS}")
+ STRING(REGEX REPLACE "namespace ([A-Za-z]+)" "\\1"
+ GFLAGS_NAMESPACE "${GFLAGS_NAMESPACE}")
+
+ IF (NOT GFLAGS_NAMESPACE)
+ GFLAGS_REPORT_NOT_FOUND(
+ "Failed to extract gflags namespace from header file: "
+ "${GFLAGS_HEADER_FILE}.")
+ ENDIF (NOT GFLAGS_NAMESPACE)
+
+ # Verify that the namespace is either google or gflags. Strictly
+ # speaking the users can specify something else when building gflags,
+ # but doing so would cause so many compatibility issues, that they should
+ # not have done, so any mismatch is almost certainly a regex failure.
+ IF (NOT GFLAGS_NAMESPACE STREQUAL "google" AND
+ NOT GFLAGS_NAMESPACE STREQUAL "gflags")
+ GFLAGS_REPORT_NOT_FOUND(
+ "Failed to extract valid gflags namespace from header file: "
+ "${GFLAGS_HEADER_FILE}, result: ${GFLAGS_NAMESPACE} is not "
+ "google or gflags.")
+ ENDIF()
+ ENDIF (NOT EXISTS ${GFLAGS_HEADER_FILE})
+ ENDIF (NOT GFLAGS_NAMESPACE)
+
+ IF (NOT GFLAGS_NAMESPACE)
+ GFLAGS_REPORT_NOT_FOUND(
+ "Failed to determine gflags namespace either by "
+ "check_cxx_source_compiles(), or namespace regex.")
+ ENDIF (NOT GFLAGS_NAMESPACE)
+ENDIF (GFLAGS_INCLUDE_DIR AND NOT GFLAGS_NAMESPACE)
+
+# Make the GFLAGS_NAMESPACE a cache variable s/t the user can view it, and could
+# overwrite it in the CMake GUI.
+SET(GFLAGS_NAMESPACE "${GFLAGS_NAMESPACE}" CACHE STRING
+ "gflags namespace (google or gflags)" FORCE)
# gflags does not seem to provide any record of the version in its
# source tree, thus cannot extract version.
+# Catch case when caller has set GFLAGS_NAMESPACE in the cache / GUI
+# with an invalid value.
+IF (GFLAGS_NAMESPACE AND
+ NOT GFLAGS_NAMESPACE STREQUAL "google" AND
+ NOT GFLAGS_NAMESPACE STREQUAL "gflags")
+ GFLAGS_REPORT_NOT_FOUND(
+ "Caller defined GFLAGS_NAMESPACE:"
+ " ${GFLAGS_NAMESPACE} is not valid, not google or gflags.")
+ENDIF ()
# Catch case when caller has set GFLAGS_INCLUDE_DIR in the cache / GUI and
# thus FIND_[PATH/LIBRARY] are not called, but specified locations are
# invalid, otherwise we would report the library as found.
@@ -202,7 +392,7 @@
# Set standard CMake FindPackage variables if found.
IF (GFLAGS_FOUND)
SET(GFLAGS_INCLUDE_DIRS ${GFLAGS_INCLUDE_DIR})
- SET(GFLAGS_LIBRARIES ${GFLAGS_LIBRARY})
+ SET(GFLAGS_LIBRARIES ${GFLAGS_LIBRARY} ${GFLAGS_LINK_LIBRARIES})
ENDIF (GFLAGS_FOUND)
# Handle REQUIRED / QUIET optional arguments.