diff options
112 files changed, 4677 insertions, 2011 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index fc033ba..51a1d8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. -cmake_minimum_required(VERSION 3.1...3.12 FATAL_ERROR) +cmake_minimum_required(VERSION 3.1...3.14 FATAL_ERROR) set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideC.cmake) set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/Source/Modules/OverrideCXX.cmake) project(CMake) @@ -822,4 +822,10 @@ if(NOT CMake_TEST_EXTERNAL_CMAKE) # Install auxiliary files integrating with other tools. add_subdirectory(Auxiliary) + + # Optionally sign installed binaries. + if(CMake_INSTALL_SIGNTOOL) + configure_file(Source/CMakeInstallSignTool.cmake.in Source/CMakeInstallSignTool.cmake @ONLY) + install(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/Source/CMakeInstallSignTool.cmake) + endif() endif() diff --git a/Help/release/dev/FindBoost-fphsa.rst b/Help/release/dev/FindBoost-fphsa.rst new file mode 100644 index 0000000..c4d34d4 --- /dev/null +++ b/Help/release/dev/FindBoost-fphsa.rst @@ -0,0 +1,31 @@ +FindBoost-fphsa +--------------- + +* The :module:`FindBoost` module was reworked to expose a more + consistent user experience between its config and module modes + and with other find modules in general. + + * A new imported target ``Boost::headers`` is now defined (same + as ``Boost::boost``). + + * New output variables ``Boost_VERSION_MACRO``, + ``Boost_VERSION_MAJOR``, ``Boost_VERSION_MINOR``, + ``Boost_VERSION_PATCH``, and ``Boost_VERSION_COUNT`` + were added. + + * The internal logic for determining the value for + ``Boost_FOUND``, for version and component checks, and + for reporting the result to the user was replaced with + the :module:`FindPackageHandleStandardArgs` module. (This + fixed a bug that sometimes printed wrong status + messages in config mode.) + + * The ``QUIET`` argument passed to :command:`find_package` is no + longer ignored in config mode. + + * *Known issue*: The CMake package shipped with Boost ``1.70.0`` + ignores the ``QUIET`` argument passed to :command:`find_package`. + This is fixed in the next Boost release. + + * The input switch ``Boost_DETAILED_FAILURE_MSG`` was + removed. diff --git a/Help/release/dev/FindPython-module.rst b/Help/release/dev/FindPython-module.rst new file mode 100644 index 0000000..79ff9d8 --- /dev/null +++ b/Help/release/dev/FindPython-module.rst @@ -0,0 +1,6 @@ +FindPython-module +----------------- + +* Modules :module:`FindPython`, :module:`FindPython2` and :module:`FindPython3` + gain a new target (respectively ``Python::Module``, ``Python2::Module`` + and ``Python3::Module``) which can be used to develop Python modules. diff --git a/Help/release/dev/apple-clang-supports-cxx20.rst b/Help/release/dev/apple-clang-supports-cxx20.rst new file mode 100644 index 0000000..f19ee68 --- /dev/null +++ b/Help/release/dev/apple-clang-supports-cxx20.rst @@ -0,0 +1,4 @@ +apple-clang-supports-cxx20 +-------------------------- + +* AppleClang compiler have learned how to compile C++20. diff --git a/Help/release/dev/iar-new_architectures.rst b/Help/release/dev/iar-new_architectures.rst new file mode 100644 index 0000000..d661c80 --- /dev/null +++ b/Help/release/dev/iar-new_architectures.rst @@ -0,0 +1,6 @@ +iar-new_architectures +--------------------- + +* Support was added for the IAR compiler architectures Renesas RX, + RL78, RH850 and Texas Instruments MSP430. +* The IAR compilers built for Linux (IAR BuildLx) now work as well. diff --git a/Modules/CMakePlatformId.h.in b/Modules/CMakePlatformId.h.in index c88094a..8f5747a 100644 --- a/Modules/CMakePlatformId.h.in +++ b/Modules/CMakePlatformId.h.in @@ -159,9 +159,18 @@ # elif defined(__ICCRX__) # define ARCHITECTURE_ID "RX" +# elif defined(__ICCRH850__) +# define ARCHITECTURE_ID "RH850" + +# elif defined(__ICCRL78__) +# define ARCHITECTURE_ID "RL78" + # elif defined(__ICCAVR__) # define ARCHITECTURE_ID "AVR" +# elif defined(__ICC430__) +# define ARCHITECTURE_ID "MSP430" + # else /* unknown architecture */ # define ARCHITECTURE_ID "" # endif diff --git a/Modules/Compiler/AppleClang-CXX.cmake b/Modules/Compiler/AppleClang-CXX.cmake index 2042360..3fa6990 100644 --- a/Modules/Compiler/AppleClang-CXX.cmake +++ b/Modules/Compiler/AppleClang-CXX.cmake @@ -17,10 +17,12 @@ endif() if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.1) set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++14") + set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1) # AppleClang 5.0 knows this flag, but does not set a __cplusplus macro greater than 201103L set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++1y") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION "-std=gnu++1y") + set(CMAKE_CXX14_STANDARD__HAS_FULL_SUPPORT ON) endif() if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.1) @@ -28,9 +30,13 @@ if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.1) set(CMAKE_CXX17_EXTENSION_COMPILE_OPTION "-std=gnu++1z") endif() - if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.0) set(CMAKE_CXX11_STANDARD__HAS_FULL_SUPPORT ON) endif() +if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.0) + set(CMAKE_CXX20_STANDARD_COMPILE_OPTION "-std=c++2a") + set(CMAKE_CXX20_EXTENSION_COMPILE_OPTION "-std=gnu++2a") +endif() + __compiler_check_default_language_standard(CXX 4.0 98) diff --git a/Modules/Compiler/IAR-ASM.cmake b/Modules/Compiler/IAR-ASM.cmake index f9c0ced..71f2ac6 100644 --- a/Modules/Compiler/IAR-ASM.cmake +++ b/Modules/Compiler/IAR-ASM.cmake @@ -12,11 +12,26 @@ elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX") __compiler_iar_ilink(ASM) set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa) +elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RH850") + set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>") + __compiler_iar_ilink(ASM) + set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa) + +elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RL78") + set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>") + __compiler_iar_ilink(ASM) + set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa) + elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>") __compiler_iar_xlink(ASM) set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s90;asm;msa) +elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "MSP430") + set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>") + __compiler_iar_xlink(ASM) + set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s43;asm;msa) + else() message(FATAL_ERROR "CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID not detected. This should be automatic.") endif() diff --git a/Modules/Compiler/IAR-C.cmake b/Modules/Compiler/IAR-C.cmake index af5874c..4b02a9a 100644 --- a/Modules/Compiler/IAR-C.cmake +++ b/Modules/Compiler/IAR-C.cmake @@ -38,20 +38,23 @@ elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX") __compiler_iar_ilink(C) __compiler_check_default_language_standard(C 1.10 90 2.10 99 4.10 11) +elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "RH850") + __compiler_iar_ilink(C) + __compiler_check_default_language_standard(C 1.10 90 1.10 99 2.10 11) + +elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "RL78") + __compiler_iar_ilink(C) + __compiler_check_default_language_standard(C 1.10 90 1.10 99 4.10 11) + elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") __compiler_iar_xlink(C) __compiler_check_default_language_standard(C 7.10 99) set(CMAKE_C_OUTPUT_EXTENSION ".r90") - if(NOT CMAKE_C_LINK_FLAGS) - set(CMAKE_C_LINK_FLAGS "-Fmotorola") - endif() - - # add the target specific include directory: - get_filename_component(_compilerDir "${CMAKE_C_COMPILER}" PATH) - get_filename_component(_compilerDir "${_compilerDir}" PATH) - include_directories("${_compilerDir}/inc" ) - include_directories("${_compilerDir}/inc/Atmel" ) +elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "MSP430") + __compiler_iar_xlink(C) + __compiler_check_default_language_standard(C 1.10 90 5.10 99) + set(CMAKE_C_OUTPUT_EXTENSION ".r43") else() message(FATAL_ERROR "CMAKE_C_COMPILER_ARCHITECTURE_ID not detected. This should be automatic.") diff --git a/Modules/Compiler/IAR-CXX.cmake b/Modules/Compiler/IAR-CXX.cmake index 04ccbe5..22ec118 100644 --- a/Modules/Compiler/IAR-CXX.cmake +++ b/Modules/Compiler/IAR-CXX.cmake @@ -4,12 +4,14 @@ include(Compiler/IAR) include(Compiler/CMakeCommonCompilerMacros) # Common -if(NOT CMAKE_IAR_CXX_FLAG) - if(NOT CMAKE_CXX_COMPILER_VERSION) - message(FATAL_ERROR "CMAKE_CXX_COMPILER_VERSION not detected. This should be automatic.") - endif() +if(NOT CMAKE_CXX_COMPILER_VERSION) + message(FATAL_ERROR "CMAKE_CXX_COMPILER_VERSION not detected. This should be automatic.") +endif() - if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 7) +if(NOT CMAKE_IAR_CXX_FLAG) + # The --c++ flag was introduced in platform version 9 for all architectures except ARM where it was introduced already in version 7 + if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 8 OR + (CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 6 AND "${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM") ) set(CMAKE_IAR_CXX_FLAG --c++) else() set(CMAKE_IAR_CXX_FLAG --eec++) @@ -45,20 +47,22 @@ elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX") __compiler_iar_ilink(CXX) __compiler_check_default_language_standard(CXX 2.10 98 4.10 14) +elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "RH850") + __compiler_iar_ilink(CXX) + __compiler_check_default_language_standard(CXX 1.10 98 2.10 14) + +elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "RL78") + __compiler_iar_ilink(CXX) + __compiler_check_default_language_standard(CXX 1.10 98 4.10 14) + elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") __compiler_iar_xlink(CXX) __compiler_check_default_language_standard(CXX 7.10 98) - set(CMAKE_CXX_OUTPUT_EXTENSION ".r90") - if(NOT CMAKE_CXX_LINK_FLAGS) - set(CMAKE_CXX_LINK_FLAGS "-Fmotorola") - endif() - - # add the target specific include directory: - get_filename_component(_compilerDir "${CMAKE_C_COMPILER}" PATH) - get_filename_component(_compilerDir "${_compilerDir}" PATH) - include_directories("${_compilerDir}/inc" ) - include_directories("${_compilerDir}/inc/Atmel" ) +elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "MSP430") + __compiler_iar_xlink(CXX) + __compiler_check_default_language_standard(CXX 5.10 98) + set(CMAKE_CXX_OUTPUT_EXTENSION ".r43") else() message(FATAL_ERROR "CMAKE_CXX_COMPILER_ARCHITECTURE_ID not detected. This should be automatic." ) diff --git a/Modules/Compiler/IAR-DetermineCompiler.cmake b/Modules/Compiler/IAR-DetermineCompiler.cmake index cdfb095..6b09c40 100644 --- a/Modules/Compiler/IAR-DetermineCompiler.cmake +++ b/Modules/Compiler/IAR-DetermineCompiler.cmake @@ -31,7 +31,7 @@ set(_compiler_id_version_compute " # define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(((__VER__) / 1000) % 1000) # define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@((__VER__) % 1000) # define @PREFIX@COMPILER_VERSION_INTERNAL @MACRO_DEC@(__IAR_SYSTEMS_ICC__) -# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__)) +# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__) || defined(__ICCRH850__) || defined(__ICCRL78__) || defined(__ICC430__)) # define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@((__VER__) / 100) # define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@((__VER__) - (((__VER__) / 100)*100)) # define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@(__SUBVERSION__) diff --git a/Modules/Compiler/IAR-FindBinUtils.cmake b/Modules/Compiler/IAR-FindBinUtils.cmake index 7fc6b59..3f13dce 100644 --- a/Modules/Compiler/IAR-FindBinUtils.cmake +++ b/Modules/Compiler/IAR-FindBinUtils.cmake @@ -11,7 +11,9 @@ get_filename_component(__iar_hint_2 "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPI set(__iar_hints "${__iar_hint_1}" "${__iar_hint_2}") if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM" OR - "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX") + "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX" OR + "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RH850" OR + "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RL78") string(TOLOWER "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" _archid_lower) @@ -41,9 +43,10 @@ set(CMAKE_IAR_OBJMANIP \"${CMAKE_IAR_OBJMANIP}\") set(CMAKE_IAR_LINKER \"${CMAKE_IAR_LINKER}\") ") -elseif("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") +elseif("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR" OR + "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "MSP430") - # For AVR and AVR32, IAR uses the "xlink" linker and the "xar" archiver: + # Find the "xlink" linker and "xar" archiver: find_program(CMAKE_IAR_LINKER xlink HINTS ${__iar_hints} DOC "The IAR XLINK linker") find_program(CMAKE_IAR_AR xar HINTS ${__iar_hints} diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake index 023cc8d..0f15190 100644 --- a/Modules/FindBoost.cmake +++ b/Modules/FindBoost.cmake @@ -31,11 +31,16 @@ case results are reported in variables:: Boost_<C>_LIBRARY - Libraries to link for component <C> (may include target_link_libraries debug/optimized keywords) Boost_VERSION - BOOST_VERSION value from boost/version.hpp - Boost_LIB_VERSION - Version string appended to library filenames - Boost_MAJOR_VERSION - Boost major version number (X in X.y.z) - Boost_MINOR_VERSION - Boost minor version number (Y in x.Y.z) - Boost_SUBMINOR_VERSION - Boost subminor version number (Z in x.y.Z) + alias: Boost_VERSION_MACRO Boost_VERSION_STRING - Boost version number in x.y.z format + Boost_LIB_VERSION - Version string appended to library filenames + Boost_VERSION_MAJOR - Boost major version number (X in X.y.z) + alias: Boost_MAJOR_VERSION + Boost_VERSION_MINOR - Boost minor version number (Y in x.Y.z) + alias: Boost_MINOR_VERSION + Boost_VERSION_PATCH - Boost subminor version number (Z in x.y.Z) + alias: Boost_SUBMINOR_VERSION + Boost_VERSION_COUNT - Amount of version components (3) Boost_LIB_DIAGNOSTIC_DEFINITIONS (Windows) - Pass to add_definitions() to have diagnostic information about Boost's automatic linking @@ -72,8 +77,9 @@ and saves search results persistently in CMake cache entries:: The following :prop_tgt:`IMPORTED` targets are also defined:: - Boost::boost - Target for header-only dependencies + Boost::headers - Target for header-only dependencies (Boost include directory) + alias: Boost::boost Boost::<C> - Target for specific component dependency (shared or static library); <C> is lower- case @@ -162,10 +168,6 @@ Other variables one may set to control this module are:: Boost_DEBUG - Set to ON to enable debug output from FindBoost. Please enable this before filing any bug report. - Boost_DETAILED_FAILURE_MSG - - Set to ON to add detailed information to the - failure message even when the REQUIRED option - is not given to the find_package call. Boost_REALPATH - Set to ON to resolve symlinks for discovered libraries to assist with packaging. For example, the "system" component library may be resolved to @@ -227,9 +229,9 @@ Example to find Boost headers and some *static* (release only) libraries:: Boost CMake ^^^^^^^^^^^ -If Boost was built using the boost-cmake project it provides a package -configuration file for use with find_package's Config mode. This -module looks for the package configuration file called +If Boost was built using the boost-cmake project or from Boost 1.70.0 on +it provides a package configuration file for use with find_package's config mode. +This module looks for the package configuration file called ``BoostConfig.cmake`` or ``boost-config.cmake`` and stores the result in ``CACHE`` entry "Boost_DIR". If found, the package configuration file is loaded and this module returns with no further action. See documentation of @@ -238,13 +240,17 @@ the Boost CMake package configuration for details on what it provides. Set ``Boost_NO_BOOST_CMAKE`` to ``ON``, to disable the search for boost-cmake. #]=======================================================================] +# The FPHSA helper provides standard way of reporting final search results to +# the user including the version and component checks. +include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake) + # Save project's policies cmake_policy(PUSH) cmake_policy(SET CMP0057 NEW) # if IN_LIST #------------------------------------------------------------------------------- -# Before we go searching, check whether boost-cmake is available, unless the -# user specifically asked NOT to search for boost-cmake. +# Before we go searching, check whether a boost cmake package is available, unless +# the user specifically asked NOT to search for one. # # If Boost_DIR is set, this behaves as any find_package call would. If not, # it looks at BOOST_ROOT and BOOSTROOT to find Boost. @@ -266,13 +272,25 @@ if (NOT Boost_NO_BOOST_CMAKE) find_package(Boost QUIET NO_MODULE) mark_as_advanced(Boost_DIR) - # If we found boost-cmake, then we're done. Print out what we found. + # If we found a boost cmake package, then we're done. Print out what we found. # Otherwise let the rest of the module try to find it. - if (Boost_FOUND) - message(STATUS "Boost ${Boost_FIND_VERSION} found.") - if (Boost_FIND_COMPONENTS) - message(STATUS "Found Boost components:\n ${Boost_FIND_COMPONENTS}") + if(Boost_FOUND) + # Neither the legacy boost-cmake nor the new builtin BoostConfig (as in 1.70) + # report the found components in the standard variables, so we need to convert + # them here + if(Boost_FIND_COMPONENTS) + foreach(_comp IN LISTS Boost_FIND_COMPONENTS) + string(TOUPPER ${_comp} _uppercomp) + if(DEFINED Boost${_comp}_FOUND) + set(Boost_${_comp}_FOUND ${Boost${_comp}_FOUND}) + elseif(DEFINED Boost_${_uppercomp}_FOUND) + set(Boost_${_comp}_FOUND ${Boost_${_uppercomp}_FOUND}) + endif() + endforeach() endif() + + find_package_handle_standard_args(Boost HANDLE_COMPONENTS CONFIG_MODE) + # Restore project's policies cmake_policy(POP) return() @@ -499,7 +517,7 @@ function(_Boost_GUESS_COMPILER_PREFIX _ret) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "XL") set(_boost_COMPILER "-xlc") elseif (MINGW) - if(${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION} VERSION_LESS 1.34) + if(Boost_VERSION_STRING VERSION_LESS 1.34) set(_boost_COMPILER "-mgw") # no GCC version encoding prior to 1.34 else() _Boost_COMPILER_DUMPVERSION(_boost_COMPILER_VERSION _boost_COMPILER_VERSION_MAJOR _boost_COMPILER_VERSION_MINOR) @@ -507,7 +525,7 @@ function(_Boost_GUESS_COMPILER_PREFIX _ret) endif() elseif (UNIX) _Boost_COMPILER_DUMPVERSION(_boost_COMPILER_VERSION _boost_COMPILER_VERSION_MAJOR _boost_COMPILER_VERSION_MINOR) - if(NOT Boost_VERSION VERSION_LESS 106900) + if(NOT Boost_VERSION_STRING VERSION_LESS 1.69.0) # From GCC 5 and clang 4, versioning changes and minor becomes patch. # For those compilers, patch is exclude from compiler tag in Boost 1.69+ library naming. if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND _boost_COMPILER_VERSION_MAJOR VERSION_GREATER 4) @@ -518,25 +536,19 @@ function(_Boost_GUESS_COMPILER_PREFIX _ret) endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - if(${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION} VERSION_LESS 1.34) + if(Boost_VERSION_STRING VERSION_LESS 1.34) set(_boost_COMPILER "-gcc") # no GCC version encoding prior to 1.34 else() # Determine which version of GCC we have. if(APPLE) - if(Boost_MINOR_VERSION) - if(${Boost_MINOR_VERSION} GREATER 35) - # In Boost 1.36.0 and newer, the mangled compiler name used - # on macOS/Darwin is "xgcc". - set(_boost_COMPILER "-xgcc${_boost_COMPILER_VERSION}") - else() - # In Boost <= 1.35.0, there is no mangled compiler name for - # the macOS/Darwin version of GCC. - set(_boost_COMPILER "") - endif() - else() - # We don't know the Boost version, so assume it's - # pre-1.36.0. + if(Boost_VERSION_STRING VERSION_LESS 1.36.0) + # In Boost <= 1.35.0, there is no mangled compiler name for + # the macOS/Darwin version of GCC. set(_boost_COMPILER "") + else() + # In Boost 1.36.0 and newer, the mangled compiler name used + # on macOS/Darwin is "xgcc". + set(_boost_COMPILER "-xgcc${_boost_COMPILER_VERSION}") endif() else() set(_boost_COMPILER "-gcc${_boost_COMPILER_VERSION}") @@ -587,15 +599,14 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) # - Indent # s;^set(; set(;; # - Add conditionals - # s;Scanning /path/to/boost/sources/boost_\(.*\)_\(.*\)_\(.*); elseif(NOT Boost_VERSION VERSION_LESS \10\20\3 AND Boost_VERSION VERSION_LESS xxxx); + # s;Scanning /path/to/boost/sources/boost_\(.*\)_\(.*\)_\(.*); elseif(NOT Boost_VERSION_STRING VERSION_LESS \1\.\2\.\3 AND Boost_VERSION_STRING VERSION_LESS xxxx); # # This results in the logic seen below, but will require the xxxx # replacing with the following Boost release version (or the next # minor version to be released, e.g. 1.59 was the latest at the time - # of writing, making 1.60 the next, so 106000 is the needed version - # number). Identical consecutive releases were then merged together - # by updating the end range of the first block and removing the - # following redundant blocks. + # of writing, making 1.60 the next. Identical consecutive releases + # were then merged together by updating the end range of the first + # block and removing the following redundant blocks. # # Running the script against all historical releases should be # required only if the BoostScanDeps.cmake script logic is changed. @@ -609,22 +620,22 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) endif() set(_Boost_IMPORTED_TARGETS TRUE) - if(Boost_VERSION AND Boost_VERSION VERSION_LESS 103300) - message(WARNING "Imported targets and dependency information not available for Boost version ${Boost_VERSION} (all versions older than 1.33)") + if(Boost_VERSION_STRING AND Boost_VERSION_STRING VERSION_LESS 1.33.0) + message(WARNING "Imported targets and dependency information not available for Boost version ${Boost_VERSION_STRING} (all versions older than 1.33)") set(_Boost_IMPORTED_TARGETS FALSE) - elseif(NOT Boost_VERSION VERSION_LESS 103300 AND Boost_VERSION VERSION_LESS 103500) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.33.0 AND Boost_VERSION_STRING VERSION_LESS 1.35.0) set(_Boost_IOSTREAMS_DEPENDENCIES regex thread) set(_Boost_REGEX_DEPENDENCIES thread) set(_Boost_WAVE_DEPENDENCIES filesystem thread) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 103500 AND Boost_VERSION VERSION_LESS 103600) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.35.0 AND Boost_VERSION_STRING VERSION_LESS 1.36.0) set(_Boost_FILESYSTEM_DEPENDENCIES system) set(_Boost_IOSTREAMS_DEPENDENCIES regex) set(_Boost_MPI_DEPENDENCIES serialization) set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization) set(_Boost_WAVE_DEPENDENCIES filesystem system thread) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 103600 AND Boost_VERSION VERSION_LESS 103800) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.36.0 AND Boost_VERSION_STRING VERSION_LESS 1.38.0) set(_Boost_FILESYSTEM_DEPENDENCIES system) set(_Boost_IOSTREAMS_DEPENDENCIES regex) set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l) @@ -632,7 +643,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization) set(_Boost_WAVE_DEPENDENCIES filesystem system thread) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 103800 AND Boost_VERSION VERSION_LESS 104300) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.38.0 AND Boost_VERSION_STRING VERSION_LESS 1.43.0) set(_Boost_FILESYSTEM_DEPENDENCIES system) set(_Boost_IOSTREAMS_DEPENDENCIES regex) set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l) @@ -641,7 +652,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_THREAD_DEPENDENCIES date_time) set(_Boost_WAVE_DEPENDENCIES filesystem system thread date_time) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 104300 AND Boost_VERSION VERSION_LESS 104400) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.43.0 AND Boost_VERSION_STRING VERSION_LESS 1.44.0) set(_Boost_FILESYSTEM_DEPENDENCIES system) set(_Boost_IOSTREAMS_DEPENDENCIES regex) set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random) @@ -650,7 +661,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_THREAD_DEPENDENCIES date_time) set(_Boost_WAVE_DEPENDENCIES filesystem system thread date_time) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 104400 AND Boost_VERSION VERSION_LESS 104500) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.44.0 AND Boost_VERSION_STRING VERSION_LESS 1.45.0) set(_Boost_FILESYSTEM_DEPENDENCIES system) set(_Boost_IOSTREAMS_DEPENDENCIES regex) set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random serialization) @@ -659,7 +670,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_THREAD_DEPENDENCIES date_time) set(_Boost_WAVE_DEPENDENCIES serialization filesystem system thread date_time) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 104500 AND Boost_VERSION VERSION_LESS 104700) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.45.0 AND Boost_VERSION_STRING VERSION_LESS 1.47.0) set(_Boost_FILESYSTEM_DEPENDENCIES system) set(_Boost_IOSTREAMS_DEPENDENCIES regex) set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random) @@ -668,7 +679,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_THREAD_DEPENDENCIES date_time) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread date_time) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 104700 AND Boost_VERSION VERSION_LESS 104800) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.47.0 AND Boost_VERSION_STRING VERSION_LESS 1.48.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_FILESYSTEM_DEPENDENCIES system) set(_Boost_IOSTREAMS_DEPENDENCIES regex) @@ -678,7 +689,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_THREAD_DEPENDENCIES date_time) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread date_time) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 104800 AND Boost_VERSION VERSION_LESS 105000) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.48.0 AND Boost_VERSION_STRING VERSION_LESS 1.50.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_FILESYSTEM_DEPENDENCIES system) set(_Boost_IOSTREAMS_DEPENDENCIES regex) @@ -689,7 +700,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono system) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread date_time) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 105000 AND Boost_VERSION VERSION_LESS 105300) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.50.0 AND Boost_VERSION_STRING VERSION_LESS 1.53.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_FILESYSTEM_DEPENDENCIES system) set(_Boost_IOSTREAMS_DEPENDENCIES regex) @@ -700,7 +711,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono system) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 105300 AND Boost_VERSION VERSION_LESS 105400) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.53.0 AND Boost_VERSION_STRING VERSION_LESS 1.54.0) set(_Boost_ATOMIC_DEPENDENCIES thread chrono system date_time) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_FILESYSTEM_DEPENDENCIES system) @@ -712,7 +723,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono system) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 105400 AND Boost_VERSION VERSION_LESS 105500) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.54.0 AND Boost_VERSION_STRING VERSION_LESS 1.55.0) set(_Boost_ATOMIC_DEPENDENCIES thread chrono system date_time) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_FILESYSTEM_DEPENDENCIES system) @@ -725,7 +736,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono system) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 105500 AND Boost_VERSION VERSION_LESS 105600) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.55.0 AND Boost_VERSION_STRING VERSION_LESS 1.56.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_COROUTINE_DEPENDENCIES context system) set(_Boost_FILESYSTEM_DEPENDENCIES system) @@ -738,7 +749,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono system) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 105600 AND Boost_VERSION VERSION_LESS 105900) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.56.0 AND Boost_VERSION_STRING VERSION_LESS 1.59.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_COROUTINE_DEPENDENCIES context system) set(_Boost_FILESYSTEM_DEPENDENCIES system) @@ -752,7 +763,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono system) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 105900 AND Boost_VERSION VERSION_LESS 106000) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.59.0 AND Boost_VERSION_STRING VERSION_LESS 1.60.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_COROUTINE_DEPENDENCIES context system) set(_Boost_FILESYSTEM_DEPENDENCIES system) @@ -766,7 +777,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono system) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 106000 AND Boost_VERSION VERSION_LESS 106100) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.60.0 AND Boost_VERSION_STRING VERSION_LESS 1.61.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_COROUTINE_DEPENDENCIES context system) set(_Boost_FILESYSTEM_DEPENDENCIES system) @@ -780,7 +791,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono system) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 106100 AND Boost_VERSION VERSION_LESS 106200) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.61.0 AND Boost_VERSION_STRING VERSION_LESS 1.62.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) set(_Boost_COROUTINE_DEPENDENCIES context system) @@ -794,7 +805,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 106200 AND Boost_VERSION VERSION_LESS 106300) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.62.0 AND Boost_VERSION_STRING VERSION_LESS 1.63.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) set(_Boost_COROUTINE_DEPENDENCIES context system) @@ -809,7 +820,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 106300 AND Boost_VERSION VERSION_LESS 106500) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.63.0 AND Boost_VERSION_STRING VERSION_LESS 1.65.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) set(_Boost_COROUTINE_DEPENDENCIES context system) @@ -825,7 +836,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 106500 AND Boost_VERSION VERSION_LESS 106700) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.65.0 AND Boost_VERSION_STRING VERSION_LESS 1.67.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) set(_Boost_COROUTINE_DEPENDENCIES context system) @@ -842,7 +853,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono system) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 106700 AND Boost_VERSION VERSION_LESS 106800) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.67.0 AND Boost_VERSION_STRING VERSION_LESS 1.68.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) set(_Boost_COROUTINE_DEPENDENCIES context system) @@ -859,7 +870,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono system) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 106800 AND Boost_VERSION VERSION_LESS 106900) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.68.0 AND Boost_VERSION_STRING VERSION_LESS 1.69.0) set(_Boost_CHRONO_DEPENDENCIES system) set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time) set(_Boost_CONTRACT_DEPENDENCIES thread chrono system date_time) @@ -877,7 +888,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_TIMER_DEPENDENCIES chrono system) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) - elseif(NOT Boost_VERSION VERSION_LESS 106900 AND Boost_VERSION VERSION_LESS 107000) + elseif(NOT Boost_VERSION_STRING VERSION_LESS 1.69.0 AND Boost_VERSION_STRING VERSION_LESS 1.70.0) set(_Boost_CONTRACT_DEPENDENCIES thread chrono date_time) set(_Boost_COROUTINE_DEPENDENCIES context) set(_Boost_FIBER_DEPENDENCIES context) @@ -892,7 +903,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) else() - if(NOT Boost_VERSION VERSION_LESS 107000) + if(NOT Boost_VERSION_STRING VERSION_LESS 1.70.0) set(_Boost_CONTRACT_DEPENDENCIES thread chrono date_time) set(_Boost_COROUTINE_DEPENDENCIES context) set(_Boost_FIBER_DEPENDENCIES context) @@ -907,7 +918,7 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) endif() - if(NOT Boost_VERSION VERSION_LESS 107100) + if(NOT Boost_VERSION_STRING VERSION_LESS 1.71.0) message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets") endif() endif() @@ -945,7 +956,7 @@ function(_Boost_COMPONENT_HEADERS component _hdrs) set(_Boost_CHRONO_HEADERS "boost/chrono.hpp") set(_Boost_CONTAINER_HEADERS "boost/container/container_fwd.hpp") set(_Boost_CONTRACT_HEADERS "boost/contract.hpp") - if(Boost_VERSION VERSION_LESS 106100) + if(Boost_VERSION_STRING VERSION_LESS 1.61.0) set(_Boost_CONTEXT_HEADERS "boost/context/all.hpp") else() set(_Boost_CONTEXT_HEADERS "boost/context/detail/fcontext.hpp") @@ -1064,7 +1075,7 @@ endfunction() # function(_Boost_COMPILER_FEATURES component _ret) # Boost >= 1.62 - if(NOT Boost_VERSION VERSION_LESS 106200) + if(NOT Boost_VERSION_STRING VERSION_LESS 1.62.0) set(_Boost_FIBER_COMPILER_FEATURES cxx_alias_templates cxx_auto_type @@ -1200,11 +1211,6 @@ else() endif() endif() -# The reason that we failed to find Boost. This will be set to a -# user-friendly message when we fail to find some necessary piece of -# Boost. -set(Boost_ERROR_REASON) - if(Boost_DEBUG) # Output some of their choices message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " @@ -1388,74 +1394,45 @@ endif() # Extract version information from version.hpp # ------------------------------------------------------------------------ -# Set Boost_FOUND based only on header location and version. -# It will be updated below for component libraries. if(Boost_INCLUDE_DIR) if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "location of version.hpp: ${Boost_INCLUDE_DIR}/boost/version.hpp") endif() - # Extract Boost_VERSION and Boost_LIB_VERSION from version.hpp - set(Boost_VERSION 0) + # Extract Boost_VERSION_MACRO and Boost_LIB_VERSION from version.hpp + set(Boost_VERSION_MACRO 0) set(Boost_LIB_VERSION "") file(STRINGS "${Boost_INCLUDE_DIR}/boost/version.hpp" _boost_VERSION_HPP_CONTENTS REGEX "#define BOOST_(LIB_)?VERSION ") - set(_Boost_VERSION_REGEX "([0-9]+)") - set(_Boost_LIB_VERSION_REGEX "\"([0-9_]+)\"") - foreach(v VERSION LIB_VERSION) - if("${_boost_VERSION_HPP_CONTENTS}" MATCHES "#define BOOST_${v} ${_Boost_${v}_REGEX}") - set(Boost_${v} "${CMAKE_MATCH_1}") - endif() - endforeach() + if("${_boost_VERSION_HPP_CONTENTS}" MATCHES "#define BOOST_VERSION ([0-9]+)") + set(Boost_VERSION_MACRO "${CMAKE_MATCH_1}") + endif() + if("${_boost_VERSION_HPP_CONTENTS}" MATCHES "#define BOOST_LIB_VERSION \"([0-9_]+)\"") + set(Boost_LIB_VERSION "${CMAKE_MATCH_1}") + endif() unset(_boost_VERSION_HPP_CONTENTS) - math(EXPR Boost_MAJOR_VERSION "${Boost_VERSION} / 100000") - math(EXPR Boost_MINOR_VERSION "${Boost_VERSION} / 100 % 1000") - math(EXPR Boost_SUBMINOR_VERSION "${Boost_VERSION} % 100") - set(Boost_VERSION_STRING "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") + # Calculate version components + math(EXPR Boost_VERSION_MAJOR "${Boost_VERSION_MACRO} / 100000") + math(EXPR Boost_VERSION_MINOR "${Boost_VERSION_MACRO} / 100 % 1000") + math(EXPR Boost_VERSION_PATCH "${Boost_VERSION_MACRO} % 100") + set(Boost_VERSION_COUNT 3) + + # Define alias variables for backwards compat. + set(Boost_MAJOR_VERSION ${Boost_VERSION_MAJOR}) + set(Boost_MINOR_VERSION ${Boost_VERSION_MINOR}) + set(Boost_SUBMINOR_VERSION ${Boost_VERSION_PATCH}) + + # Define Boost version in x.y.z format + set(Boost_VERSION_STRING "${Boost_VERSION_MAJOR}.${Boost_VERSION_MINOR}.${Boost_VERSION_PATCH}") + + # Define final Boost_VERSION + set(Boost_VERSION ${Boost_VERSION_MACRO}) - string(APPEND Boost_ERROR_REASON - "Boost version: ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}\nBoost include path: ${Boost_INCLUDE_DIR}") if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - "version.hpp reveals boost " - "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") - endif() - - if(Boost_FIND_VERSION) - # Set Boost_FOUND based on requested version. - set(_Boost_VERSION "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") - if("${_Boost_VERSION}" VERSION_LESS "${Boost_FIND_VERSION}") - set(Boost_FOUND 0) - set(_Boost_VERSION_AGE "old") - elseif(Boost_FIND_VERSION_EXACT AND - NOT "${_Boost_VERSION}" VERSION_EQUAL "${Boost_FIND_VERSION}") - set(Boost_FOUND 0) - set(_Boost_VERSION_AGE "new") - else() - set(Boost_FOUND 1) - endif() - if(NOT Boost_FOUND) - # State that we found a version of Boost that is too new or too old. - string(APPEND Boost_ERROR_REASON - "\nDetected version of Boost is too ${_Boost_VERSION_AGE}. Requested version was ${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}") - if (Boost_FIND_VERSION_PATCH) - string(APPEND Boost_ERROR_REASON - ".${Boost_FIND_VERSION_PATCH}") - endif () - if (NOT Boost_FIND_VERSION_EXACT) - string(APPEND Boost_ERROR_REASON " (or newer)") - endif () - string(APPEND Boost_ERROR_REASON ".") - endif () - else() - # Caller will accept any Boost version. - set(Boost_FOUND 1) + "version.hpp reveals boost ${Boost_VERSION_STRING}") endif() -else() - set(Boost_FOUND 0) - string(APPEND Boost_ERROR_REASON - "Unable to find the Boost header files. Please set BOOST_ROOT to the root directory containing Boost or BOOST_INCLUDEDIR to the directory containing Boost's headers.") endif() # ------------------------------------------------------------------------ @@ -1580,7 +1557,7 @@ if(DEFINED Boost_ARCHITECTURE) else() set(_boost_ARCHITECTURE_TAG "") # {CMAKE_CXX_COMPILER_ARCHITECTURE_ID} is not currently set for all compilers - if(NOT "x${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "x" AND NOT Boost_VERSION VERSION_LESS 106600) + if(NOT "x${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "x" AND NOT Boost_VERSION_STRING VERSION_LESS 1.66.0) string(APPEND _boost_ARCHITECTURE_TAG "-") # This needs to be kept in-sync with the section of CMakePlatformId.h.in # inside 'defined(_WIN32) && defined(_MSC_VER)' @@ -1712,10 +1689,10 @@ endif() # On versions < 1.35, remove the System library from the considered list # since it wasn't added until 1.35. -if(Boost_VERSION AND Boost_FIND_COMPONENTS) - if(Boost_VERSION LESS 103500) - list(REMOVE_ITEM Boost_FIND_COMPONENTS system) - endif() +if(Boost_VERSION_STRING AND Boost_FIND_COMPONENTS) + if(Boost_VERSION_STRING VERSION_LESS 1.35.0) + list(REMOVE_ITEM Boost_FIND_COMPONENTS system) + endif() endif() # Additional components may be required via component dependencies. @@ -1975,57 +1952,25 @@ if(Boost_LIBRARY_DIRS) list(REMOVE_DUPLICATES Boost_LIBRARY_DIRS) endif() -# The above setting of Boost_FOUND was based only on the header files. -# Update it for the requested component libraries. -if(Boost_FOUND) - # The headers were found. Check for requested component libs. - set(_boost_CHECKED_COMPONENT FALSE) - set(_Boost_MISSING_COMPONENTS "") - foreach(COMPONENT ${Boost_FIND_COMPONENTS}) - string(TOUPPER ${COMPONENT} UPPERCOMPONENT) - set(_boost_CHECKED_COMPONENT TRUE) - if(NOT Boost_${UPPERCOMPONENT}_FOUND AND Boost_FIND_REQUIRED_${COMPONENT}) - list(APPEND _Boost_MISSING_COMPONENTS ${COMPONENT}) - endif() - endforeach() - if(_Boost_MISSING_COMPONENTS AND _Boost_EXTRA_FIND_COMPONENTS) - # Optional indirect dependencies are not counted as missing. - list(REMOVE_ITEM _Boost_MISSING_COMPONENTS ${_Boost_EXTRA_FIND_COMPONENTS}) - endif() +# ------------------------------------------------------------------------ +# Call FPHSA helper, see https://cmake.org/cmake/help/latest/module/FindPackageHandleStandardArgs.html +# ------------------------------------------------------------------------ - if(Boost_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] Boost_FOUND = ${Boost_FOUND}") +# Define aliases as needed by the component handler in the FPHSA helper below +foreach(_comp IN LISTS Boost_FIND_COMPONENTS) + string(TOUPPER ${_comp} _uppercomp) + if(DEFINED Boost_${_uppercomp}_FOUND) + set(Boost_${_comp}_FOUND ${Boost_${_uppercomp}_FOUND}) endif() +endforeach() - if (_Boost_MISSING_COMPONENTS) - set(Boost_FOUND 0) - # We were unable to find some libraries, so generate a sensible - # error message that lists the libraries we were unable to find. - string(APPEND Boost_ERROR_REASON - "\nCould not find the following") - if(Boost_USE_STATIC_LIBS) - string(APPEND Boost_ERROR_REASON " static") - endif() - string(APPEND Boost_ERROR_REASON - " Boost libraries:\n") - foreach(COMPONENT ${_Boost_MISSING_COMPONENTS}) - string(TOUPPER ${COMPONENT} UPPERCOMPONENT) - string(APPEND Boost_ERROR_REASON - " ${Boost_NAMESPACE}_${COMPONENT}${Boost_ERROR_REASON_${UPPERCOMPONENT}}\n") - endforeach() - - list(LENGTH Boost_FIND_COMPONENTS Boost_NUM_COMPONENTS_WANTED) - list(LENGTH _Boost_MISSING_COMPONENTS Boost_NUM_MISSING_COMPONENTS) - if (${Boost_NUM_COMPONENTS_WANTED} EQUAL ${Boost_NUM_MISSING_COMPONENTS}) - string(APPEND Boost_ERROR_REASON - "No Boost libraries were found. You may need to set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.") - else () - string(APPEND Boost_ERROR_REASON - "Some (but not all) of the required Boost libraries were found. You may need to install these additional Boost libraries. Alternatively, set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.") - endif () - endif () +find_package_handle_standard_args(Boost + REQUIRED_VARS Boost_INCLUDE_DIR + VERSION_VAR Boost_VERSION_STRING + HANDLE_COMPONENTS) - if( NOT Boost_LIBRARY_DIRS AND NOT _boost_CHECKED_COMPONENT ) +if(Boost_FOUND) + if( NOT Boost_LIBRARY_DIRS ) # Compatibility Code for backwards compatibility with CMake # 2.4's FindBoost module. @@ -2069,15 +2014,24 @@ endif() # ------------------------------------------------------------------------ if(Boost_FOUND) - # For header-only libraries - if(NOT TARGET Boost::boost) - add_library(Boost::boost INTERFACE IMPORTED) + # The builtin CMake package in Boost 1.70+ introduces a new name + # for the header-only lib, let's provide the same UI in module mode + if(NOT TARGET Boost::headers) + add_library(Boost::headers INTERFACE IMPORTED) if(Boost_INCLUDE_DIRS) - set_target_properties(Boost::boost PROPERTIES + set_target_properties(Boost::headers PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}") endif() endif() + # Define the old target name for header-only libraries for backwards + # compat. + if(NOT TARGET Boost::boost) + add_library(Boost::boost INTERFACE IMPORTED) + set_target_properties(Boost::boost + PROPERTIES INTERFACE_LINK_LIBRARIES Boost::headers) + endif() + foreach(COMPONENT ${Boost_FIND_COMPONENTS}) if(_Boost_IMPORTED_TARGETS AND NOT TARGET Boost::${COMPONENT}) string(TOUPPER ${COMPONENT} UPPERCOMPONENT) @@ -2133,46 +2087,20 @@ if(Boost_FOUND) endif() # ------------------------------------------------------------------------ -# Notification to end user about what was found +# Finalize # ------------------------------------------------------------------------ +# Report Boost_LIBRARIES set(Boost_LIBRARIES "") -if(Boost_FOUND) - if(NOT Boost_FIND_QUIETLY) - message(STATUS "Boost version: ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") - if(Boost_FIND_COMPONENTS) - message(STATUS "Found the following Boost libraries:") +foreach(_comp IN LISTS Boost_FIND_COMPONENTS) + string(TOUPPER ${_comp} _uppercomp) + if(Boost_${_uppercomp}_FOUND) + list(APPEND Boost_LIBRARIES ${Boost_${_uppercomp}_LIBRARY}) + if(_comp STREQUAL "thread") + list(APPEND Boost_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) endif() endif() - foreach( COMPONENT ${Boost_FIND_COMPONENTS} ) - string( TOUPPER ${COMPONENT} UPPERCOMPONENT ) - if( Boost_${UPPERCOMPONENT}_FOUND ) - if(NOT Boost_FIND_QUIETLY) - message (STATUS " ${COMPONENT}") - endif() - list(APPEND Boost_LIBRARIES ${Boost_${UPPERCOMPONENT}_LIBRARY}) - if(COMPONENT STREQUAL "thread") - list(APPEND Boost_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) - endif() - endif() - endforeach() -else() - if(Boost_FIND_REQUIRED) - message(SEND_ERROR "Unable to find the requested Boost libraries.\n${Boost_ERROR_REASON}") - else() - if(NOT Boost_FIND_QUIETLY) - # we opt not to automatically output Boost_ERROR_REASON here as - # it could be quite lengthy and somewhat imposing in its requests - # Since Boost is not always a required dependency we'll leave this - # up to the end-user. - if(Boost_DEBUG OR Boost_DETAILED_FAILURE_MSG) - message(STATUS "Could NOT find Boost\n${Boost_ERROR_REASON}") - else() - message(STATUS "Could NOT find Boost") - endif() - endif() - endif() -endif() +endforeach() # Configure display of cache entries in GUI. foreach(v BOOSTROOT BOOST_ROOT ${_Boost_VARS_INC} ${_Boost_VARS_LIB}) diff --git a/Modules/FindProtobuf.cmake b/Modules/FindProtobuf.cmake index 1758fb3..76bc873 100644 --- a/Modules/FindProtobuf.cmake +++ b/Modules/FindProtobuf.cmake @@ -207,9 +207,14 @@ function(protobuf_generate) get_filename_component(_basename ${_proto} NAME_WE) file(RELATIVE_PATH _rel_dir ${CMAKE_CURRENT_SOURCE_DIR} ${_abs_dir}) + set(_possible_rel_dir) + if (NOT protobuf_generate_APPEND_PATH) + set(_possible_rel_dir ${_rel_dir}/) + endif() + set(_generated_srcs) foreach(_ext ${protobuf_generate_GENERATE_EXTENSIONS}) - list(APPEND _generated_srcs "${protobuf_generate_PROTOC_OUT_DIR}/${_basename}${_ext}") + list(APPEND _generated_srcs "${protobuf_generate_PROTOC_OUT_DIR}/${_possible_rel_dir}${_basename}${_ext}") endforeach() if(protobuf_generate_DESCRIPTORS AND protobuf_generate_LANGUAGE STREQUAL cpp) diff --git a/Modules/FindPython.cmake b/Modules/FindPython.cmake index c5074e8..36c0611 100644 --- a/Modules/FindPython.cmake +++ b/Modules/FindPython.cmake @@ -46,7 +46,11 @@ This module defines the following :ref:`Imported Targets <Imported Targets>` ``Python::Compiler`` Python compiler. Target defined if component ``Compiler`` is found. ``Python::Python`` - Python library. Target defined if component ``Development`` is found. + Python library for Python embedding. Target defined if component + ``Development`` is found. +``Python::Module`` + Python library for Python module. Target defined if component ``Development`` + is found. ``Python::NumPy`` NumPy Python library. Target defined if component ``NumPy`` is found. @@ -175,9 +179,9 @@ Commands This module defines the command ``Python_add_library`` (when :prop_gbl:`CMAKE_ROLE` is ``PROJECT``), which has the same semantics as -:command:`add_library`, but takes care of Python module naming rules -(only applied if library is of type ``MODULE``), and adds a dependency to target -``Python::Python``:: +:command:`add_library` and adds a dependency to target ``Python::Python`` or, +when library type is ``MODULE``, to target ``Python::Module`` and takes care of +Python module naming rules:: Python_add_library (my_module MODULE src1.cpp) diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake index 6572fb8..6d709e1 100644 --- a/Modules/FindPython/Support.cmake +++ b/Modules/FindPython/Support.cmake @@ -1253,72 +1253,104 @@ if(_${_PYTHON_PREFIX}_CMAKE_ROLE STREQUAL "PROJECT") endif() if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS - AND ${_PYTHON_PREFIX}_Development_FOUND AND NOT TARGET ${_PYTHON_PREFIX}::Python) + AND ${_PYTHON_PREFIX}_Development_FOUND) - if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" - OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" - OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) - set (_${_PYTHON_PREFIX}_LIBRARY_TYPE SHARED) - else() - set (_${_PYTHON_PREFIX}_LIBRARY_TYPE STATIC) - endif() - - add_library (${_PYTHON_PREFIX}::Python ${_${_PYTHON_PREFIX}_LIBRARY_TYPE} IMPORTED) - - set_property (TARGET ${_PYTHON_PREFIX}::Python - PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIR}") - - if ((${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE) - OR (${_PYTHON_PREFIX}_LIBRARY_DEBUG AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG)) - # System manage shared libraries in two parts: import and runtime - if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG) - set_property (TARGET ${_PYTHON_PREFIX}::Python PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) - set_target_properties (${_PYTHON_PREFIX}::Python - PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" - IMPORTED_IMPLIB_RELEASE "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" - IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") - set_target_properties (${_PYTHON_PREFIX}::Python - PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" - IMPORTED_IMPLIB_DEBUG "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}" - IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") + macro (__PYTHON_IMPORT_LIBRARY __name) + if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" + OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" + OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) + set (_${_PYTHON_PREFIX}_LIBRARY_TYPE SHARED) else() - set_target_properties (${_PYTHON_PREFIX}::Python - PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_IMPLIB "${${_PYTHON_PREFIX}_LIBRARY}" - IMPORTED_LOCATION "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY}") + set (_${_PYTHON_PREFIX}_LIBRARY_TYPE STATIC) endif() - else() - if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG) - set_property (TARGET ${_PYTHON_PREFIX}::Python PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) - set_target_properties (${_PYTHON_PREFIX}::Python - PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" - IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}") - set_target_properties (${_PYTHON_PREFIX}::Python - PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" - IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}") + + add_library (${__name} ${_${_PYTHON_PREFIX}_LIBRARY_TYPE} IMPORTED) + + set_property (TARGET ${__name} + PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIR}") + + if ((${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE) + OR (${_PYTHON_PREFIX}_LIBRARY_DEBUG AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG)) + # System manage shared libraries in two parts: import and runtime + if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG) + set_property (TARGET ${__name} PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" + IMPORTED_IMPLIB_RELEASE "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" + IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" + IMPORTED_IMPLIB_DEBUG "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}" + IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") + else() + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_IMPLIB "${${_PYTHON_PREFIX}_LIBRARY}" + IMPORTED_LOCATION "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY}") + endif() else() - set_target_properties (${_PYTHON_PREFIX}::Python - PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_LOCATION "${${_PYTHON_PREFIX}_LIBRARY}") + if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG) + set_property (TARGET ${_PYTHON_PREFIX}::Python PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" + IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}") + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" + IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}") + else() + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${${_PYTHON_PREFIX}_LIBRARY}") + endif() endif() + + if (_${_PYTHON_PREFIX}_CONFIG AND _${_PYTHON_PREFIX}_LIBRARY_TYPE STREQUAL "STATIC") + # extend link information with dependent libraries + execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --ldflags + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_FLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (NOT _${_PYTHON_PREFIX}_RESULT) + string (REGEX MATCHALL "-[Ll][^ ]+" _${_PYTHON_PREFIX}_LINK_LIBRARIES "${_${_PYTHON_PREFIX}_FLAGS}") + # remove elements relative to python library itself + list (FILTER _${_PYTHON_PREFIX}_LINK_LIBRARIES EXCLUDE REGEX "-lpython") + foreach (_${_PYTHON_PREFIX}_DIR IN LISTS ${_PYTHON_PREFIX}_LIBRARY_DIRS) + list (FILTER _${_PYTHON_PREFIX}_LINK_LIBRARIES EXCLUDE REGEX "-L${${_PYTHON_PREFIX}_DIR}") + endforeach() + set_property (TARGET ${__name} + PROPERTY INTERFACE_LINK_LIBRARIES ${_${_PYTHON_PREFIX}_LINK_LIBRARIES}) + endif() + endif() + endmacro() + + if (NOT TARGET ${_PYTHON_PREFIX}::Python) + __python_import_library (${_PYTHON_PREFIX}::Python) endif() - if (_${_PYTHON_PREFIX}_CONFIG AND _${_PYTHON_PREFIX}_LIBRARY_TYPE STREQUAL "STATIC") - # extend link information with dependent libraries - execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --ldflags - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_FLAGS - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (NOT _${_PYTHON_PREFIX}_RESULT) - string (REGEX MATCHALL "-[Ll][^ ]+" _${_PYTHON_PREFIX}_LINK_LIBRARIES "${_${_PYTHON_PREFIX}_FLAGS}") - # remove elements relative to python library itself - list (FILTER _${_PYTHON_PREFIX}_LINK_LIBRARIES EXCLUDE REGEX "-lpython") - foreach (_${_PYTHON_PREFIX}_DIR IN LISTS ${_PYTHON_PREFIX}_LIBRARY_DIRS) - list (FILTER _${_PYTHON_PREFIX}_LINK_LIBRARIES EXCLUDE REGEX "-L${${_PYTHON_PREFIX}_DIR}") - endforeach() - set_property (TARGET ${_PYTHON_PREFIX}::Python - PROPERTY INTERFACE_LINK_LIBRARIES ${_${_PYTHON_PREFIX}_LINK_LIBRARIES}) + if (NOT TARGET ${_PYTHON_PREFIX}::Module) + if (CMAKE_SYSTEM_NAME MATCHES "^(Windows.*|CYGWIN|MSYS)$") + # On Windows/CYGWIN/MSYS, Python::Module is the same as Python::Python + # but ALIAS cannot be used because the imported library is not GLOBAL. + __python_import_library (${_PYTHON_PREFIX}::Module) + else() + add_library (${_PYTHON_PREFIX}::Module INTERFACE IMPORTED) + set_property (TARGET ${_PYTHON_PREFIX}::Module + PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIR}") + + # When available, enforce shared library generation with undefined symbols + if (APPLE) + set_property (TARGET ${_PYTHON_PREFIX}::Module + PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup") + endif() + if (CMAKE_SYSTEM_NAME STREQUAL "SunOS") + set_property (TARGET ${_PYTHON_PREFIX}::Module + PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-z,nodefs") + endif() + if (CMAKE_SYSTEM_NAME STREQUAL "AIX") + set_property (TARGET ${_PYTHON_PREFIX}::Module + PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-b,erok") + endif() endif() endif() @@ -1328,7 +1360,7 @@ if(_${_PYTHON_PREFIX}_CMAKE_ROLE STREQUAL "PROJECT") # function (__${_PYTHON_PREFIX}_ADD_LIBRARY prefix name) cmake_parse_arguments (PARSE_ARGV 2 PYTHON_ADD_LIBRARY - "STATIC;SHARED;MODULE" "" "") + "STATIC;SHARED;MODULE" "" "") unset (type) if (NOT (PYTHON_ADD_LIBRARY_STATIC @@ -1337,15 +1369,18 @@ if(_${_PYTHON_PREFIX}_CMAKE_ROLE STREQUAL "PROJECT") set (type MODULE) endif() add_library (${name} ${type} ${ARGN}) - target_link_libraries (${name} PRIVATE ${prefix}::Python) - # customize library name to follow module name rules get_property (type TARGET ${name} PROPERTY TYPE) + if (type STREQUAL "MODULE_LIBRARY") + target_link_libraries (${name} PRIVATE ${prefix}::Module) + # customize library name to follow module name rules set_property (TARGET ${name} PROPERTY PREFIX "") if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set_property (TARGET ${name} PROPERTY SUFFIX ".pyd") endif() + else() + target_link_libraries (${name} PRIVATE ${prefix}::Python) endif() endfunction() endif() @@ -1355,7 +1390,7 @@ if(_${_PYTHON_PREFIX}_CMAKE_ROLE STREQUAL "PROJECT") add_library (${_PYTHON_PREFIX}::NumPy INTERFACE IMPORTED) set_property (TARGET ${_PYTHON_PREFIX}::NumPy PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") - target_link_libraries (${_PYTHON_PREFIX}::NumPy INTERFACE ${_PYTHON_PREFIX}::Python) + target_link_libraries (${_PYTHON_PREFIX}::NumPy INTERFACE ${_PYTHON_PREFIX}::Module) endif() endif() diff --git a/Modules/FindPython2.cmake b/Modules/FindPython2.cmake index a2be84f..67499d8 100644 --- a/Modules/FindPython2.cmake +++ b/Modules/FindPython2.cmake @@ -47,7 +47,11 @@ This module defines the following :ref:`Imported Targets <Imported Targets>` ``Python2::Compiler`` Python 2 compiler. Target defined if component ``Compiler`` is found. ``Python2::Python`` - Python 2 library. Target defined if component ``Development`` is found. + Python 2 library for Python embedding. Target defined if component + ``Development`` is found. +``Python2::Module`` + Python 2 library for Python module. Target defined if component + ``Development`` is found. ``Python2::NumPy`` NumPy library for Python 2. Target defined if component ``NumPy`` is found. @@ -174,11 +178,11 @@ Hints Commands ^^^^^^^^ -This module defines the command ``Python2_add_library`` (when +This module defines the command ``Python_add_library`` (when :prop_gbl:`CMAKE_ROLE` is ``PROJECT``), which has the same semantics as -:command:`add_library`, but takes care of Python module naming rules -(only applied if library is of type ``MODULE``), and adds a dependency to target -``Python2::Python``:: +:command:`add_library` and adds a dependency to target ``Python2::Python`` or, +when library type is ``MODULE``, to target ``Python2::Module`` and takes care +of Python module naming rules:: Python2_add_library (my_module MODULE src1.cpp) diff --git a/Modules/FindPython3.cmake b/Modules/FindPython3.cmake index 3409554..fdb99a6 100644 --- a/Modules/FindPython3.cmake +++ b/Modules/FindPython3.cmake @@ -47,7 +47,11 @@ This module defines the following :ref:`Imported Targets <Imported Targets>` ``Python3::Compiler`` Python 3 compiler. Target defined if component ``Compiler`` is found. ``Python3::Python`` - Python 3 library. Target defined if component ``Development`` is found. + Python 3 library for Python embedding. Target defined if component + ``Development`` is found. +``Python3::Module`` + Python 3 library for Python module. Target defined if component + ``Development`` is found. ``Python3::NumPy`` NumPy library for Python 3. Target defined if component ``NumPy`` is found. @@ -174,11 +178,11 @@ Hints Commands ^^^^^^^^ -This module defines the command ``Python3_add_library`` (when +This module defines the command ``Python_add_library`` (when :prop_gbl:`CMAKE_ROLE` is ``PROJECT``), which has the same semantics as -:command:`add_library`, but takes care of Python module naming rules -(only applied if library is of type ``MODULE``), and adds a dependency to target -``Python3::Python``:: +:command:`add_library` and adds a dependency to target ``Python3::Python`` or, +when library type is ``MODULE``, to target ``Python3::Module`` and takes care +of Python module naming rules:: Python3_add_library (my_module MODULE src1.cpp) diff --git a/Modules/FindSWIG.cmake b/Modules/FindSWIG.cmake index a5e3a37..ae6ae56 100644 --- a/Modules/FindSWIG.cmake +++ b/Modules/FindSWIG.cmake @@ -26,7 +26,7 @@ to be found can be changed from the command line by means of setting ``SWIG_EXECUTABLE`` #]=======================================================================] -find_program(SWIG_EXECUTABLE NAMES swig3.0 swig2.0 swig) +find_program(SWIG_EXECUTABLE NAMES swig4.0 swig3.0 swig2.0 swig) if(SWIG_EXECUTABLE) execute_process(COMMAND ${SWIG_EXECUTABLE} -swiglib diff --git a/Modules/FindXCTest.cmake b/Modules/FindXCTest.cmake index 8497336..15721e1 100644 --- a/Modules/FindXCTest.cmake +++ b/Modules/FindXCTest.cmake @@ -61,6 +61,22 @@ The following variables are set by including this module: #]=======================================================================] +set(_PRESERVED_CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH}") + +if(CMAKE_EFFECTIVE_SYSTEM_NAME STREQUAL "Apple" + AND NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") + # Non-macos systems set the CMAKE_FIND_ROOT_PATH_MODE to "ONLY" which + # restricts the search paths too much to find XCTest.framework. In + # contrast to the regular system frameworks which reside within the + # SDK direectory the XCTest framework is located in the respective + # platform directory which is not added to the CMAKE_FIND_ROOT_PATH + # (only to CMAKE_SYSTEM_FRAMEWORK_PATH) and therefore not searched. + # + # Until this is properly addressed, temporaily add the platform + # directory to CMAKE_FIND_ROOT_PATH. + list(APPEND CMAKE_FIND_ROOT_PATH "${_CMAKE_OSX_SYSROOT_PATH}/../..") +endif() + find_path(XCTest_INCLUDE_DIR NAMES "XCTest/XCTest.h" DOC "XCTest include directory") @@ -71,6 +87,9 @@ find_library(XCTest_LIBRARY DOC "XCTest Framework library") mark_as_advanced(XCTest_LIBRARY) +set(CMAKE_FIND_ROOT_PATH "${_PRESERVED_CMAKE_FIND_ROOT_PATH}") +unset(_PRESERVED_CMAKE_FIND_ROOT_PATH) + execute_process( COMMAND xcrun --find xctest OUTPUT_VARIABLE _xcrun_out OUTPUT_STRIP_TRAILING_WHITESPACE diff --git a/Source/CMakeInstallSignTool.cmake.in b/Source/CMakeInstallSignTool.cmake.in new file mode 100644 index 0000000..fca629c --- /dev/null +++ b/Source/CMakeInstallSignTool.cmake.in @@ -0,0 +1,51 @@ +# The signtool. Default to PATH. +set(CMake_INSTALL_SIGNTOOL "@CMake_INSTALL_SIGNTOOL@") +if(NOT CMake_INSTALL_SIGNTOOL) + set(CMake_INSTALL_SIGNTOOL signtool) +endif() + +# Select a certificate by Subject Name. Default to automatic selection. +set(CMake_INSTALL_SIGNTOOL_SUBJECT_NAME "@CMake_INSTALL_SIGNTOOL_SUBJECT_NAME@") +if(CMake_INSTALL_SIGNTOOL_SUBJECT_NAME) + set(select_cert -n "${CMake_INSTALL_SIGNTOOL_SUBJECT_NAME}") +else() + set(select_cert -a) +endif() + +# Timestamp URL. Default to a common provider. +set(CMake_INSTALL_SIGNTOOL_TIMESTAMP_URL "@CMake_INSTALL_SIGNTOOL_TIMESTAMP_URL@") +if(NOT CMake_INSTALL_SIGNTOOL_TIMESTAMP_URL) + set(CMake_INSTALL_SIGNTOOL_TIMESTAMP_URL "http://timestamp.digicert.com") +endif() + +# Glob files that need a signature. +file(GLOB files "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/*.exe") + +# Sign all files at once. +if(files) + # Run the signtool through 'cmd /c' to enable password prompt popup. + # Some providers have trouble when signtool is invoked with SW_HIDE. + set(cmd cmd /c "${CMake_INSTALL_SIGNTOOL}" sign -v ${select_cert}) + + # Sign with SHA-1 for Windows 7 and below. + execute_process( + COMMAND ${cmd} -t "${CMake_INSTALL_SIGNTOOL_TIMESTAMP_URL}" ${files} + RESULT_VARIABLE result + ERROR_VARIABLE stderr + ) + if(NOT result EQUAL 0) + string(REPLACE "\n" "\n " stderr " ${stderr}") + message(WARNING "signtool failed:\n${stderr}") + endif() + + # Sign with SHA-256 for Windows 8 and above. + execute_process( + COMMAND ${cmd} -tr "${CMake_INSTALL_SIGNTOOL_TIMESTAMP_URL}" -fd sha256 -td sha256 -as ${files} + RESULT_VARIABLE result + ERROR_VARIABLE stderr + ) + if(NOT result EQUAL 0) + string(REPLACE "\n" "\n " stderr " ${stderr}") + message(WARNING "signtool failed:\n${stderr}") + endif() +endif() diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 01c6cd7..42eed4d 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -388,6 +388,8 @@ set(SRCS cmUuid.cxx cmUVHandlePtr.cxx cmUVHandlePtr.h + cmUVProcessChain.cxx + cmUVProcessChain.h cmUVStreambuf.h cmUVSignalHackRAII.h cmVariableWatch.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 503bd6e..9f2754f 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 14) -set(CMake_VERSION_PATCH 20190508) +set(CMake_VERSION_PATCH 20190514) #set(CMake_VERSION_RC 1) diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index 9918c35..a245b54 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx @@ -194,11 +194,11 @@ std::string cmQtAutoGen::QuotedCommand(std::vector<std::string> const& command) std::string cmQtAutoGen::SubDirPrefix(std::string const& filename) { - std::string res(cmSystemTools::GetFilenamePath(filename)); - if (!res.empty()) { - res += '/'; + std::string::size_type slash_pos = filename.rfind('/'); + if (slash_pos == std::string::npos) { + return std::string(); } - return res; + return filename.substr(0, slash_pos + 1); } std::string cmQtAutoGen::AppendFilenameSuffix(std::string const& filename, diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index be96f1a..38f39fb 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -34,6 +34,7 @@ #include <map> #include <set> #include <string> +#include <unordered_set> #include <utility> #include <vector> @@ -77,7 +78,8 @@ static std::string FileProjectRelativePath(cmMakefile* makefile, return res; } -/* @brief Tests if targetDepend is a STATIC_LIBRARY and if any of its +/** + * Tests if targetDepend is a STATIC_LIBRARY and if any of its * recursive STATIC_LIBRARY dependencies depends on targetOrigin * (STATIC_LIBRARY cycle). */ @@ -384,6 +386,10 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } else { AddCleanFile(makefile, this->AutogenTarget.SettingsFile); } + + this->AutogenTarget.ParseCacheFile = this->Dir.Info; + this->AutogenTarget.ParseCacheFile += "/ParseCache.txt"; + AddCleanFile(makefile, this->AutogenTarget.ParseCacheFile); } // Autogen target: Compute user defined dependencies @@ -1255,53 +1261,107 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() ofs.Write("AM_INCLUDE_DIR", this->Dir.Include); ofs.WriteConfig("AM_INCLUDE_DIR", this->Dir.ConfigInclude); - // Use sorted sets - std::set<std::string> headers; - std::set<std::string> sources; - std::set<std::string> moc_headers; - std::set<std::string> moc_sources; + std::vector<std::string> headers; + std::vector<std::string> headersFlags; + std::vector<std::string> headersBuildPaths; + std::vector<std::string> sources; + std::vector<std::string> sourcesFlags; std::set<std::string> moc_skip; - std::set<std::string> uic_headers; - std::set<std::string> uic_sources; std::set<std::string> uic_skip; + // Filter headers - for (auto const& pair : this->AutogenTarget.Headers) { - MUFile const& muf = *pair.second; - if (muf.Generated && !this->CMP0071Accept) { - continue; - } - if (muf.SkipMoc) { - moc_skip.insert(muf.RealPath); + { + auto headerCount = this->AutogenTarget.Headers.size(); + headers.reserve(headerCount); + headersFlags.reserve(headerCount); + + std::vector<MUFile const*> sortedHeaders; + { + sortedHeaders.reserve(headerCount); + for (auto const& pair : this->AutogenTarget.Headers) { + sortedHeaders.emplace_back(pair.second.get()); + } + std::sort(sortedHeaders.begin(), sortedHeaders.end(), + [](MUFile const* a, MUFile const* b) { + return (a->RealPath < b->RealPath); + }); } - if (muf.SkipUic) { - uic_skip.insert(muf.RealPath); + + for (MUFile const* const muf : sortedHeaders) { + if (muf->Generated && !this->CMP0071Accept) { + continue; + } + if (muf->SkipMoc) { + moc_skip.insert(muf->RealPath); + } + if (muf->SkipUic) { + uic_skip.insert(muf->RealPath); + } + if (muf->MocIt || muf->UicIt) { + headers.emplace_back(muf->RealPath); + std::string flags; + flags += muf->MocIt ? 'M' : 'm'; + flags += muf->UicIt ? 'U' : 'u'; + headersFlags.emplace_back(std::move(flags)); + } } - if (muf.MocIt && muf.UicIt) { - headers.insert(muf.RealPath); - } else if (muf.MocIt) { - moc_headers.insert(muf.RealPath); - } else if (muf.UicIt) { - uic_headers.insert(muf.RealPath); + } + // Header build paths + { + cmFilePathChecksum const fpathCheckSum(makefile); + std::unordered_set<std::string> emitted; + for (std::string const& hdr : headers) { + std::string basePath = fpathCheckSum.getPart(hdr); + basePath += "/moc_"; + basePath += cmSystemTools::GetFilenameWithoutLastExtension(hdr); + for (unsigned int ii = 1; ii != 1024; ++ii) { + std::string path = basePath; + if (ii > 1) { + path += '_'; + path += std::to_string(ii); + } + path += ".cpp"; + if (emitted.emplace(path).second) { + headersBuildPaths.emplace_back(std::move(path)); + break; + } + } } } + // Filter sources - for (auto const& pair : this->AutogenTarget.Sources) { - MUFile const& muf = *pair.second; - if (muf.Generated && !this->CMP0071Accept) { - continue; - } - if (muf.SkipMoc) { - moc_skip.insert(muf.RealPath); - } - if (muf.SkipUic) { - uic_skip.insert(muf.RealPath); + { + auto sourcesCount = this->AutogenTarget.Sources.size(); + sources.reserve(sourcesCount); + sourcesFlags.reserve(sourcesCount); + + std::vector<MUFile const*> sorted; + sorted.reserve(sourcesCount); + for (auto const& pair : this->AutogenTarget.Sources) { + sorted.emplace_back(pair.second.get()); } - if (muf.MocIt && muf.UicIt) { - sources.insert(muf.RealPath); - } else if (muf.MocIt) { - moc_sources.insert(muf.RealPath); - } else if (muf.UicIt) { - uic_sources.insert(muf.RealPath); + std::sort(sorted.begin(), sorted.end(), + [](MUFile const* a, MUFile const* b) { + return (a->RealPath < b->RealPath); + }); + + for (MUFile const* const muf : sorted) { + if (muf->Generated && !this->CMP0071Accept) { + continue; + } + if (muf->SkipMoc) { + moc_skip.insert(muf->RealPath); + } + if (muf->SkipUic) { + uic_skip.insert(muf->RealPath); + } + if (muf->MocIt || muf->UicIt) { + sources.emplace_back(muf->RealPath); + std::string flags; + flags += muf->MocIt ? 'M' : 'm'; + flags += muf->UicIt ? 'U' : 'u'; + sourcesFlags.emplace_back(std::move(flags)); + } } } @@ -1311,17 +1371,20 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() ofs.Write("AM_QT_UIC_EXECUTABLE", this->Uic.Executable); ofs.Write("# Files\n"); + ofs.Write("AM_CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand()); ofs.Write("AM_SETTINGS_FILE", this->AutogenTarget.SettingsFile); ofs.WriteConfig("AM_SETTINGS_FILE", this->AutogenTarget.ConfigSettingsFile); + ofs.Write("AM_PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile); ofs.WriteStrings("AM_HEADERS", headers); + ofs.WriteStrings("AM_HEADERS_FLAGS", headersFlags); + ofs.WriteStrings("AM_HEADERS_BUILD_PATHS", headersBuildPaths); ofs.WriteStrings("AM_SOURCES", sources); + ofs.WriteStrings("AM_SOURCES_FLAGS", sourcesFlags); // Write moc settings if (this->Moc.Enabled) { ofs.Write("# MOC settings\n"); - ofs.WriteStrings("AM_MOC_HEADERS", moc_headers); - ofs.WriteStrings("AM_MOC_SOURCES", moc_sources); ofs.WriteStrings("AM_MOC_SKIP", moc_skip); ofs.WriteStrings("AM_MOC_DEFINITIONS", this->Moc.Defines); ofs.WriteConfigStrings("AM_MOC_DEFINITIONS", this->Moc.ConfigDefines); @@ -1343,8 +1406,6 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end()); ofs.Write("# UIC settings\n"); - ofs.WriteStrings("AM_UIC_HEADERS", uic_headers); - ofs.WriteStrings("AM_UIC_SOURCES", uic_sources); ofs.WriteStrings("AM_UIC_SKIP", uic_skip); ofs.WriteStrings("AM_UIC_TARGET_OPTIONS", this->Uic.Options); ofs.WriteConfigStrings("AM_UIC_TARGET_OPTIONS", this->Uic.ConfigOptions); diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h index 7ff33c3..153f56d 100644 --- a/Source/cmQtAutoGenInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -183,6 +183,7 @@ private: // Configuration files std::string InfoFile; std::string SettingsFile; + std::string ParseCacheFile; std::map<std::string, std::string> ConfigSettingsFile; // Dependencies bool DependOrigin = false; diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx index 6fbea82..e1c435b 100644 --- a/Source/cmQtAutoGenerator.cxx +++ b/Source/cmQtAutoGenerator.cxx @@ -66,7 +66,8 @@ std::string cmQtAutoGenerator::Logger::HeadLine(std::string const& title) return head; } -void cmQtAutoGenerator::Logger::Info(GenT genType, std::string const& message) +void cmQtAutoGenerator::Logger::Info(GenT genType, + std::string const& message) const { std::string msg = GeneratorName(genType); msg += ": "; @@ -81,7 +82,7 @@ void cmQtAutoGenerator::Logger::Info(GenT genType, std::string const& message) } void cmQtAutoGenerator::Logger::Warning(GenT genType, - std::string const& message) + std::string const& message) const { std::string msg; if (message.find('\n') == std::string::npos) { @@ -106,7 +107,7 @@ void cmQtAutoGenerator::Logger::Warning(GenT genType, void cmQtAutoGenerator::Logger::WarningFile(GenT genType, std::string const& filename, - std::string const& message) + std::string const& message) const { std::string msg = " "; msg += Quoted(filename); @@ -116,7 +117,8 @@ void cmQtAutoGenerator::Logger::WarningFile(GenT genType, Warning(genType, msg); } -void cmQtAutoGenerator::Logger::Error(GenT genType, std::string const& message) +void cmQtAutoGenerator::Logger::Error(GenT genType, + std::string const& message) const { std::string msg; msg += HeadLine(GeneratorName(genType) + " error"); @@ -134,7 +136,7 @@ void cmQtAutoGenerator::Logger::Error(GenT genType, std::string const& message) void cmQtAutoGenerator::Logger::ErrorFile(GenT genType, std::string const& filename, - std::string const& message) + std::string const& message) const { std::string emsg = " "; emsg += Quoted(filename); @@ -146,7 +148,7 @@ void cmQtAutoGenerator::Logger::ErrorFile(GenT genType, void cmQtAutoGenerator::Logger::ErrorCommand( GenT genType, std::string const& message, - std::vector<std::string> const& command, std::string const& output) + std::vector<std::string> const& command, std::string const& output) const { std::string msg; msg.push_back('\n'); @@ -191,7 +193,7 @@ bool cmQtAutoGenerator::FileRead(std::string& content, content.clear(); if (!cmSystemTools::FileExists(filename, true)) { if (error != nullptr) { - error->append("Not a file."); + *error = "Not a file."; } return false; } @@ -203,7 +205,7 @@ bool cmQtAutoGenerator::FileRead(std::string& content, return [&ifs, length, &content, error]() -> bool { if (!ifs) { if (error != nullptr) { - error->append("Opening the file for reading failed."); + *error = "Opening the file for reading failed."; } return false; } @@ -213,7 +215,7 @@ bool cmQtAutoGenerator::FileRead(std::string& content, if (!ifs) { content.clear(); if (error != nullptr) { - error->append("Reading from the file failed."); + *error = "Reading from the file failed."; } return false; } @@ -228,7 +230,7 @@ bool cmQtAutoGenerator::FileWrite(std::string const& filename, // Make sure the parent directory exists if (!cmQtAutoGenerator::MakeParentDirectory(filename)) { if (error != nullptr) { - error->assign("Could not create parent directory."); + *error = "Could not create parent directory."; } return false; } @@ -240,14 +242,14 @@ bool cmQtAutoGenerator::FileWrite(std::string const& filename, return [&ofs, &content, error]() -> bool { if (!ofs) { if (error != nullptr) { - error->assign("Opening file for writing failed."); + *error = "Opening file for writing failed."; } return false; } ofs << content; if (!ofs.good()) { if (error != nullptr) { - error->assign("File writing failed."); + *error = "File writing failed."; } return false; } @@ -255,176 +257,17 @@ bool cmQtAutoGenerator::FileWrite(std::string const& filename, }(); } -cmQtAutoGenerator::FileSystem::FileSystem() = default; - -cmQtAutoGenerator::FileSystem::~FileSystem() = default; - -std::string cmQtAutoGenerator::FileSystem::GetRealPath( - std::string const& filename) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmSystemTools::GetRealPath(filename); -} - -std::string cmQtAutoGenerator::FileSystem::CollapseFullPath( - std::string const& file, std::string const& dir) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmSystemTools::CollapseFullPath(file, dir); -} - -void cmQtAutoGenerator::FileSystem::SplitPath( - const std::string& p, std::vector<std::string>& components, - bool expand_home_dir) -{ - std::lock_guard<std::mutex> lock(Mutex_); - cmSystemTools::SplitPath(p, components, expand_home_dir); -} - -std::string cmQtAutoGenerator::FileSystem::JoinPath( - const std::vector<std::string>& components) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmSystemTools::JoinPath(components); -} - -std::string cmQtAutoGenerator::FileSystem::JoinPath( - std::vector<std::string>::const_iterator first, - std::vector<std::string>::const_iterator last) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmSystemTools::JoinPath(first, last); -} - -std::string cmQtAutoGenerator::FileSystem::GetFilenameWithoutLastExtension( - const std::string& filename) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmSystemTools::GetFilenameWithoutLastExtension(filename); -} - -std::string cmQtAutoGenerator::FileSystem::SubDirPrefix( - std::string const& filename) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmQtAutoGen::SubDirPrefix(filename); -} - -void cmQtAutoGenerator::FileSystem::setupFilePathChecksum( - std::string const& currentSrcDir, std::string const& currentBinDir, - std::string const& projectSrcDir, std::string const& projectBinDir) -{ - std::lock_guard<std::mutex> lock(Mutex_); - FilePathChecksum_.setupParentDirs(currentSrcDir, currentBinDir, - projectSrcDir, projectBinDir); -} - -std::string cmQtAutoGenerator::FileSystem::GetFilePathChecksum( - std::string const& filename) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return FilePathChecksum_.getPart(filename); -} - -bool cmQtAutoGenerator::FileSystem::FileExists(std::string const& filename) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmSystemTools::FileExists(filename); -} - -bool cmQtAutoGenerator::FileSystem::FileExists(std::string const& filename, - bool isFile) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmSystemTools::FileExists(filename, isFile); -} - -unsigned long cmQtAutoGenerator::FileSystem::FileLength( - std::string const& filename) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmSystemTools::FileLength(filename); -} - -bool cmQtAutoGenerator::FileSystem::FileIsOlderThan( - std::string const& buildFile, std::string const& sourceFile, - std::string* error) -{ - bool res(false); - int result = 0; - { - std::lock_guard<std::mutex> lock(Mutex_); - res = cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result); - } - if (res) { - res = (result < 0); - } else { - if (error != nullptr) { - error->append( - "File modification time comparison failed for the files\n "); - error->append(Quoted(buildFile)); - error->append("\nand\n "); - error->append(Quoted(sourceFile)); - } - } - return res; -} - -bool cmQtAutoGenerator::FileSystem::FileRead(std::string& content, - std::string const& filename, - std::string* error) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmQtAutoGenerator::FileRead(content, filename, error); -} - -bool cmQtAutoGenerator::FileSystem::FileWrite(std::string const& filename, - std::string const& content, - std::string* error) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmQtAutoGenerator::FileWrite(filename, content, error); -} - -bool cmQtAutoGenerator::FileSystem::FileDiffers(std::string const& filename, - std::string const& content) +bool cmQtAutoGenerator::FileDiffers(std::string const& filename, + std::string const& content) { bool differs = true; - { - std::string oldContents; - if (FileRead(oldContents, filename)) { - differs = (oldContents != content); - } + std::string oldContents; + if (FileRead(oldContents, filename) && (oldContents == content)) { + differs = false; } return differs; } -bool cmQtAutoGenerator::FileSystem::FileRemove(std::string const& filename) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmSystemTools::RemoveFile(filename); -} - -bool cmQtAutoGenerator::FileSystem::Touch(std::string const& filename, - bool create) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmSystemTools::Touch(filename, create); -} - -bool cmQtAutoGenerator::FileSystem::MakeDirectory(std::string const& dirname) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmSystemTools::MakeDirectory(dirname); -} - -bool cmQtAutoGenerator::FileSystem::MakeParentDirectory( - std::string const& filename) -{ - std::lock_guard<std::mutex> lock(Mutex_); - return cmQtAutoGenerator::MakeParentDirectory(filename); -} - cmQtAutoGenerator::cmQtAutoGenerator() = default; cmQtAutoGenerator::~cmQtAutoGenerator() = default; @@ -435,6 +278,13 @@ bool cmQtAutoGenerator::Run(std::string const& infoFile, // Info settings InfoFile_ = infoFile; cmSystemTools::ConvertToUnixSlashes(InfoFile_); + if (!InfoFileTime_.Load(InfoFile_)) { + std::string msg = "AutoGen: The info file "; + msg += Quoted(InfoFile_); + msg += " is not readable\n"; + cmSystemTools::Stderr(msg); + return false; + } InfoDir_ = cmSystemTools::GetFilenamePath(infoFile); InfoConfig_ = config; diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h index 437fa20..ff4c4c9 100644 --- a/Source/cmQtAutoGenerator.h +++ b/Source/cmQtAutoGenerator.h @@ -5,7 +5,7 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include "cmFilePathChecksum.h" +#include "cmFileTime.h" #include "cmQtAutoGen.h" #include <mutex> @@ -14,13 +14,17 @@ class cmMakefile; -/// @brief Base class for QtAutoGen gernerators +/** \class cmQtAutoGenerator + * \brief Base class for QtAutoGen generators + */ class cmQtAutoGenerator : public cmQtAutoGen { public: // -- Types - /// @brief Thread safe logging + /** + * Thread safe logger + */ class Logger { public: @@ -37,24 +41,24 @@ public: bool ColorOutput() const { return this->ColorOutput_; } void SetColorOutput(bool value); // -- Log info - void Info(GenT genType, std::string const& message); + void Info(GenT genType, std::string const& message) const; // -- Log warning - void Warning(GenT genType, std::string const& message); + void Warning(GenT genType, std::string const& message) const; void WarningFile(GenT genType, std::string const& filename, - std::string const& message); + std::string const& message) const; // -- Log error - void Error(GenT genType, std::string const& message); + void Error(GenT genType, std::string const& message) const; void ErrorFile(GenT genType, std::string const& filename, - std::string const& message); + std::string const& message) const; void ErrorCommand(GenT genType, std::string const& message, std::vector<std::string> const& command, - std::string const& output); + std::string const& output) const; private: static std::string HeadLine(std::string const& title); private: - std::mutex Mutex_; + mutable std::mutex Mutex_; unsigned int Verbosity_ = 0; bool ColorOutput_ = false; }; @@ -66,70 +70,8 @@ public: static bool FileWrite(std::string const& filename, std::string const& content, std::string* error = nullptr); - - /// @brief Thread safe file system interface - class FileSystem - { - public: - FileSystem(); - ~FileSystem(); - - // -- Paths - /// @brief Wrapper for cmSystemTools::GetRealPath - std::string GetRealPath(std::string const& filename); - /// @brief Wrapper for cmSystemTools::CollapseFullPath - std::string CollapseFullPath(std::string const& file, - std::string const& dir); - /// @brief Wrapper for cmSystemTools::SplitPath - void SplitPath(const std::string& p, std::vector<std::string>& components, - bool expand_home_dir = true); - /// @brief Wrapper for cmSystemTools::JoinPath - std::string JoinPath(const std::vector<std::string>& components); - /// @brief Wrapper for cmSystemTools::JoinPath - std::string JoinPath(std::vector<std::string>::const_iterator first, - std::vector<std::string>::const_iterator last); - /// @brief Wrapper for cmSystemTools::GetFilenameWithoutLastExtension - std::string GetFilenameWithoutLastExtension(const std::string& filename); - /// @brief Wrapper for cmQtAutoGen::SubDirPrefix - std::string SubDirPrefix(std::string const& filename); - /// @brief Wrapper for cmFilePathChecksum::setupParentDirs - void setupFilePathChecksum(std::string const& currentSrcDir, - std::string const& currentBinDir, - std::string const& projectSrcDir, - std::string const& projectBinDir); - /// @brief Wrapper for cmFilePathChecksum::getPart - std::string GetFilePathChecksum(std::string const& filename); - - // -- File access - /// @brief Wrapper for cmSystemTools::FileExists - bool FileExists(std::string const& filename); - /// @brief Wrapper for cmSystemTools::FileExists - bool FileExists(std::string const& filename, bool isFile); - /// @brief Wrapper for cmSystemTools::FileLength - unsigned long FileLength(std::string const& filename); - bool FileIsOlderThan(std::string const& buildFile, - std::string const& sourceFile, - std::string* error = nullptr); - - bool FileRead(std::string& content, std::string const& filename, - std::string* error = nullptr); - - bool FileWrite(std::string const& filename, std::string const& content, - std::string* error = nullptr); - - bool FileDiffers(std::string const& filename, std::string const& content); - - bool FileRemove(std::string const& filename); - bool Touch(std::string const& filename, bool create = false); - - // -- Directory access - bool MakeDirectory(std::string const& dirname); - bool MakeParentDirectory(std::string const& filename); - - private: - std::mutex Mutex_; - cmFilePathChecksum FilePathChecksum_; - }; + static bool FileDiffers(std::string const& filename, + std::string const& content); public: // -- Constructors @@ -142,8 +84,9 @@ public: // -- Run bool Run(std::string const& infoFile, std::string const& config); - // InfoFile + // -- InfoFile std::string const& InfoFile() const { return InfoFile_; } + cmFileTime const& InfoFileTime() const { return InfoFileTime_; } std::string const& InfoDir() const { return InfoDir_; } std::string const& InfoConfig() const { return InfoConfig_; } @@ -158,6 +101,7 @@ protected: private: // -- Info settings std::string InfoFile_; + cmFileTime InfoFileTime_; std::string InfoDir_; std::string InfoConfig_; }; diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 005c27d..36794d6 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -4,7 +4,6 @@ #include <algorithm> #include <array> -#include <deque> #include <list> #include <memory> #include <set> @@ -13,67 +12,187 @@ #include "cmAlgorithms.h" #include "cmCryptoHash.h" +#include "cmGeneratedFileStream.h" #include "cmMakefile.h" #include "cmQtAutoGen.h" #include "cmSystemTools.h" #include "cmake.h" +#include "cmsys/FStream.hxx" #if defined(__APPLE__) # include <unistd.h> #endif -// -- Class methods +static constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_" +static constexpr std::size_t UiUnderscoreLength = 3; // Length of "ui_" -std::string cmQtAutoMocUic::BaseSettingsT::AbsoluteBuildPath( - std::string const& relativePath) const +cmQtAutoMocUic::IncludeKeyT::IncludeKeyT(std::string const& key, + std::size_t basePrefixLength) + : Key(key) + , Dir(SubDirPrefix(key)) + , Base(cmSystemTools::GetFilenameWithoutLastExtension(key)) { - return FileSys->CollapseFullPath(relativePath, AutogenBuildDir); + if (basePrefixLength != 0) { + Base = Base.substr(basePrefixLength); + } } -/** - * @brief Tries to find the header file to the given file base path by - * appending different header extensions - * @return True on success - */ -bool cmQtAutoMocUic::BaseSettingsT::FindHeader( - std::string& header, std::string const& testBasePath) const +void cmQtAutoMocUic::ParseCacheT::FileT::Clear() { - for (std::string const& ext : HeaderExtensions) { - std::string testFilePath(testBasePath); - testFilePath.push_back('.'); - testFilePath += ext; - if (FileSys->FileExists(testFilePath)) { - header = testFilePath; - return true; + Moc.Macro.clear(); + Moc.Include.Underscore.clear(); + Moc.Include.Dot.clear(); + Moc.Depends.clear(); + + Uic.Include.clear(); + Uic.Depends.clear(); +} + +cmQtAutoMocUic::ParseCacheT::FileHandleT cmQtAutoMocUic::ParseCacheT::Get( + std::string const& fileName) const +{ + auto it = Map_.find(fileName); + if (it != Map_.end()) { + return it->second; + } + return FileHandleT(); +} + +cmQtAutoMocUic::ParseCacheT::GetOrInsertT +cmQtAutoMocUic::ParseCacheT::GetOrInsert(std::string const& fileName) +{ + // Find existing entry + { + auto it = Map_.find(fileName); + if (it != Map_.end()) { + return GetOrInsertT{ it->second, false }; } } - return false; + + // Insert new entry + return GetOrInsertT{ + Map_.emplace(fileName, std::make_shared<FileT>()).first->second, true + }; } -bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const +cmQtAutoMocUic::ParseCacheT::ParseCacheT() = default; +cmQtAutoMocUic::ParseCacheT::~ParseCacheT() = default; + +void cmQtAutoMocUic::ParseCacheT::Clear() { - return (!Enabled || (SkipList.find(fileName) != SkipList.end())); + Map_.clear(); } -/** - * @brief Returns the first relevant Qt macro name found in the given C++ code - * @return The name of the Qt macro or an empty string - */ -std::string cmQtAutoMocUic::MocSettingsT::FindMacro( - std::string const& content) const +bool cmQtAutoMocUic::ParseCacheT::ReadFromFile(std::string const& fileName) { - for (KeyExpT const& filter : MacroFilters) { - // Run a simple find string operation before the expensive - // regular expression check - if (content.find(filter.Key) != std::string::npos) { - cmsys::RegularExpressionMatch match; - if (filter.Exp.find(content.c_str(), match)) { - // Return macro name on demand - return filter.Key; + cmsys::ifstream fin(fileName.c_str()); + if (!fin) { + return false; + } + FileHandleT fileHandle; + + std::string line; + while (std::getline(fin, line)) { + // Check if this an empty or a comment line + if (line.empty() || line.front() == '#') { + continue; + } + // Drop carriage return character at the end + if (line.back() == '\r') { + line.pop_back(); + if (line.empty()) { + continue; } } + // Check if this a file name line + if (line.front() != ' ') { + fileHandle = GetOrInsert(line).first; + continue; + } + + // Bad line or bad file handle + if (!fileHandle || (line.size() < 6)) { + continue; + } + + constexpr std::size_t offset = 5; + if (cmHasLiteralPrefix(line, " mmc:")) { + fileHandle->Moc.Macro = line.substr(offset); + continue; + } + if (cmHasLiteralPrefix(line, " miu:")) { + fileHandle->Moc.Include.Underscore.emplace_back(line.substr(offset), + MocUnderscoreLength); + continue; + } + if (cmHasLiteralPrefix(line, " mid:")) { + fileHandle->Moc.Include.Dot.emplace_back(line.substr(offset), 0); + continue; + } + if (cmHasLiteralPrefix(line, " mdp:")) { + fileHandle->Moc.Depends.emplace_back(line.substr(offset)); + continue; + } + if (cmHasLiteralPrefix(line, " uic:")) { + fileHandle->Uic.Include.emplace_back(line.substr(offset), + UiUnderscoreLength); + continue; + } + if (cmHasLiteralPrefix(line, " udp:")) { + fileHandle->Uic.Depends.emplace_back(line.substr(offset)); + continue; + } + } + return true; +} + +bool cmQtAutoMocUic::ParseCacheT::WriteToFile(std::string const& fileName) +{ + cmGeneratedFileStream ofs(fileName); + if (!ofs) { + return false; + } + ofs << "# Generated by CMake. Changes will be overwritten." << std::endl; + for (auto const& pair : Map_) { + ofs << pair.first << std::endl; + FileT const& file = *pair.second; + if (!file.Moc.Macro.empty()) { + ofs << " mmc:" << file.Moc.Macro << std::endl; + } + for (IncludeKeyT const& item : file.Moc.Include.Underscore) { + ofs << " miu:" << item.Key << std::endl; + } + for (IncludeKeyT const& item : file.Moc.Include.Dot) { + ofs << " mid:" << item.Key << std::endl; + } + for (std::string const& item : file.Moc.Depends) { + ofs << " mdp:" << item << std::endl; + } + for (IncludeKeyT const& item : file.Uic.Include) { + ofs << " uic:" << item.Key << std::endl; + } + for (std::string const& item : file.Uic.Depends) { + ofs << " udp:" << item << std::endl; + } } - return std::string(); + return ofs.Close(); +} + +cmQtAutoMocUic::BaseSettingsT::BaseSettingsT() = default; +cmQtAutoMocUic::BaseSettingsT::~BaseSettingsT() = default; + +cmQtAutoMocUic::MocSettingsT::MocSettingsT() +{ + RegExpInclude.compile( + "(^|\n)[ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); +} + +cmQtAutoMocUic::MocSettingsT::~MocSettingsT() = default; + +bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const +{ + return (!Enabled || (SkipList.find(fileName) != SkipList.end())); } std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const @@ -98,53 +217,13 @@ std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const return res; } -std::string cmQtAutoMocUic::MocSettingsT::FindIncludedFile( - std::string const& sourcePath, std::string const& includeString) const +cmQtAutoMocUic::UicSettingsT::UicSettingsT() { - // Search in vicinity of the source - { - std::string testPath = sourcePath; - testPath += includeString; - if (FileSys->FileExists(testPath)) { - return FileSys->GetRealPath(testPath); - } - } - // Search in include directories - for (std::string const& path : IncludePaths) { - std::string fullPath = path; - fullPath.push_back('/'); - fullPath += includeString; - if (FileSys->FileExists(fullPath)) { - return FileSys->GetRealPath(fullPath); - } - } - // Return empty string - return std::string(); + RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); } -void cmQtAutoMocUic::MocSettingsT::FindDependencies( - std::string const& content, std::set<std::string>& depends) const -{ - if (!DependFilters.empty() && !content.empty()) { - for (KeyExpT const& filter : DependFilters) { - // Run a simple find string check - if (content.find(filter.Key) != std::string::npos) { - // Run the expensive regular expression check loop - const char* contentChars = content.c_str(); - cmsys::RegularExpressionMatch match; - while (filter.Exp.find(contentChars, match)) { - { - std::string dep = match.match(1); - if (!dep.empty()) { - depends.emplace(std::move(dep)); - } - } - contentChars += match.end(); - } - } - } - } -} +cmQtAutoMocUic::UicSettingsT::~UicSettingsT() = default; bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const { @@ -176,498 +255,844 @@ void cmQtAutoMocUic::JobT::LogCommandError( bool cmQtAutoMocUic::JobT::RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, - std::vector<std::string> const& command) + std::vector<std::string> const& command, + std::string* infoMessage) { // Log command if (Log().Verbose()) { - std::string msg = "Running command:\n"; + std::string msg; + if ((infoMessage != nullptr) && !infoMessage->empty()) { + msg = *infoMessage; + if (msg.back() != '\n') { + msg += '\n'; + } + } msg += QuotedCommand(command); msg += '\n'; Log().Info(genType, msg); } return cmWorkerPool::JobT::RunProcess(result, command, - Gen()->Base().AutogenBuildDir); + BaseConst().AutogenBuildDir); } void cmQtAutoMocUic::JobMocPredefsT::Process() { // (Re)generate moc_predefs.h on demand - bool generate(false); - bool fileExists(FileSys().FileExists(Gen()->Moc().PredefsFileAbs)); - if (!fileExists) { - if (Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(Gen()->Moc().PredefsFileRel); - reason += " because it doesn't exist"; - Log().Info(GenT::MOC, reason); - } - generate = true; - } else if (Gen()->Moc().SettingsChanged) { - if (Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(Gen()->Moc().PredefsFileRel); - reason += " because the settings changed."; - Log().Info(GenT::MOC, reason); - } - generate = true; + std::unique_ptr<std::string> reason; + if (Log().Verbose()) { + reason = cm::make_unique<std::string>(); + } + if (!Update(reason.get())) { + return; } - if (generate) { + std::string const& predefsFileRel = MocConst().PredefsFileRel; + std::string const& predefsFileAbs = MocConst().PredefsFileAbs; + { cmWorkerPool::ProcessResultT result; { // Compose command - std::vector<std::string> cmd = Gen()->Moc().PredefsCmd; + std::vector<std::string> cmd = MocConst().PredefsCmd; // Add includes - cmd.insert(cmd.end(), Gen()->Moc().Includes.begin(), - Gen()->Moc().Includes.end()); + cmd.insert(cmd.end(), MocConst().Includes.begin(), + MocConst().Includes.end()); // Add definitions - for (std::string const& def : Gen()->Moc().Definitions) { + for (std::string const& def : MocConst().Definitions) { cmd.push_back("-D" + def); } // Execute command - if (!RunProcess(GenT::MOC, result, cmd)) { - std::string emsg = "The content generation command for "; - emsg += Quoted(Gen()->Moc().PredefsFileRel); - emsg += " failed.\n"; - emsg += result.ErrorMessage; - LogCommandError(GenT::MOC, emsg, cmd, result.StdOut); + if (!RunProcess(GenT::MOC, result, cmd, reason.get())) { + std::string msg = "The content generation command for "; + msg += Quoted(predefsFileRel); + msg += " failed.\n"; + msg += result.ErrorMessage; + LogCommandError(GenT::MOC, msg, cmd, result.StdOut); + return; } } // (Re)write predefs file only on demand - if (!result.error()) { - if (!fileExists || - FileSys().FileDiffers(Gen()->Moc().PredefsFileAbs, result.StdOut)) { - if (FileSys().FileWrite(Gen()->Moc().PredefsFileAbs, result.StdOut)) { - // Success - } else { - std::string emsg = "Writing "; - emsg += Quoted(Gen()->Moc().PredefsFileRel); - emsg += " failed."; - LogFileError(GenT::MOC, Gen()->Moc().PredefsFileAbs, emsg); - } - } else { - // Touch to update the time stamp - if (Log().Verbose()) { - std::string msg = "Touching "; - msg += Quoted(Gen()->Moc().PredefsFileRel); - msg += "."; - Log().Info(GenT::MOC, msg); - } - FileSys().Touch(Gen()->Moc().PredefsFileAbs); + if (cmQtAutoGenerator::FileDiffers(predefsFileAbs, result.StdOut)) { + if (!cmQtAutoGenerator::FileWrite(predefsFileAbs, result.StdOut)) { + std::string msg = "Writing "; + msg += Quoted(predefsFileRel); + msg += " failed."; + LogFileError(GenT::MOC, predefsFileAbs, msg); + return; + } + } else { + // Touch to update the time stamp + if (Log().Verbose()) { + Log().Info(GenT::MOC, "Touching " + Quoted(predefsFileRel)); + } + if (!cmSystemTools::Touch(predefsFileAbs, false)) { + std::string msg = "Touching "; + msg += Quoted(predefsFileAbs); + msg += " failed."; + LogFileError(GenT::MOC, predefsFileAbs, msg); + return; } } } + + // Read file time afterwards + if (!MocEval().PredefsTime.Load(predefsFileAbs)) { + LogFileError(GenT::MOC, predefsFileAbs, "File time reading failed."); + return; + } } -void cmQtAutoMocUic::JobParseT::Process() +bool cmQtAutoMocUic::JobMocPredefsT::Update(std::string* reason) const { - if (AutoMoc && Header) { - // Don't parse header for moc if the file is included by a source already - if (Gen()->ParallelMocIncluded(FileName)) { - AutoMoc = false; + // Test if the file exists + if (!MocEval().PredefsTime.Load(MocConst().PredefsFileAbs)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(MocConst().PredefsFileRel); + *reason += ", because it doesn't exist."; } + return true; } - if (AutoMoc || AutoUic) { - std::string error; - MetaT meta; - if (FileSys().FileRead(meta.Content, FileName, &error)) { - if (!meta.Content.empty()) { - meta.FileDir = FileSys().SubDirPrefix(FileName); - meta.FileBase = FileSys().GetFilenameWithoutLastExtension(FileName); - - bool success = true; - if (AutoMoc) { - if (Header) { - success = ParseMocHeader(meta); - } else { - success = ParseMocSource(meta); - } - } - if (AutoUic && success) { - ParseUic(meta); + // Test if the settings changed + if (MocConst().SettingsChanged) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(MocConst().PredefsFileRel); + *reason += ", because the moc settings changed."; + } + return true; + } + + // Test if the executable is newer + { + std::string const& exec = MocConst().PredefsCmd.at(0); + cmFileTime execTime; + if (execTime.Load(exec)) { + if (MocEval().PredefsTime.Older(execTime)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(MocConst().PredefsFileRel); + *reason += " because it is older than "; + *reason += Quoted(exec); + *reason += "."; } - } else { - Log().WarningFile(GenT::GEN, FileName, "The source file is empty"); + return true; } - } else { - LogFileError(GenT::GEN, FileName, "Could not read the file: " + error); } } + + return false; } -bool cmQtAutoMocUic::JobParseT::ParseMocSource(MetaT const& meta) +bool cmQtAutoMocUic::JobParseT::ReadFile() { - struct JobPre + // Clear old parse information + FileHandle->ParseData->Clear(); + std::string const& fileName = FileHandle->FileName; + // Write info + if (Log().Verbose()) { + Log().Info(GenT::GEN, "Parsing " + Quoted(fileName)); + } + // Read file content { - bool self; // source file is self - bool underscore; // "moc_" style include - std::string SourceFile; - std::string IncludeString; - }; + std::string error; + if (!cmQtAutoGenerator::FileRead(Content, fileName, &error)) { + LogFileError(GenT::GEN, fileName, "Could not read the file: " + error); + return false; + } + } + // Warn if empty + if (Content.empty()) { + Log().WarningFile(GenT::GEN, fileName, "The file is empty."); + return false; + } + return true; +} + +void cmQtAutoMocUic::JobParseT::CreateKeys(std::vector<IncludeKeyT>& container, + std::set<std::string> const& source, + std::size_t basePrefixLength) +{ + if (source.empty()) { + return; + } + container.reserve(source.size()); + for (std::string const& src : source) { + container.emplace_back(src, basePrefixLength); + } +} + +void cmQtAutoMocUic::JobParseT::MocMacro() +{ + for (KeyExpT const& filter : MocConst().MacroFilters) { + // Run a simple find string check + if (Content.find(filter.Key) == std::string::npos) { + continue; + } + // Run the expensive regular expression check loop + cmsys::RegularExpressionMatch match; + if (filter.Exp.find(Content.c_str(), match)) { + // Keep detected macro name + FileHandle->ParseData->Moc.Macro = filter.Key; + return; + } + } +} + +void cmQtAutoMocUic::JobParseT::MocDependecies() +{ + if (MocConst().DependFilters.empty()) { + return; + } - struct MocInclude + // Find dependency strings + std::set<std::string> parseDepends; + for (KeyExpT const& filter : MocConst().DependFilters) { + // Run a simple find string check + if (Content.find(filter.Key) == std::string::npos) { + continue; + } + // Run the expensive regular expression check loop + const char* contentChars = Content.c_str(); + cmsys::RegularExpressionMatch match; + while (filter.Exp.find(contentChars, match)) { + { + std::string dep = match.match(1); + if (!dep.empty()) { + parseDepends.emplace(std::move(dep)); + } + } + contentChars += match.end(); + } + } + + // Store dependency strings { - std::string Inc; // full include string - std::string Dir; // include string directory - std::string Base; // include string file base - }; + auto& Depends = FileHandle->ParseData->Moc.Depends; + Depends.reserve(parseDepends.size()); + for (std::string const& item : parseDepends) { + Depends.emplace_back(item); + // Replace end of line characters in filenames + std::string& path = Depends.back(); + std::replace(path.begin(), path.end(), '\n', ' '); + std::replace(path.begin(), path.end(), '\r', ' '); + } + } +} - // Check if this source file contains a relevant macro - std::string const ownMacro = Gen()->Moc().FindMacro(meta.Content); +void cmQtAutoMocUic::JobParseT::MocIncludes() +{ + if (Content.find("moc") == std::string::npos) { + return; + } - // Extract moc includes from file - std::deque<MocInclude> mocIncsUsc; - std::deque<MocInclude> mocIncsDot; + std::set<std::string> underscore; + std::set<std::string> dot; { - if (meta.Content.find("moc") != std::string::npos) { - const char* contentChars = meta.Content.c_str(); - cmsys::RegularExpressionMatch match; - while (Gen()->Moc().RegExpInclude.find(contentChars, match)) { - std::string incString = match.match(2); - std::string incDir(FileSys().SubDirPrefix(incString)); - std::string incBase = - FileSys().GetFilenameWithoutLastExtension(incString); - if (cmHasLiteralPrefix(incBase, "moc_")) { - // moc_<BASE>.cxx - // Remove the moc_ part from the base name - mocIncsUsc.emplace_back(MocInclude{ - std::move(incString), std::move(incDir), incBase.substr(4) }); - } else { - // <BASE>.moc - mocIncsDot.emplace_back(MocInclude{ - std::move(incString), std::move(incDir), std::move(incBase) }); - } - // Forward content pointer - contentChars += match.end(); + const char* contentChars = Content.c_str(); + cmsys::RegularExpression const& regExp = MocConst().RegExpInclude; + cmsys::RegularExpressionMatch match; + while (regExp.find(contentChars, match)) { + std::string incString = match.match(2); + std::string const incBase = + cmSystemTools::GetFilenameWithoutLastExtension(incString); + if (cmHasLiteralPrefix(incBase, "moc_")) { + // moc_<BASE>.cpp + // Remove the moc_ part from the base name + underscore.emplace(std::move(incString)); + } else { + // <BASE>.moc + dot.emplace(std::move(incString)); + } + // Forward content pointer + contentChars += match.end(); + } + } + auto& Include = FileHandle->ParseData->Moc.Include; + CreateKeys(Include.Underscore, underscore, MocUnderscoreLength); + CreateKeys(Include.Dot, dot, 0); +} + +void cmQtAutoMocUic::JobParseT::UicIncludes() +{ + if (Content.find("ui_") == std::string::npos) { + return; + } + + std::set<std::string> includes; + { + const char* contentChars = Content.c_str(); + cmsys::RegularExpression const& regExp = UicConst().RegExpInclude; + cmsys::RegularExpressionMatch match; + while (regExp.find(contentChars, match)) { + includes.emplace(match.match(2)); + // Forward content pointer + contentChars += match.end(); + } + } + CreateKeys(FileHandle->ParseData->Uic.Include, includes, UiUnderscoreLength); +} + +void cmQtAutoMocUic::JobParseHeaderT::Process() +{ + if (!ReadFile()) { + return; + } + // Moc parsing + if (FileHandle->Moc) { + MocMacro(); + MocDependecies(); + } + // Uic parsing + if (FileHandle->Uic) { + UicIncludes(); + } +} + +void cmQtAutoMocUic::JobParseSourceT::Process() +{ + if (!ReadFile()) { + return; + } + // Moc parsing + if (FileHandle->Moc) { + MocMacro(); + MocDependecies(); + MocIncludes(); + } + // Uic parsing + if (FileHandle->Uic) { + UicIncludes(); + } +} + +void cmQtAutoMocUic::JobEvaluateT::Process() +{ + // Evaluate for moc + if (MocConst().Enabled) { + // Evaluate headers + for (auto const& pair : BaseEval().Headers) { + if (!MocEvalHeader(pair.second)) { + return; + } + } + // Evaluate sources + for (auto const& pair : BaseEval().Sources) { + if (!MocEvalSource(pair.second)) { + return; } } } + // Evaluate for uic + if (UicConst().Enabled) { + if (!UicEval(BaseEval().Headers) || !UicEval(BaseEval().Sources)) { + return; + } + } - // Check if there is anything to do - if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) { + // Add discovered header parse jobs + Gen()->CreateParseJobs<JobParseHeaderT>(MocEval().HeadersDiscovered); + // Add generate job after + Gen()->WorkerPool().EmplaceJob<JobGenerateT>(); +} + +bool cmQtAutoMocUic::JobEvaluateT::MocEvalHeader(SourceFileHandleT source) +{ + SourceFileT const& sourceFile = *source; + auto const& parseData = sourceFile.ParseData->Moc; + if (!source->Moc) { return true; } - bool ownDotMocIncluded = false; - bool ownMocUscIncluded = false; - std::deque<JobPre> jobs; + if (!parseData.Macro.empty()) { + // Create a new mapping + MappingHandleT handle = std::make_shared<MappingT>(); + handle->SourceFile = std::move(source); - // Process moc_<BASE>.cxx includes - for (const MocInclude& mocInc : mocIncsUsc) { - std::string const header = - MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base); - if (!header.empty()) { - // Check if header is skipped - if (Gen()->Moc().skipped(header)) { - continue; + // Absolute build path + if (BaseConst().MultiConfig) { + handle->OutputFile = Gen()->AbsoluteIncludePath(sourceFile.BuildPath); + } else { + handle->OutputFile = Gen()->AbsoluteBuildPath(sourceFile.BuildPath); + } + + // Register mapping in headers map + MocRegisterMapping(handle, true); + } + + return true; +} + +bool cmQtAutoMocUic::JobEvaluateT::MocEvalSource( + SourceFileHandleT const& source) +{ + SourceFileT const& sourceFile = *source; + auto const& parseData = sourceFile.ParseData->Moc; + if (!sourceFile.Moc || + (parseData.Macro.empty() && parseData.Include.Underscore.empty() && + parseData.Include.Dot.empty())) { + return true; + } + + std::string const sourceDir = SubDirPrefix(sourceFile.FileName); + std::string const sourceBase = + cmSystemTools::GetFilenameWithoutLastExtension(sourceFile.FileName); + + // For relaxed mode check if the own "moc_" or ".moc" file is included + bool const relaxedMode = MocConst().RelaxedMode; + bool sourceIncludesMocUnderscore = false; + bool sourceIncludesDotMoc = false; + // Check if the sources own "moc_" or ".moc" file is included + if (relaxedMode) { + for (IncludeKeyT const& incKey : parseData.Include.Underscore) { + if (incKey.Base == sourceBase) { + sourceIncludesMocUnderscore = true; + break; } - // Register moc job - const bool ownMoc = (mocInc.Base == meta.FileBase); - jobs.emplace_back(JobPre{ ownMoc, true, header, mocInc.Inc }); - // Store meta information for relaxed mode - if (ownMoc) { - ownMocUscIncluded = true; + } + } + for (IncludeKeyT const& incKey : parseData.Include.Dot) { + if (incKey.Base == sourceBase) { + sourceIncludesDotMoc = true; + break; + } + } + + // Check if this source needs to be moc processed but doesn't. + if (!sourceIncludesDotMoc && !parseData.Macro.empty() && + !(relaxedMode && sourceIncludesMocUnderscore)) { + { + std::string emsg = "The file contains a "; + emsg += Quoted(parseData.Macro); + emsg += " macro, but does not include "; + emsg += Quoted(sourceBase + ".moc"); + emsg += "!\nConsider to\n - add #include \""; + emsg += sourceBase; + emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; + LogFileError(GenT::MOC, sourceFile.FileName, emsg); + } + return false; + } + + // Evaluate "moc_" includes + for (IncludeKeyT const& incKey : parseData.Include.Underscore) { + std::string const headerBase = incKey.Dir + incKey.Base; + SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase); + if (!header) { + { + std::string msg = "The file includes the moc file "; + msg += Quoted(incKey.Key); + msg += ",\nbut the header could not be found " + "in the following locations\n"; + msg += MocMessageTestHeaders(headerBase); + LogFileError(GenT::MOC, sourceFile.FileName, msg); } - } else { + return false; + } + // The include might be handled differently in relaxed mode + if (relaxedMode && !sourceIncludesDotMoc && !parseData.Macro.empty() && + (incKey.Base == sourceBase)) { + // The <BASE>.cpp file includes a Qt macro but does not include the + // <BASE>.moc file. In this case, the moc_<BASE>.cpp should probably + // be generated from <BASE>.cpp instead of <BASE>.h, because otherwise + // it won't build. But warn, since this is not how it is supposed to be + // used. This is for KDE4 compatibility. { - std::string emsg = "The file includes the moc file "; - emsg += Quoted(mocInc.Inc); - emsg += ", but the header "; - emsg += Quoted(MocStringHeaders(mocInc.Base)); - emsg += " could not be found."; - LogFileError(GenT::MOC, FileName, emsg); + // Issue a warning + std::string msg = "The file contains a "; + msg += Quoted(parseData.Macro); + msg += " macro, but does not include "; + msg += Quoted(sourceBase + ".moc"); + msg += ".\nInstead it includes "; + msg += Quoted(incKey.Key); + msg += ".\nRunning moc on the source\n "; + msg += Quoted(sourceFile.FileName); + msg += "!\nBetter include "; + msg += Quoted(sourceBase + ".moc"); + msg += " for compatibility with strict mode.\n"; + msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"; + Log().WarningFile(GenT::MOC, sourceFile.FileName, msg); } + // Create mapping + if (!MocRegisterIncluded(incKey.Key, source, source, false)) { + return false; + } + continue; + } + + // Check if header is skipped + if (MocConst().skipped(header->FileName)) { + continue; + } + // Create mapping + if (!MocRegisterIncluded(incKey.Key, source, std::move(header), true)) { return false; } } - // Process <BASE>.moc includes - for (const MocInclude& mocInc : mocIncsDot) { - const bool ownMoc = (mocInc.Base == meta.FileBase); - if (Gen()->Moc().RelaxedMode) { - // Relaxed mode - if (!ownMacro.empty() && ownMoc) { - // Add self - jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc }); - ownDotMocIncluded = true; - } else { - // In relaxed mode try to find a header instead but issue a warning. - // This is for KDE4 compatibility - std::string const header = - MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base); - if (!header.empty()) { - // Check if header is skipped - if (Gen()->Moc().skipped(header)) { - continue; - } - // Register moc job - jobs.emplace_back(JobPre{ ownMoc, false, header, mocInc.Inc }); - if (ownMacro.empty()) { - if (ownMoc) { - std::string emsg = "The file includes the moc file "; - emsg += Quoted(mocInc.Inc); - emsg += ", but does not contain a "; - emsg += Gen()->Moc().MacrosString(); - emsg += " macro.\nRunning moc on\n "; - emsg += Quoted(header); - emsg += "!\nBetter include "; - emsg += Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += " for a compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - Log().WarningFile(GenT::MOC, FileName, emsg); - } else { - std::string emsg = "The file includes the moc file "; - emsg += Quoted(mocInc.Inc); - emsg += " instead of "; - emsg += Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += ".\nRunning moc on\n "; - emsg += Quoted(header); - emsg += "!\nBetter include "; - emsg += Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - Log().WarningFile(GenT::MOC, FileName, emsg); - } - } - } else { - { - std::string emsg = "The file includes the moc file "; - emsg += Quoted(mocInc.Inc); - emsg += ", which seems to be the moc file from a different " - "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a " - "matching header "; - emsg += Quoted(MocStringHeaders(mocInc.Base)); - emsg += " could not be found."; - LogFileError(GenT::MOC, FileName, emsg); - } + // Evaluate ".moc" includes + if (relaxedMode) { + // Relaxed mode + for (IncludeKeyT const& incKey : parseData.Include.Dot) { + // Check if this is the sources own .moc file + bool const ownMoc = (incKey.Base == sourceBase); + if (ownMoc && !parseData.Macro.empty()) { + // Create mapping for the regular use case + if (!MocRegisterIncluded(incKey.Key, source, source, false)) { return false; } + continue; } - } else { - // Strict mode - if (ownMoc) { - // Include self - jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc }); - ownDotMocIncluded = true; - // Accept but issue a warning if moc isn't required - if (ownMacro.empty()) { - std::string emsg = "The file includes the moc file "; - emsg += Quoted(mocInc.Inc); - emsg += ", but does not contain a "; - emsg += Gen()->Moc().MacrosString(); - emsg += " macro."; - Log().WarningFile(GenT::MOC, FileName, emsg); - } + // Try to find a header instead but issue a warning. + // This is for KDE4 compatibility. + std::string const headerBase = incKey.Dir + incKey.Base; + SourceFileHandleT header = MocFindIncludedHeader(sourceDir, headerBase); + if (!header) { + std::string msg = "The file includes the moc file "; + msg += Quoted(incKey.Key); + msg += ",\nwhich seems to be the moc file from a different source " + "file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a matching header" + "could not be found in the following locations\n"; + msg += MocMessageTestHeaders(headerBase); + LogFileError(GenT::MOC, sourceFile.FileName, msg); + return false; + } + // Check if header is skipped + if (MocConst().skipped(header->FileName)) { + continue; + } + // Issue a warning + if (ownMoc && parseData.Macro.empty()) { + std::string msg = "The file includes the moc file "; + msg += Quoted(incKey.Key); + msg += ", but does not contain a\n"; + msg += MocConst().MacrosString(); + msg += " macro.\nRunning moc on the header\n "; + msg += Quoted(header->FileName); + msg += "!\nBetter include "; + msg += Quoted("moc_" + incKey.Base + ".cpp"); + msg += " for a compatibility with strict mode.\n"; + msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"; + Log().WarningFile(GenT::MOC, sourceFile.FileName, msg); } else { - // Don't allow <BASE>.moc include other than self in strict mode - { - std::string emsg = "The file includes the moc file "; - emsg += Quoted(mocInc.Inc); - emsg += ", which seems to be the moc file from a different " - "source file.\nThis is not supported. Include "; - emsg += Quoted(meta.FileBase + ".moc"); - emsg += " to run moc on this source file."; - LogFileError(GenT::MOC, FileName, emsg); - } + std::string msg = "The file includes the moc file "; + msg += Quoted(incKey.Key); + msg += " instead of "; + msg += Quoted("moc_" + incKey.Base + ".cpp"); + msg += ".\nRunning moc on the header\n "; + msg += Quoted(header->FileName); + msg += "!\nBetter include "; + msg += Quoted("moc_" + incKey.Base + ".cpp"); + msg += " for compatibility with strict mode.\n"; + msg += "This is a CMAKE_AUTOMOC_RELAXED_MODE warning.\n"; + Log().WarningFile(GenT::MOC, sourceFile.FileName, msg); + } + // Create mapping + if (!MocRegisterIncluded(incKey.Key, source, std::move(header), true)) { return false; } } - } - - if (!ownMacro.empty() && !ownDotMocIncluded) { - // In this case, check whether the scanned file itself contains a - // Q_OBJECT. - // If this is the case, the moc_foo.cpp should probably be generated from - // foo.cpp instead of foo.h, because otherwise it won't build. - // But warn, since this is not how it is supposed to be used. - // This is for KDE4 compatibility. - if (Gen()->Moc().RelaxedMode && ownMocUscIncluded) { - JobPre uscJobPre; - // Remove underscore job request - { - auto itC = jobs.begin(); - auto itE = jobs.end(); - for (; itC != itE; ++itC) { - JobPre& job(*itC); - if (job.self && job.underscore) { - uscJobPre = std::move(job); - jobs.erase(itC); - break; - } - } + } else { + // Strict mode + for (IncludeKeyT const& incKey : parseData.Include.Dot) { + // Check if this is the sources own .moc file + bool const ownMoc = (incKey.Base == sourceBase); + if (!ownMoc) { + // Don't allow <BASE>.moc include other than own in strict mode + std::string msg = "The file includes the moc file "; + msg += Quoted(incKey.Key); + msg += ",\nwhich seems to be the moc file from a different " + "source file.\nThis is not supported. Include "; + msg += Quoted(sourceBase + ".moc"); + msg += " to run moc on this source file."; + LogFileError(GenT::MOC, sourceFile.FileName, msg); + return false; } - // Issue a warning - { - std::string emsg = "The file contains a "; - emsg += ownMacro; - emsg += " macro, but does not include "; - emsg += Quoted(meta.FileBase + ".moc"); - emsg += ". Instead it includes "; - emsg += Quoted(uscJobPre.IncludeString); - emsg += ".\nRunning moc on\n "; - emsg += Quoted(FileName); - emsg += "!\nBetter include "; - emsg += Quoted(meta.FileBase + ".moc"); - emsg += " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; - Log().WarningFile(GenT::MOC, FileName, emsg); - } - // Add own source job - jobs.emplace_back( - JobPre{ true, false, FileName, uscJobPre.IncludeString }); - } else { - // Otherwise always error out since it will not compile. - { - std::string emsg = "The file contains a "; - emsg += ownMacro; - emsg += " macro, but does not include "; - emsg += Quoted(meta.FileBase + ".moc"); - emsg += "!\nConsider to\n - add #include \""; - emsg += meta.FileBase; - emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; - LogFileError(GenT::MOC, FileName, emsg); + // Accept but issue a warning if moc isn't required + if (parseData.Macro.empty()) { + std::string msg = "The file includes the moc file "; + msg += Quoted(incKey.Key); + msg += ", but does not contain a "; + msg += MocConst().MacrosString(); + msg += " macro."; + Log().WarningFile(GenT::MOC, sourceFile.FileName, msg); + } + // Create mapping + if (!MocRegisterIncluded(incKey.Key, source, source, false)) { + return false; } - return false; } } - // Convert pre jobs to actual jobs - for (JobPre& jobPre : jobs) { - cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>( - std::move(jobPre.SourceFile), FileName, std::move(jobPre.IncludeString)); - if (jobPre.self) { - // Read dependencies from this source - JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle); - Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends); - jobMoc.DependsValid = true; + return true; +} + +cmQtAutoMocUic::SourceFileHandleT +cmQtAutoMocUic::JobEvaluateT::MocFindIncludedHeader( + std::string const& includerDir, std::string const& includeBase) const +{ + // Search in vicinity of the source + { + SourceFileHandleT res = MocFindHeader(includerDir + includeBase); + if (res) { + return res; } - if (!Gen()->ParallelJobPushMoc(std::move(jobHandle))) { - return false; + } + // Search in include directories + for (std::string const& path : MocConst().IncludePaths) { + std::string testPath = path; + testPath += '/'; + testPath += includeBase; + SourceFileHandleT res = MocFindHeader(testPath); + if (res) { + return res; } } - return true; + // Return without success + return SourceFileHandleT(); } -bool cmQtAutoMocUic::JobParseT::ParseMocHeader(MetaT const& meta) +cmQtAutoMocUic::SourceFileHandleT cmQtAutoMocUic::JobEvaluateT::MocFindHeader( + std::string const& basePath) const { - bool success = true; - std::string const macroName = Gen()->Moc().FindMacro(meta.Content); - if (!macroName.empty()) { - cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>( - std::string(FileName), std::string(), std::string()); - // Read dependencies from this source - { - JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle); - Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends); - jobMoc.DependsValid = true; + std::string testPath; + testPath.reserve(basePath.size() + 8); + for (std::string const& ext : BaseConst().HeaderExtensions) { + testPath.clear(); + testPath += basePath; + testPath += '.'; + testPath += ext; + cmFileTime fileTime; + if (fileTime.Load(testPath)) { + // Compute real path of the file + testPath = cmSystemTools::GetRealPath(testPath); + // Return a known file if it exists already + { + auto it = BaseEval().Headers.find(testPath); + if (it != BaseEval().Headers.end()) { + return it->second; + } + } + // Created and return discovered file entry + SourceFileHandleT& res = MocEval().HeadersDiscovered[testPath]; + if (!res) { + res = std::make_shared<SourceFileT>(testPath); + res->FileTime = fileTime; + res->Moc = true; + } + return res; } - success = Gen()->ParallelJobPushMoc(std::move(jobHandle)); } - return success; + // Return without success + return SourceFileHandleT(); } -std::string cmQtAutoMocUic::JobParseT::MocStringHeaders( +std::string cmQtAutoMocUic::JobEvaluateT::MocMessageTestHeaders( std::string const& fileBase) const { - std::string res = fileBase; - res += ".{"; - res += cmJoin(Gen()->Base().HeaderExtensions, ","); - res += "}"; - return res; + std::ostringstream res; + { + std::string exts = ".{"; + exts += cmJoin(BaseConst().HeaderExtensions, ","); + exts += '}'; + // Compose result string + res << " " << fileBase << exts << '\n'; + for (std::string const& path : MocConst().IncludePaths) { + res << " " << path << '/' << fileBase << exts << '\n'; + } + } + return res.str(); } -std::string cmQtAutoMocUic::JobParseT::MocFindIncludedHeader( - std::string const& includerDir, std::string const& includeBase) +bool cmQtAutoMocUic::JobEvaluateT::MocRegisterIncluded( + std::string const& includeString, SourceFileHandleT includerFileHandle, + SourceFileHandleT sourceFileHandle, bool sourceIsHeader) const { - std::string header; - // Search in vicinity of the source - if (!Gen()->Base().FindHeader(header, includerDir + includeBase)) { - // Search in include directories - for (std::string const& path : Gen()->Moc().IncludePaths) { - std::string fullPath = path; - fullPath.push_back('/'); - fullPath += includeBase; - if (Gen()->Base().FindHeader(header, fullPath)) { - break; + // Check if this file is already included + MappingHandleT& handle = MocEval().Includes[includeString]; + if (handle) { + // Check if the output file would be generated from different source files + if (handle->SourceFile != sourceFileHandle) { + std::string msg = "The source files\n "; + msg += Quoted(includerFileHandle->FileName); + msg += '\n'; + for (auto const& item : handle->IncluderFiles) { + msg += " "; + msg += Quoted(item->FileName); + msg += '\n'; } + msg += "contain the same include string "; + msg += Quoted(includeString); + msg += ", but\nthe moc file would be generated from different " + "source files\n "; + msg += Quoted(sourceFileHandle->FileName); + msg += " and\n "; + msg += Quoted(handle->SourceFile->FileName); + msg += ".\nConsider to\n" + " - not include the \"moc_<NAME>.cpp\" file\n" + " - add a directory prefix to a \"<NAME>.moc\" include " + "(e.g \"sub/<NAME>.moc\")\n" + " - rename the source file(s)\n"; + LogError(GenT::MOC, msg); + return false; } + + // The same mapping already exists. Just add to the includers list. + handle->IncluderFiles.emplace_back(std::move(includerFileHandle)); + return true; } - // Sanitize - if (!header.empty()) { - header = FileSys().GetRealPath(header); + + // Create a new mapping + handle = std::make_shared<MappingT>(); + handle->IncludeString = includeString; + handle->IncluderFiles.emplace_back(std::move(includerFileHandle)); + handle->SourceFile = std::move(sourceFileHandle); + handle->OutputFile += Gen()->AbsoluteIncludePath(includeString); + + // Register mapping in sources/headers map + MocRegisterMapping(handle, sourceIsHeader); + return true; +} + +void cmQtAutoMocUic::JobEvaluateT::MocRegisterMapping( + MappingHandleT mappingHandle, bool sourceIsHeader) const +{ + auto& regMap = + sourceIsHeader ? MocEval().HeaderMappings : MocEval().SourceMappings; + // Check if source file already gets mapped + auto& regHandle = regMap[mappingHandle->SourceFile->FileName]; + if (!regHandle) { + // Yet unknown mapping + regHandle = std::move(mappingHandle); + } else { + // Mappings with include string override those without + if (!mappingHandle->IncludeString.empty()) { + regHandle = std::move(mappingHandle); + } } - return header; } -bool cmQtAutoMocUic::JobParseT::ParseUic(MetaT const& meta) +bool cmQtAutoMocUic::JobEvaluateT::UicEval(SourceFileMapT const& fileMap) { - bool success = true; - if (meta.Content.find("ui_") != std::string::npos) { - const char* contentChars = meta.Content.c_str(); - cmsys::RegularExpressionMatch match; - while (Gen()->Uic().RegExpInclude.find(contentChars, match)) { - if (!ParseUicInclude(meta, match.match(2))) { - success = false; - break; - } - contentChars += match.end(); + for (auto const& pair : fileMap) { + if (!UicEvalFile(pair.second)) { + return false; } } - return success; + return true; } -bool cmQtAutoMocUic::JobParseT::ParseUicInclude(MetaT const& meta, - std::string&& includeString) +bool cmQtAutoMocUic::JobEvaluateT::UicEvalFile( + SourceFileHandleT sourceFileHandle) { - bool success = false; - std::string uiInputFile = UicFindIncludedFile(meta, includeString); - if (!uiInputFile.empty()) { - if (!Gen()->Uic().skipped(uiInputFile)) { - cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobUicT>( - std::move(uiInputFile), FileName, std::move(includeString)); - success = Gen()->ParallelJobPushUic(std::move(jobHandle)); - } else { - // A skipped file is successful - success = true; + SourceFileT const& sourceFile = *sourceFileHandle; + auto const& Include = sourceFile.ParseData->Uic.Include; + if (!sourceFile.Uic || Include.empty()) { + return true; + } + + std::string const sourceDir = SubDirPrefix(sourceFile.FileName); + for (IncludeKeyT const& incKey : Include) { + // Find .ui file name + SourceFileHandleT uiFileHandle = + UicFindIncludedUi(sourceFile.FileName, sourceDir, incKey); + if (!uiFileHandle || UicConst().skipped(uiFileHandle->FileName)) { + continue; + } + // Register mapping + if (!UicRegisterMapping(incKey.Key, std::move(uiFileHandle), + std::move(sourceFileHandle))) { + return false; } } - return success; + + return true; } -std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile( - MetaT const& meta, std::string const& includeString) +bool cmQtAutoMocUic::JobEvaluateT::UicRegisterMapping( + std::string const& includeString, SourceFileHandleT uiFileHandle, + SourceFileHandleT includerFileHandle) { - std::string res; - std::string searchFile = - FileSys().GetFilenameWithoutLastExtension(includeString).substr(3); - searchFile += ".ui"; + auto& Includes = Gen()->UicEval().Includes; + auto it = Includes.find(includeString); + if (it != Includes.end()) { + MappingHandleT const& handle = it->second; + if (handle->SourceFile != uiFileHandle) { + // The output file already gets generated - from a different .ui file! + std::string msg = "The source files\n "; + msg += Quoted(includerFileHandle->FileName); + msg += '\n'; + for (auto const& item : handle->IncluderFiles) { + msg += " "; + msg += Quoted(item->FileName); + msg += '\n'; + } + msg += "contain the same include string "; + msg += Quoted(includeString); + msg += ", but\nthe uic file would be generated from different " + "user interface files\n "; + msg += Quoted(uiFileHandle->FileName); + msg += " and\n "; + msg += Quoted(handle->SourceFile->FileName); + msg += ".\nConsider to\n" + " - add a directory prefix to a \"ui_<NAME>.h\" include " + "(e.g \"sub/ui_<NAME>.h\")\n" + " - rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" " + "include(s)\n"; + LogError(GenT::UIC, msg); + return false; + } + // Add includer file to existing mapping + handle->IncluderFiles.emplace_back(std::move(includerFileHandle)); + } else { + // New mapping handle + MappingHandleT handle = std::make_shared<MappingT>(); + handle->IncludeString = includeString; + handle->IncluderFiles.emplace_back(std::move(includerFileHandle)); + handle->SourceFile = std::move(uiFileHandle); + handle->OutputFile += Gen()->AbsoluteIncludePath(includeString); + // Register mapping + Includes.emplace(includeString, std::move(handle)); + } + return true; +} + +cmQtAutoMocUic::SourceFileHandleT +cmQtAutoMocUic::JobEvaluateT::UicFindIncludedUi( + std::string const& sourceFile, std::string const& sourceDir, + IncludeKeyT const& incKey) const +{ + std::string searchFileName = incKey.Base; + searchFileName += ".ui"; // Collect search paths list - std::deque<std::string> testFiles; + std::vector<std::string> testFiles; { - std::string const searchPath = FileSys().SubDirPrefix(includeString); + auto& searchPaths = UicConst().SearchPaths; + testFiles.reserve((searchPaths.size() + 1) * 2); - std::string searchFileFull; - if (!searchPath.empty()) { - searchFileFull = searchPath; - searchFileFull += searchFile; - } // Vicinity of the source - { - std::string const sourcePath = meta.FileDir; - testFiles.push_back(sourcePath + searchFile); - if (!searchPath.empty()) { - testFiles.push_back(sourcePath + searchFileFull); - } + testFiles.emplace_back(sourceDir + searchFileName); + if (!incKey.Dir.empty()) { + std::string path = sourceDir; + path += incKey.Dir; + path += searchFileName; + testFiles.emplace_back(path); } // AUTOUIC search paths - if (!Gen()->Uic().SearchPaths.empty()) { - for (std::string const& sPath : Gen()->Uic().SearchPaths) { - testFiles.push_back((sPath + "/").append(searchFile)); + if (!searchPaths.empty()) { + for (std::string const& sPath : searchPaths) { + std::string path = sPath; + path += '/'; + path += searchFileName; + testFiles.emplace_back(std::move(path)); } - if (!searchPath.empty()) { - for (std::string const& sPath : Gen()->Uic().SearchPaths) { - testFiles.push_back((sPath + "/").append(searchFileFull)); + if (!incKey.Dir.empty()) { + for (std::string const& sPath : searchPaths) { + std::string path = sPath; + path += '/'; + path += incKey.Dir; + path += searchFileName; + testFiles.emplace_back(std::move(path)); } } } @@ -675,243 +1100,189 @@ std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile( // Search for the .ui file! for (std::string const& testFile : testFiles) { - if (FileSys().FileExists(testFile)) { - res = FileSys().GetRealPath(testFile); - break; + cmFileTime fileTime; + if (fileTime.Load(testFile)) { + // .ui file found in files system! + std::string realPath = cmSystemTools::GetRealPath(testFile); + // Get or create .ui file handle + SourceFileHandleT& handle = Gen()->UicEval().UiFiles[realPath]; + if (!handle) { + // The file wasn't registered, yet + handle = std::make_shared<SourceFileT>(realPath); + handle->FileTime = fileTime; + } + return handle; } } // Log error - if (res.empty()) { - std::string emsg = "Could not find "; - emsg += Quoted(searchFile); - emsg += " in\n"; + { + std::string msg = "The file includes the uic file "; + msg += Quoted(incKey.Key); + msg += ",\nbut the user interface file "; + msg += Quoted(searchFileName); + msg += "\ncould not be found in the following locations\n"; for (std::string const& testFile : testFiles) { - emsg += " "; - emsg += Quoted(testFile); - emsg += "\n"; + msg += " "; + msg += Quoted(testFile); + msg += '\n'; } - LogFileError(GenT::UIC, FileName, emsg); + LogFileError(GenT::UIC, sourceFile, msg); } - return res; + return SourceFileHandleT(); } -void cmQtAutoMocUic::JobPostParseT::Process() +void cmQtAutoMocUic::JobGenerateT::Process() { - if (Gen()->Moc().Enabled) { - // Add mocs compilations fence job + // Add moc compile jobs + if (MocConst().Enabled) { + for (auto const& pair : MocEval().HeaderMappings) { + // Register if this mapping is a candidate for mocs_compilation.cpp + bool const compFile = pair.second->IncludeString.empty(); + if (compFile) { + MocEval().CompFiles.emplace_back(pair.second->SourceFile->BuildPath); + } + if (!MocGenerate(pair.second, compFile)) { + return; + } + } + for (auto const& pair : MocEval().SourceMappings) { + if (!MocGenerate(pair.second, false)) { + return; + } + } + + // Add mocs compilations job on demand Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>(); } + + // Add uic compile jobs + if (UicConst().Enabled) { + for (auto const& pair : Gen()->UicEval().Includes) { + if (!UicGenerate(pair.second)) { + return; + } + } + } + // Add finish job Gen()->WorkerPool().EmplaceJob<JobFinishT>(); } -void cmQtAutoMocUic::JobMocsCompilationT::Process() +bool cmQtAutoMocUic::JobGenerateT::MocGenerate(MappingHandleT const& mapping, + bool compFile) const { - // Compose mocs compilation file content - std::string content = - "// This file is autogenerated. Changes will be overwritten.\n"; - if (Gen()->MocAutoFiles().empty()) { - // Placeholder content - content += "// No files found that require moc or the moc files are " - "included\n"; - content += "enum some_compilers { need_more_than_nothing };\n"; - } else { - // Valid content - char const sbeg = Gen()->Base().MultiConfig ? '<' : '"'; - char const send = Gen()->Base().MultiConfig ? '>' : '"'; - for (std::string const& mocfile : Gen()->MocAutoFiles()) { - content += "#include "; - content += sbeg; - content += mocfile; - content += send; - content += '\n'; - } + std::unique_ptr<std::string> reason; + if (Log().Verbose()) { + reason = cm::make_unique<std::string>(); } - - std::string const& compAbs = Gen()->Moc().CompFileAbs; - if (FileSys().FileDiffers(compAbs, content)) { - // Actually write mocs compilation file - if (Log().Verbose()) { - Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs); - } - if (!FileSys().FileWrite(compAbs, content)) { - LogFileError(GenT::MOC, compAbs, - "mocs compilation file writing failed."); + if (MocUpdate(*mapping, reason.get())) { + // Create the parent directory + if (!MakeParentDirectory(mapping->OutputFile)) { + LogFileError(GenT::MOC, mapping->OutputFile, + "Could not create parent directory."); + return false; } - } else if (Gen()->MocAutoFileUpdated()) { - // Only touch mocs compilation file - if (Log().Verbose()) { - Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs); + // Add moc job + Gen()->WorkerPool().EmplaceJob<JobMocT>(mapping, std::move(reason)); + // Check if a moc job for a mocs_compilation.cpp entry was generated + if (compFile) { + MocEval().CompUpdated = true; } - FileSys().Touch(compAbs); } + return true; } -void cmQtAutoMocUic::JobMocT::FindDependencies(std::string const& content) -{ - Gen()->Moc().FindDependencies(content, Depends); - DependsValid = true; -} - -void cmQtAutoMocUic::JobMocT::Process() +bool cmQtAutoMocUic::JobGenerateT::MocUpdate(MappingT const& mapping, + std::string* reason) const { - // Compute build file name - if (!IncludeString.empty()) { - BuildFile = Gen()->Base().AutogenIncludeDir; - BuildFile += '/'; - BuildFile += IncludeString; - } else { - // Relative build path - std::string relPath = FileSys().GetFilePathChecksum(SourceFile); - relPath += "/moc_"; - relPath += FileSys().GetFilenameWithoutLastExtension(SourceFile); - - // Register relative file path with duplication check - relPath = Gen()->ParallelMocAutoRegister(relPath); - - // Absolute build path - if (Gen()->Base().MultiConfig) { - BuildFile = Gen()->Base().AutogenIncludeDir; - BuildFile += '/'; - BuildFile += relPath; - } else { - BuildFile = Gen()->Base().AbsoluteBuildPath(relPath); + std::string const& sourceFile = mapping.SourceFile->FileName; + std::string const& outputFile = mapping.OutputFile; + + // Test if the output file exists + cmFileTime outputFileTime; + if (!outputFileTime.Load(outputFile)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it doesn't exist, from "; + *reason += Quoted(sourceFile); } + return true; } - if (UpdateRequired()) { - GenerateMoc(); - } -} - -bool cmQtAutoMocUic::JobMocT::UpdateRequired() -{ - bool const verbose = Log().Verbose(); - - // Test if the build file exists - if (!FileSys().FileExists(BuildFile)) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " from its source file "; - reason += Quoted(SourceFile); - reason += " because it doesn't exist"; - Log().Info(GenT::MOC, reason); + // Test if any setting changed + if (MocConst().SettingsChanged) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because the uic settings changed, from "; + *reason += Quoted(sourceFile); } return true; } - // Test if any setting changed - if (Gen()->Moc().SettingsChanged) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " from "; - reason += Quoted(SourceFile); - reason += " because the MOC settings changed"; - Log().Info(GenT::MOC, reason); + // Test if the source file is newer + if (outputFileTime.Older(mapping.SourceFile->FileTime)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it's older than its source file, from "; + *reason += Quoted(sourceFile); } return true; } // Test if the moc_predefs file is newer - if (!Gen()->Moc().PredefsFileAbs.empty()) { - bool isOlder = false; - { - std::string error; - isOlder = FileSys().FileIsOlderThan(BuildFile, - Gen()->Moc().PredefsFileAbs, &error); - if (!isOlder && !error.empty()) { - LogError(GenT::MOC, error); - return false; - } - } - if (isOlder) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " because it's older than: "; - reason += Quoted(Gen()->Moc().PredefsFileAbs); - Log().Info(GenT::MOC, reason); + if (!MocConst().PredefsFileAbs.empty()) { + if (outputFileTime.Older(MocEval().PredefsTime)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it's older than "; + *reason += Quoted(MocConst().PredefsFileAbs); + *reason += ", from "; + *reason += Quoted(sourceFile); } return true; } } - // Test if the source file is newer - { - bool isOlder = false; - { - std::string error; - isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); - if (!isOlder && !error.empty()) { - LogError(GenT::MOC, error); - return false; - } - } - if (isOlder) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " because it's older than its source file "; - reason += Quoted(SourceFile); - Log().Info(GenT::MOC, reason); - } - return true; + // Test if the moc executable is newer + if (outputFileTime.Older(MocConst().ExecutableTime)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it's older than the moc executable, from "; + *reason += Quoted(sourceFile); } + return true; } // Test if a dependency file is newer { - // Read dependencies on demand - if (!DependsValid) { - std::string content; - { - std::string error; - if (!FileSys().FileRead(content, SourceFile, &error)) { - std::string emsg = "Could not read file\n "; - emsg += Quoted(SourceFile); - emsg += "\nrequired by moc include "; - emsg += Quoted(IncludeString); - emsg += " in\n "; - emsg += Quoted(IncluderFile); - emsg += ".\n"; - emsg += error; - LogError(GenT::MOC, emsg); - return false; - } - } - FindDependencies(content); - } // Check dependency timestamps - std::string error; - std::string sourceDir = FileSys().SubDirPrefix(SourceFile); - for (std::string const& depFileRel : Depends) { - std::string depFileAbs = - Gen()->Moc().FindIncludedFile(sourceDir, depFileRel); - if (!depFileAbs.empty()) { - if (FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " from "; - reason += Quoted(SourceFile); - reason += " because it is older than it's dependency file "; - reason += Quoted(depFileAbs); - Log().Info(GenT::MOC, reason); - } - return true; - } - if (!error.empty()) { - LogError(GenT::MOC, error); - return false; + std::string const sourceDir = SubDirPrefix(sourceFile); + for (std::string const& dep : mapping.SourceFile->ParseData->Moc.Depends) { + // Find dependency file + auto const depMatch = MocFindDependency(sourceDir, dep); + if (depMatch.first.empty()) { + Log().WarningFile(GenT::MOC, sourceFile, + "Could not find dependency file " + Quoted(dep)); + continue; + } + // Test if dependency file is older + if (outputFileTime.Older(depMatch.second)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it's older than its dependency file "; + *reason += Quoted(depMatch.first); + *reason += ", from "; + *reason += Quoted(sourceFile); } - } else { - std::string message = "Could not find dependency file "; - message += Quoted(depFileRel); - Log().WarningFile(GenT::MOC, SourceFile, message); + return true; } } } @@ -919,198 +1290,254 @@ bool cmQtAutoMocUic::JobMocT::UpdateRequired() return false; } -void cmQtAutoMocUic::JobMocT::GenerateMoc() +std::pair<std::string, cmFileTime> +cmQtAutoMocUic::JobGenerateT::MocFindDependency( + std::string const& sourceDir, std::string const& includeString) const { - // Make sure the parent directory exists - if (!FileSys().MakeParentDirectory(BuildFile)) { - LogFileError(GenT::MOC, BuildFile, "Could not create parent directory."); - return; - } + typedef std::pair<std::string, cmFileTime> ResPair; + // Search in vicinity of the source { - // Compose moc command - std::vector<std::string> cmd; - cmd.push_back(Gen()->Moc().Executable); - // Add options - cmd.insert(cmd.end(), Gen()->Moc().AllOptions.begin(), - Gen()->Moc().AllOptions.end()); - // Add predefs include - if (!Gen()->Moc().PredefsFileAbs.empty()) { - cmd.emplace_back("--include"); - cmd.push_back(Gen()->Moc().PredefsFileAbs); - } - cmd.emplace_back("-o"); - cmd.push_back(BuildFile); - cmd.push_back(SourceFile); - - // Execute moc command - cmWorkerPool::ProcessResultT result; - if (RunProcess(GenT::MOC, result, cmd)) { - // Moc command success - // Print moc output - if (!result.StdOut.empty()) { - Log().Info(GenT::MOC, result.StdOut); - } - // Notify the generator that a not included file changed (on demand) - if (IncludeString.empty()) { - Gen()->ParallelMocAutoUpdated(); - } - } else { - // Moc command failed - { - std::string emsg = "The moc process failed to compile\n "; - emsg += Quoted(SourceFile); - emsg += "\ninto\n "; - emsg += Quoted(BuildFile); - emsg += ".\n"; - emsg += result.ErrorMessage; - LogCommandError(GenT::MOC, emsg, cmd, result.StdOut); - } - FileSys().FileRemove(BuildFile); + ResPair res{ sourceDir + includeString, {} }; + if (res.second.Load(res.first)) { + return res; + } + } + // Search in include directories + for (std::string const& includePath : MocConst().IncludePaths) { + ResPair res{ includePath, {} }; + res.first += '/'; + res.first += includeString; + if (res.second.Load(res.first)) { + return res; } } + // Return empty + return ResPair(); } -void cmQtAutoMocUic::JobUicT::Process() +bool cmQtAutoMocUic::JobGenerateT::UicGenerate( + MappingHandleT const& mapping) const { - // Compute build file name - BuildFile = Gen()->Base().AutogenIncludeDir; - BuildFile += '/'; - BuildFile += IncludeString; - - if (UpdateRequired()) { - GenerateUic(); + std::unique_ptr<std::string> reason; + if (Log().Verbose()) { + reason = cm::make_unique<std::string>(); } + if (UicUpdate(*mapping, reason.get())) { + // Create the parent directory + if (!MakeParentDirectory(mapping->OutputFile)) { + LogFileError(GenT::UIC, mapping->OutputFile, + "Could not create parent directory."); + return false; + } + // Add uic job + Gen()->WorkerPool().EmplaceJob<JobUicT>(mapping, std::move(reason)); + } + return true; } -bool cmQtAutoMocUic::JobUicT::UpdateRequired() +bool cmQtAutoMocUic::JobGenerateT::UicUpdate(MappingT const& mapping, + std::string* reason) const { - bool const verbose = Log().Verbose(); + std::string const& sourceFile = mapping.SourceFile->FileName; + std::string const& outputFile = mapping.OutputFile; // Test if the build file exists - if (!FileSys().FileExists(BuildFile)) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " from its source file "; - reason += Quoted(SourceFile); - reason += " because it doesn't exist"; - Log().Info(GenT::UIC, reason); + cmFileTime outputFileTime; + if (!outputFileTime.Load(outputFile)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it doesn't exist, from "; + *reason += Quoted(sourceFile); } return true; } // Test if the uic settings changed - if (Gen()->Uic().SettingsChanged) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " from "; - reason += Quoted(SourceFile); - reason += " because the UIC settings changed"; - Log().Info(GenT::UIC, reason); + if (UicConst().SettingsChanged) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because the uic settings changed, from "; + *reason += Quoted(sourceFile); } return true; } // Test if the source file is newer - { - bool isOlder = false; - { - std::string error; - isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); - if (!isOlder && !error.empty()) { - LogError(GenT::UIC, error); - return false; - } + if (outputFileTime.Older(mapping.SourceFile->FileTime)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += " because it's older than the source file "; + *reason += Quoted(sourceFile); } - if (isOlder) { - if (verbose) { - std::string reason = "Generating "; - reason += Quoted(BuildFile); - reason += " because it's older than its source file "; - reason += Quoted(SourceFile); - Log().Info(GenT::UIC, reason); - } - return true; + return true; + } + + // Test if the uic executable is newer + if (outputFileTime.Older(UicConst().ExecutableTime)) { + if (reason != nullptr) { + *reason = "Generating "; + *reason += Quoted(outputFile); + *reason += ", because it's older than the uic executable, from "; + *reason += Quoted(sourceFile); } + return true; } return false; } -void cmQtAutoMocUic::JobUicT::GenerateUic() +void cmQtAutoMocUic::JobMocT::Process() { - // Make sure the parent directory exists - if (!FileSys().MakeParentDirectory(BuildFile)) { - LogFileError(GenT::UIC, BuildFile, "Could not create parent directory."); - return; + std::string const& sourceFile = Mapping->SourceFile->FileName; + std::string const& outputFile = Mapping->OutputFile; + + // Compose moc command + std::vector<std::string> cmd; + cmd.push_back(MocConst().Executable); + // Add options + cmd.insert(cmd.end(), MocConst().AllOptions.begin(), + MocConst().AllOptions.end()); + // Add predefs include + if (!MocConst().PredefsFileAbs.empty()) { + cmd.emplace_back("--include"); + cmd.push_back(MocConst().PredefsFileAbs); + } + cmd.emplace_back("-o"); + cmd.push_back(outputFile); + cmd.push_back(sourceFile); + + // Execute moc command + cmWorkerPool::ProcessResultT result; + if (RunProcess(GenT::MOC, result, cmd, Reason.get())) { + // Moc command success. Print moc output. + if (!result.StdOut.empty()) { + Log().Info(GenT::MOC, result.StdOut); + } + } else { + // Moc command failed + std::string msg = "The moc process failed to compile\n "; + msg += Quoted(sourceFile); + msg += "\ninto\n "; + msg += Quoted(outputFile); + if (Mapping->IncluderFiles.empty()) { + msg += ".\n"; + } else { + msg += "\nincluded by\n"; + for (auto const& item : Mapping->IncluderFiles) { + msg += " "; + msg += Quoted(item->FileName); + msg += '\n'; + } + } + msg += result.ErrorMessage; + LogCommandError(GenT::MOC, msg, cmd, result.StdOut); } +} + +void cmQtAutoMocUic::JobUicT::Process() +{ + std::string const& sourceFile = Mapping->SourceFile->FileName; + std::string const& outputFile = Mapping->OutputFile; + + // Compose uic command + std::vector<std::string> cmd; + cmd.push_back(UicConst().Executable); { - // Compose uic command - std::vector<std::string> cmd; - cmd.push_back(Gen()->Uic().Executable); - { - std::vector<std::string> allOpts = Gen()->Uic().TargetOptions; - auto optionIt = Gen()->Uic().Options.find(SourceFile); - if (optionIt != Gen()->Uic().Options.end()) { - UicMergeOptions(allOpts, optionIt->second, - (Gen()->Base().QtVersionMajor == 5)); - } - cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); + std::vector<std::string> allOpts = UicConst().TargetOptions; + auto optionIt = UicConst().Options.find(sourceFile); + if (optionIt != UicConst().Options.end()) { + UicMergeOptions(allOpts, optionIt->second, + (BaseConst().QtVersionMajor == 5)); } - cmd.emplace_back("-o"); - cmd.emplace_back(BuildFile); - cmd.emplace_back(SourceFile); + cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); + } + cmd.emplace_back("-o"); + cmd.emplace_back(outputFile); + cmd.emplace_back(sourceFile); - cmWorkerPool::ProcessResultT result; - if (RunProcess(GenT::UIC, result, cmd)) { - // Uic command success - // Print uic output - if (!result.StdOut.empty()) { - Log().Info(GenT::UIC, result.StdOut); - } - } else { - // Uic command failed - { - std::string emsg = "The uic process failed to compile\n "; - emsg += Quoted(SourceFile); - emsg += "\ninto\n "; - emsg += Quoted(BuildFile); - emsg += "\nincluded by\n "; - emsg += Quoted(IncluderFile); - emsg += ".\n"; - emsg += result.ErrorMessage; - LogCommandError(GenT::UIC, emsg, cmd, result.StdOut); - } - FileSys().FileRemove(BuildFile); + cmWorkerPool::ProcessResultT result; + if (RunProcess(GenT::UIC, result, cmd, Reason.get())) { + // Uic command success + // Print uic output + if (!result.StdOut.empty()) { + Log().Info(GenT::UIC, result.StdOut); } + } else { + // Uic command failed + std::string msg = "The uic process failed to compile\n "; + msg += Quoted(sourceFile); + msg += "\ninto\n "; + msg += Quoted(outputFile); + msg += "\nincluded by\n"; + for (auto const& item : Mapping->IncluderFiles) { + msg += " "; + msg += Quoted(item->FileName); + msg += '\n'; + } + msg += result.ErrorMessage; + LogCommandError(GenT::UIC, msg, cmd, result.StdOut); } } -void cmQtAutoMocUic::JobFinishT::Process() +void cmQtAutoMocUic::JobMocsCompilationT::Process() { - Gen()->AbortSuccess(); + // Compose mocs compilation file content + std::string content = + "// This file is autogenerated. Changes will be overwritten.\n"; + + if (MocEval().CompFiles.empty()) { + // Placeholder content + content += "// No files found that require moc or the moc files are " + "included\n"; + content += "enum some_compilers { need_more_than_nothing };\n"; + } else { + // Valid content + char const clampB = BaseConst().MultiConfig ? '<' : '"'; + char const clampE = BaseConst().MultiConfig ? '>' : '"'; + for (std::string const& mocfile : MocEval().CompFiles) { + content += "#include "; + content += clampB; + content += mocfile; + content += clampE; + content += '\n'; + } + } + + std::string const& compAbs = MocConst().CompFileAbs; + if (cmQtAutoGenerator::FileDiffers(compAbs, content)) { + // Actually write mocs compilation file + if (Log().Verbose()) { + Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs); + } + if (!FileWrite(compAbs, content)) { + LogFileError(GenT::MOC, compAbs, + "mocs compilation file writing failed."); + } + } else if (MocEval().CompUpdated) { + // Only touch mocs compilation file + if (Log().Verbose()) { + Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs); + } + if (!cmSystemTools::Touch(compAbs, false)) { + LogFileError(GenT::MOC, compAbs, + "mocs compilation file touching failed."); + } + } } -cmQtAutoMocUic::cmQtAutoMocUic() - : Base_(&FileSys()) - , Moc_(&FileSys()) +void cmQtAutoMocUic::JobFinishT::Process() { - // Precompile regular expressions - Moc_.RegExpInclude.compile( - "(^|\n)[ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); - Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); + Gen()->AbortSuccess(); } +cmQtAutoMocUic::cmQtAutoMocUic() = default; cmQtAutoMocUic::~cmQtAutoMocUic() = default; bool cmQtAutoMocUic::Init(cmMakefile* makefile) { - // -- Meta - Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); - // Utility lambdas auto InfoGet = [makefile](const char* key) { return makefile->GetSafeDefinition(key); @@ -1169,171 +1596,185 @@ bool cmQtAutoMocUic::Init(cmMakefile* makefile) cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); return list; }; + auto LogInfoError = [this](std::string const& msg) -> bool { + std::ostringstream err; + err << "In " << Quoted(this->InfoFile()) << ":\n" << msg; + this->Log().Error(GenT::GEN, err.str()); + return false; + }; + auto MatchSizes = [&LogInfoError](const char* keyA, const char* keyB, + std::size_t sizeA, + std::size_t sizeB) -> bool { + if (sizeA == sizeB) { + return true; + } + std::ostringstream err; + err << "Lists sizes mismatch " << keyA << '(' << sizeA << ") " << keyB + << '(' << sizeB << ')'; + return LogInfoError(err.str()); + }; // -- Read info file if (!makefile->ReadListFile(InfoFile())) { - Log().ErrorFile(GenT::GEN, InfoFile(), "File processing failed"); - return false; + return LogInfoError("File processing failed"); } // -- Meta - Log().RaiseVerbosity(InfoGet("AM_VERBOSITY")); - Base_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG"); + Logger_.RaiseVerbosity(InfoGet("AM_VERBOSITY")); + BaseConst_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG"); { - unsigned long num = Base_.NumThreads; + unsigned long num = 1; if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) { num = std::max<unsigned long>(num, 1); num = std::min<unsigned long>(num, ParallelMax); - Base_.NumThreads = static_cast<unsigned int>(num); } - WorkerPool_.SetThreadCount(Base_.NumThreads); + WorkerPool_.SetThreadCount(static_cast<unsigned int>(num)); } + BaseConst_.HeaderExtensions = + makefile->GetCMakeInstance()->GetHeaderExtensions(); // - Files and directories - Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); - Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); - Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); - Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); - Base_.IncludeProjectDirsBefore = + BaseConst_.IncludeProjectDirsBefore = InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); - Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR"); - if (Base_.AutogenBuildDir.empty()) { - Log().ErrorFile(GenT::GEN, InfoFile(), "Autogen build directory missing"); - return false; - } - // include directory - Base_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR"); - if (Base_.AutogenIncludeDir.empty()) { - Log().ErrorFile(GenT::GEN, InfoFile(), - "Autogen include directory missing"); - return false; - } - - // - Files + BaseConst_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); + BaseConst_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); + BaseConst_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); + BaseConst_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); + BaseConst_.AutogenBuildDir = InfoGet("AM_BUILD_DIR"); + if (BaseConst_.AutogenBuildDir.empty()) { + return LogInfoError("Autogen build directory missing."); + } + BaseConst_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR"); + if (BaseConst_.AutogenIncludeDir.empty()) { + return LogInfoError("Autogen include directory missing."); + } + BaseConst_.CMakeExecutable = InfoGetConfig("AM_CMAKE_EXECUTABLE"); + if (BaseConst_.CMakeExecutable.empty()) { + return LogInfoError("CMake executable file name missing."); + } + if (!BaseConst_.CMakeExecutableTime.Load(BaseConst_.CMakeExecutable)) { + std::string error = "The CMake executable "; + error += Quoted(BaseConst_.CMakeExecutable); + error += " does not exist."; + return LogInfoError(error); + } + BaseConst_.ParseCacheFile = InfoGetConfig("AM_PARSE_CACHE_FILE"); + if (BaseConst_.ParseCacheFile.empty()) { + return LogInfoError("Parse cache file name missing."); + } + + // - Settings file SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE"); if (SettingsFile_.empty()) { - Log().ErrorFile(GenT::GEN, InfoFile(), "Settings file name missing"); - return false; + return LogInfoError("Settings file name missing."); } // - Qt environment { - unsigned long qtv = Base_.QtVersionMajor; + unsigned long qtv = BaseConst_.QtVersionMajor; if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(), &qtv)) { - Base_.QtVersionMajor = static_cast<unsigned int>(qtv); + BaseConst_.QtVersionMajor = static_cast<unsigned int>(qtv); } } // - Moc - Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE"); - Moc_.Enabled = !Moc().Executable.empty(); - if (Moc().Enabled) { + MocConst_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE"); + if (!MocConst().Executable.empty()) { + MocConst_.Enabled = true; + // Load the executable file time + if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) { + std::string error = "The moc executable "; + error += Quoted(MocConst_.Executable); + error += " does not exist."; + return LogInfoError(error); + } for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) { - Moc_.SkipList.insert(std::move(sfl)); + MocConst_.SkipList.insert(std::move(sfl)); } - Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); - Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); - Moc_.Options = InfoGetList("AM_MOC_OPTIONS"); - Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); + MocConst_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); + MocConst_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); + MocConst_.Options = InfoGetList("AM_MOC_OPTIONS"); + MocConst_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) { - Moc_.MacroFilters.emplace_back( + MocConst_.MacroFilters.emplace_back( item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); } { - auto pushFilter = [this](std::string const& key, std::string const& exp, - std::string& error) { - if (!key.empty()) { - if (!exp.empty()) { - Moc_.DependFilters.emplace_back(); - KeyExpT& filter(Moc_.DependFilters.back()); - if (filter.Exp.compile(exp)) { - filter.Key = key; - } else { - error = "Regular expression compiling failed"; - } - } else { - error = "Regular expression is empty"; - } - } else { - error = "Key is empty"; + auto addFilter = [this, &LogInfoError](std::string const& key, + std::string const& exp) -> bool { + auto filterErr = [&LogInfoError, &key, &exp](const char* err) -> bool { + std::ostringstream ferr; + ferr << "AUTOMOC_DEPEND_FILTERS: " << err << '\n'; + ferr << " Key: " << Quoted(key) << '\n'; + ferr << " Exp: " << Quoted(exp) << '\n'; + return LogInfoError(ferr.str()); + }; + if (key.empty()) { + return filterErr("Key is empty"); + } + if (exp.empty()) { + return filterErr("Regular expression is empty"); } - if (!error.empty()) { - error = ("AUTOMOC_DEPEND_FILTERS: " + error); - error += "\n"; - error += " Key: "; - error += Quoted(key); - error += "\n"; - error += " Exp: "; - error += Quoted(exp); - error += "\n"; + this->MocConst_.DependFilters.emplace_back(key, exp); + if (!this->MocConst_.DependFilters.back().Exp.is_valid()) { + return filterErr("Regular expression compiling failed"); } + return true; }; - std::string error; // Insert default filter for Q_PLUGIN_METADATA - if (Base().QtVersionMajor != 4) { - pushFilter("Q_PLUGIN_METADATA", - "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" - "[^\\)]*FILE[ \t]*\"([^\"]+)\"", - error); + if (BaseConst().QtVersionMajor != 4) { + if (!addFilter("Q_PLUGIN_METADATA", + "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" + "[^\\)]*FILE[ \t]*\"([^\"]+)\"")) { + return false; + } } // Insert user defined dependency filters - { - std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS"); - if ((flts.size() % 2) == 0) { - for (std::vector<std::string>::iterator itC = flts.begin(), - itE = flts.end(); - itC != itE; itC += 2) { - pushFilter(*itC, *(itC + 1), error); - if (!error.empty()) { - break; - } - } - } else { - Log().ErrorFile( - GenT::MOC, InfoFile(), - "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); + std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS"); + if ((flts.size() % 2) != 0) { + return LogInfoError( + "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); + } + for (auto itC = flts.begin(), itE = flts.end(); itC != itE; itC += 2) { + if (!addFilter(*itC, *(itC + 1))) { return false; } } - if (!error.empty()) { - Log().ErrorFile(GenT::MOC, InfoFile(), error); - return false; - } - } - Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); - // Install moc predefs job - if (!Moc().PredefsCmd.empty()) { - WorkerPool().EmplaceJob<JobMocPredefsT>(); } + MocConst_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); } // - Uic - Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE"); - Uic_.Enabled = !Uic().Executable.empty(); - if (Uic().Enabled) { + UicConst_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE"); + if (!UicConst().Executable.empty()) { + UicConst_.Enabled = true; + // Load the executable file time + if (!UicConst_.ExecutableTime.Load(UicConst_.Executable)) { + std::string error = "The uic executable "; + error += Quoted(UicConst_.Executable); + error += " does not exist."; + return LogInfoError(error); + } for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) { - Uic_.SkipList.insert(std::move(sfl)); + UicConst_.SkipList.insert(std::move(sfl)); } - Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); - Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); + UicConst_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); + UicConst_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); { - auto sources = InfoGetList("AM_UIC_OPTIONS_FILES"); - auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS"); - // Compare list sizes - if (sources.size() != options.size()) { - std::ostringstream ost; - ost << "files/options lists sizes mismatch (" << sources.size() << "/" - << options.size() << ")"; - Log().ErrorFile(GenT::UIC, InfoFile(), ost.str()); + const char* keyFiles = "AM_UIC_OPTIONS_FILES"; + const char* keyOpts = "AM_UIC_OPTIONS_OPTIONS"; + auto sources = InfoGetList(keyFiles); + auto options = InfoGetLists(keyOpts); + if (!MatchSizes(keyFiles, keyOpts, sources.size(), options.size())) { return false; } auto fitEnd = sources.cend(); auto fit = sources.begin(); auto oit = options.begin(); while (fit != fitEnd) { - Uic_.Options[*fit] = std::move(*oit); + UicConst_.Options[*fit] = std::move(*oit); ++fit; ++oit; } @@ -1341,92 +1782,121 @@ bool cmQtAutoMocUic::Init(cmMakefile* makefile) } // - Headers and sources - // Add sources { - auto addSource = [this](std::string&& src, bool moc, bool uic) { - WorkerPool().EmplaceJob<JobParseT>(std::move(src), moc, uic, false); - }; - for (std::string& src : InfoGetList("AM_SOURCES")) { - addSource(std::move(src), true, true); - } - if (Moc().Enabled) { - for (std::string& src : InfoGetList("AM_MOC_SOURCES")) { - addSource(std::move(src), true, false); + auto makeSource = + [&LogInfoError](std::string const& fileName, + std::string const& fileFlags) -> SourceFileHandleT { + if (fileFlags.size() != 2) { + LogInfoError("Invalid file flags string size"); + return SourceFileHandleT(); } - } - if (Uic().Enabled) { - for (std::string& src : InfoGetList("AM_UIC_SOURCES")) { - addSource(std::move(src), false, true); + cmFileTime fileTime; + if (!fileTime.Load(fileName)) { + LogInfoError("The source file " + cmQtAutoGen::Quoted(fileName) + + " does not exist."); + return SourceFileHandleT(); } - } - } - // Add Fence job - WorkerPool().EmplaceJob<JobFenceT>(); - // Add headers - { - auto addHeader = [this](std::string&& hdr, bool moc, bool uic) { - WorkerPool().EmplaceJob<JobParseT>(std::move(hdr), moc, uic, true); + SourceFileHandleT sfh = std::make_shared<SourceFileT>(fileName); + sfh->FileTime = fileTime; + sfh->Moc = (fileFlags[0] == 'M'); + sfh->Uic = (fileFlags[1] == 'U'); + return sfh; }; - for (std::string& hdr : InfoGetList("AM_HEADERS")) { - addHeader(std::move(hdr), true, true); - } - if (Moc().Enabled) { - for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) { - addHeader(std::move(hdr), true, false); + + // Headers + { + // Get file lists + const char *keyFiles = "AM_HEADERS", *keyFlags = "AM_HEADERS_FLAGS"; + std::vector<std::string> files = InfoGetList(keyFiles); + std::vector<std::string> flags = InfoGetList(keyFlags); + std::vector<std::string> builds; + if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) { + return false; + } + if (MocConst().Enabled) { + const char* keyPaths = "AM_HEADERS_BUILD_PATHS"; + builds = InfoGetList(keyPaths); + if (!MatchSizes(keyFiles, keyPaths, files.size(), builds.size())) { + return false; + } + } + // Process file lists + for (std::size_t ii = 0; ii != files.size(); ++ii) { + std::string& fileName(files[ii]); + SourceFileHandleT sfh = makeSource(fileName, flags[ii]); + if (!sfh) { + return false; + } + if (MocConst().Enabled) { + sfh->BuildPath = std::move(builds[ii]); + if (sfh->BuildPath.empty()) { + Log().ErrorFile(GenT::GEN, this->InfoFile(), + "Header file build path is empty"); + return false; + } + } + BaseEval().Headers.emplace(std::move(fileName), std::move(sfh)); } } - if (Uic().Enabled) { - for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) { - addHeader(std::move(hdr), false, true); + + // Sources + { + const char *keyFiles = "AM_SOURCES", *keyFlags = "AM_SOURCES_FLAGS"; + std::vector<std::string> files = InfoGetList(keyFiles); + std::vector<std::string> flags = InfoGetList(keyFlags); + if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) { + return false; + } + // Process file lists + for (std::size_t ii = 0; ii != files.size(); ++ii) { + std::string& fileName(files[ii]); + SourceFileHandleT sfh = makeSource(fileName, flags[ii]); + if (!sfh) { + return false; + } + BaseEval().Sources.emplace(std::move(fileName), std::move(sfh)); } } } - // Addpost parse fence job - WorkerPool().EmplaceJob<JobPostParseT>(); // Init derived information // ------------------------ - // Init file path checksum generator - FileSys().setupFilePathChecksum( - Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir, - Base().ProjectBinaryDir); - // Moc variables - if (Moc().Enabled) { + if (MocConst().Enabled) { // Mocs compilation file - Moc_.CompFileAbs = Base().AbsoluteBuildPath("mocs_compilation.cpp"); + MocConst_.CompFileAbs = AbsoluteBuildPath("mocs_compilation.cpp"); // Moc predefs file - if (!Moc_.PredefsCmd.empty()) { - Moc_.PredefsFileRel = "moc_predefs"; - if (Base_.MultiConfig) { - Moc_.PredefsFileRel += '_'; - Moc_.PredefsFileRel += InfoConfig(); + if (!MocConst_.PredefsCmd.empty()) { + MocConst_.PredefsFileRel = "moc_predefs"; + if (BaseConst_.MultiConfig) { + MocConst_.PredefsFileRel += '_'; + MocConst_.PredefsFileRel += InfoConfig(); } - Moc_.PredefsFileRel += ".h"; - Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel); + MocConst_.PredefsFileRel += ".h"; + MocConst_.PredefsFileAbs = AbsoluteBuildPath(MocConst().PredefsFileRel); } // Sort include directories on demand - if (Base().IncludeProjectDirsBefore) { + if (BaseConst().IncludeProjectDirsBefore) { // Move strings to temporary list std::list<std::string> includes; - includes.insert(includes.end(), Moc().IncludePaths.begin(), - Moc().IncludePaths.end()); - Moc_.IncludePaths.clear(); - Moc_.IncludePaths.reserve(includes.size()); + includes.insert(includes.end(), MocConst().IncludePaths.begin(), + MocConst().IncludePaths.end()); + MocConst_.IncludePaths.clear(); + MocConst_.IncludePaths.reserve(includes.size()); // Append project directories only { std::array<std::string const*, 2> const movePaths = { - { &Base().ProjectBinaryDir, &Base().ProjectSourceDir } + { &BaseConst().ProjectBinaryDir, &BaseConst().ProjectSourceDir } }; for (std::string const* ppath : movePaths) { std::list<std::string>::iterator it = includes.begin(); while (it != includes.end()) { std::string const& path = *it; if (cmSystemTools::StringStartsWith(path, ppath->c_str())) { - Moc_.IncludePaths.push_back(path); + MocConst_.IncludePaths.push_back(path); it = includes.erase(it); } else { ++it; @@ -1435,124 +1905,176 @@ bool cmQtAutoMocUic::Init(cmMakefile* makefile) } } // Append remaining directories - Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(), - includes.end()); + MocConst_.IncludePaths.insert(MocConst_.IncludePaths.end(), + includes.begin(), includes.end()); } // Compose moc includes list { std::set<std::string> frameworkPaths; - for (std::string const& path : Moc().IncludePaths) { - Moc_.Includes.push_back("-I" + path); + for (std::string const& path : MocConst().IncludePaths) { + MocConst_.Includes.push_back("-I" + path); // Extract framework path if (cmHasLiteralSuffix(path, ".framework/Headers")) { // Go up twice to get to the framework root std::vector<std::string> pathComponents; - FileSys().SplitPath(path, pathComponents); - std::string frameworkPath = FileSys().JoinPath( - pathComponents.begin(), pathComponents.end() - 2); - frameworkPaths.insert(frameworkPath); + cmSystemTools::SplitPath(path, pathComponents); + frameworkPaths.emplace(cmSystemTools::JoinPath( + pathComponents.begin(), pathComponents.end() - 2)); } } // Append framework includes for (std::string const& path : frameworkPaths) { - Moc_.Includes.emplace_back("-F"); - Moc_.Includes.push_back(path); + MocConst_.Includes.emplace_back("-F"); + MocConst_.Includes.push_back(path); } } // Setup single list with all options { // Add includes - Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(), - Moc().Includes.end()); + MocConst_.AllOptions.insert(MocConst_.AllOptions.end(), + MocConst().Includes.begin(), + MocConst().Includes.end()); // Add definitions - for (std::string const& def : Moc().Definitions) { - Moc_.AllOptions.push_back("-D" + def); + for (std::string const& def : MocConst().Definitions) { + MocConst_.AllOptions.push_back("-D" + def); } // Add options - Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(), - Moc().Options.end()); + MocConst_.AllOptions.insert(MocConst_.AllOptions.end(), + MocConst().Options.begin(), + MocConst().Options.end()); } } return true; } +template <class JOBTYPE> +void cmQtAutoMocUic::CreateParseJobs(SourceFileMapT const& sourceMap) +{ + cmFileTime const parseCacheTime = BaseEval().ParseCacheTime; + ParseCacheT& parseCache = BaseEval().ParseCache; + for (auto& src : sourceMap) { + // Get or create the file parse data reference + ParseCacheT::GetOrInsertT cacheEntry = parseCache.GetOrInsert(src.first); + src.second->ParseData = std::move(cacheEntry.first); + // Create a parse job if the cache file was missing or is older + if (cacheEntry.second || src.second->FileTime.Newer(parseCacheTime)) { + BaseEval().ParseCacheChanged = true; + WorkerPool().EmplaceJob<JOBTYPE>(src.second); + } + } +} + +void cmQtAutoMocUic::InitJobs() +{ + // Add moc_predefs.h job + if (MocConst().Enabled && !MocConst().PredefsCmd.empty()) { + WorkerPool().EmplaceJob<JobMocPredefsT>(); + } + // Add header parse jobs + CreateParseJobs<JobParseHeaderT>(BaseEval().Headers); + // Add source parse jobs + CreateParseJobs<JobParseSourceT>(BaseEval().Sources); + // Add evaluate job + WorkerPool().EmplaceJob<JobEvaluateT>(); +} + bool cmQtAutoMocUic::Process() { SettingsFileRead(); + ParseCacheRead(); if (!CreateDirectories()) { return false; } + InitJobs(); if (!WorkerPool_.Process(this)) { return false; } if (JobError_) { return false; } - return SettingsFileWrite(); + if (!ParseCacheWrite()) { + return false; + } + if (!SettingsFileWrite()) { + return false; + } + return true; } void cmQtAutoMocUic::SettingsFileRead() { // Compose current settings strings { - cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); - std::string const sep(" ~~~ "); - if (Moc_.Enabled) { - std::string str; - str += Moc().Executable; - str += sep; - str += cmJoin(Moc().AllOptions, ";"); - str += sep; - str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE"; - str += sep; - str += cmJoin(Moc().PredefsCmd, ";"); - str += sep; - SettingsStringMoc_ = crypt.HashString(str); - } - if (Uic().Enabled) { - std::string str; - str += Uic().Executable; - str += sep; - str += cmJoin(Uic().TargetOptions, ";"); - for (const auto& item : Uic().Options) { - str += sep; - str += item.first; - str += sep; - str += cmJoin(item.second, ";"); - } - str += sep; - SettingsStringUic_ = crypt.HashString(str); + cmCryptoHash cryptoHash(cmCryptoHash::AlgoSHA256); + std::string const sep(";"); + auto cha = [&cryptoHash, &sep](std::string const& value) { + cryptoHash.Append(value); + cryptoHash.Append(sep); + }; + + if (MocConst_.Enabled) { + cryptoHash.Initialize(); + cha(MocConst().Executable); + for (auto const& value : MocConst().AllOptions) { + cha(value); + } + cha(BaseConst().IncludeProjectDirsBefore ? "TRUE" : "FALSE"); + for (auto const& value : MocConst().PredefsCmd) { + cha(value); + } + for (auto const& filter : MocConst().DependFilters) { + cha(filter.Key); + } + for (auto const& filter : MocConst().MacroFilters) { + cha(filter.Key); + } + SettingsStringMoc_ = cryptoHash.FinalizeHex(); + } + + if (UicConst().Enabled) { + cryptoHash.Initialize(); + cha(UicConst().Executable); + for (auto const& value : UicConst().TargetOptions) { + cha(value); + } + for (const auto& item : UicConst().Options) { + cha(item.first); + for (auto const& svalue : item.second) { + cha(svalue); + } + } + SettingsStringUic_ = cryptoHash.FinalizeHex(); } } // Read old settings and compare { std::string content; - if (FileSys().FileRead(content, SettingsFile_)) { - if (Moc().Enabled) { + if (cmQtAutoGenerator::FileRead(content, SettingsFile_)) { + if (MocConst().Enabled) { if (SettingsStringMoc_ != SettingsFind(content, "moc")) { - Moc_.SettingsChanged = true; + MocConst_.SettingsChanged = true; } } - if (Uic().Enabled) { + if (UicConst().Enabled) { if (SettingsStringUic_ != SettingsFind(content, "uic")) { - Uic_.SettingsChanged = true; + UicConst_.SettingsChanged = true; } } // In case any setting changed remove the old settings file. // This triggers a full rebuild on the next run if the current // build is aborted before writing the current settings in the end. - if (Moc().SettingsChanged || Uic().SettingsChanged) { - FileSys().FileRemove(SettingsFile_); + if (MocConst().SettingsChanged || UicConst().SettingsChanged) { + cmSystemTools::RemoveFile(SettingsFile_); } } else { // Settings file read failed - if (Moc().Enabled) { - Moc_.SettingsChanged = true; + if (MocConst().Enabled) { + MocConst_.SettingsChanged = true; } - if (Uic().Enabled) { - Uic_.SettingsChanged = true; + if (UicConst().Enabled) { + UicConst_.SettingsChanged = true; } } } @@ -1561,7 +2083,7 @@ void cmQtAutoMocUic::SettingsFileRead() bool cmQtAutoMocUic::SettingsFileWrite() { // Only write if any setting changed - if (Moc().SettingsChanged || Uic().SettingsChanged) { + if (MocConst().SettingsChanged || UicConst().SettingsChanged) { if (Log().Verbose()) { Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_)); } @@ -1582,11 +2104,54 @@ bool cmQtAutoMocUic::SettingsFileWrite() } // Write settings file std::string error; - if (!FileSys().FileWrite(SettingsFile_, content, &error)) { + if (!cmQtAutoGenerator::FileWrite(SettingsFile_, content, &error)) { Log().ErrorFile(GenT::GEN, SettingsFile_, "Settings file writing failed. " + error); // Remove old settings file to trigger a full rebuild on the next run - FileSys().FileRemove(SettingsFile_); + cmSystemTools::RemoveFile(SettingsFile_); + return false; + } + } + return true; +} + +void cmQtAutoMocUic::ParseCacheRead() +{ + const char* reason = nullptr; + // Don't read the cache if it is invalid + if (!BaseEval().ParseCacheTime.Load(BaseConst().ParseCacheFile)) { + reason = "Refreshing parse cache because it doesn't exist."; + } else if (MocConst().SettingsChanged || UicConst().SettingsChanged) { + reason = "Refreshing parse cache because the settings changed."; + } else if (BaseEval().ParseCacheTime.Older( + BaseConst().CMakeExecutableTime)) { + reason = + "Refreshing parse cache because it is older than the CMake executable."; + } + + if (reason != nullptr) { + // Don't read but refresh the complete parse cache + if (Log().Verbose()) { + Log().Info(GenT::GEN, reason); + } + BaseEval().ParseCacheChanged = true; + } else { + // Read parse cache + BaseEval().ParseCache.ReadFromFile(BaseConst().ParseCacheFile); + } +} + +bool cmQtAutoMocUic::ParseCacheWrite() +{ + if (BaseEval().ParseCacheChanged) { + if (Log().Verbose()) { + Log().Info(GenT::GEN, + "Writing parse cache file " + + Quoted(BaseConst().ParseCacheFile)); + } + if (!BaseEval().ParseCache.WriteToFile(BaseConst().ParseCacheFile)) { + Log().ErrorFile(GenT::GEN, BaseConst().ParseCacheFile, + "Parse cache file writing failed."); return false; } } @@ -1596,16 +2161,14 @@ bool cmQtAutoMocUic::SettingsFileWrite() bool cmQtAutoMocUic::CreateDirectories() { // Create AUTOGEN include directory - if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) { - Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir, + if (!cmSystemTools::MakeDirectory(BaseConst().AutogenIncludeDir)) { + Log().ErrorFile(GenT::GEN, BaseConst().AutogenIncludeDir, "Could not create directory."); return false; } return true; } -// Private method that requires cmQtAutoMocUic::JobsMutex_ to be -// locked void cmQtAutoMocUic::Abort(bool error) { if (error) { @@ -1614,132 +2177,20 @@ void cmQtAutoMocUic::Abort(bool error) WorkerPool_.Abort(); } -bool cmQtAutoMocUic::ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle) -{ - JobMocT const& mocJob(static_cast<JobMocT&>(*jobHandle)); - // Do additional tests if this is an included moc job - if (!mocJob.IncludeString.empty()) { - std::lock_guard<std::mutex> guard(MocMetaMutex_); - // Register included moc file - MocIncludedFiles_.emplace(mocJob.SourceFile); - - // Check if the same moc file would be generated from a different - // source file. - auto const range = MocIncludes_.equal_range(mocJob.IncludeString); - for (auto it = range.first; it != range.second; ++it) { - if (it->second[0] == mocJob.SourceFile) { - // The output file already gets generated - return true; - } - { - // The output file already gets generated - from a different source - // file! - std::string error = "The two source files\n "; - error += Quoted(mocJob.IncluderFile); - error += " and\n "; - error += Quoted(it->second[1]); - error += "\ncontain the same moc include string "; - error += Quoted(mocJob.IncludeString); - error += "\nbut the moc file would be generated from different " - "source files\n "; - error += Quoted(mocJob.SourceFile); - error += " and\n "; - error += Quoted(it->second[0]); - error += ".\nConsider to\n" - "- not include the \"moc_<NAME>.cpp\" file\n" - "- add a directory prefix to a \"<NAME>.moc\" include " - "(e.g \"sub/<NAME>.moc\")\n" - "- rename the source file(s)\n"; - Log().Error(GenT::MOC, error); - AbortError(); - return false; - } - } - - // We're still here so register this job - MocIncludes_.emplace_hint(range.first, mocJob.IncludeString, - std::array<std::string, 2>{ - { mocJob.SourceFile, mocJob.IncluderFile } }); - } - return WorkerPool_.PushJob(std::move(jobHandle)); -} - -bool cmQtAutoMocUic::ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle) -{ - const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle)); - { - std::lock_guard<std::mutex> guard(UicMetaMutex_); - // Check if the same uic file would be generated from a different - // source file. - auto const range = UicIncludes_.equal_range(uicJob.IncludeString); - for (auto it = range.first; it != range.second; ++it) { - if (it->second[0] == uicJob.SourceFile) { - // The output file already gets generated - return true; - } - { - // The output file already gets generated - from a different .ui - // file! - std::string error = "The two source files\n "; - error += Quoted(uicJob.IncluderFile); - error += " and\n "; - error += Quoted(it->second[1]); - error += "\ncontain the same uic include string "; - error += Quoted(uicJob.IncludeString); - error += "\nbut the uic file would be generated from different " - "source files\n "; - error += Quoted(uicJob.SourceFile); - error += " and\n "; - error += Quoted(it->second[0]); - error += - ".\nConsider to\n" - "- add a directory prefix to a \"ui_<NAME>.h\" include " - "(e.g \"sub/ui_<NAME>.h\")\n" - "- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" " - "include(s)\n"; - Log().Error(GenT::UIC, error); - AbortError(); - return false; - } - } - - // We're still here so register this job - UicIncludes_.emplace_hint(range.first, uicJob.IncludeString, - std::array<std::string, 2>{ - { uicJob.SourceFile, uicJob.IncluderFile } }); - } - return WorkerPool_.PushJob(std::move(jobHandle)); -} - -bool cmQtAutoMocUic::ParallelMocIncluded(std::string const& sourceFile) +std::string cmQtAutoMocUic::AbsoluteBuildPath( + std::string const& relativePath) const { - std::lock_guard<std::mutex> guard(MocMetaMutex_); - return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end()); + std::string res(BaseConst().AutogenBuildDir); + res += '/'; + res += relativePath; + return res; } -std::string cmQtAutoMocUic::ParallelMocAutoRegister( - std::string const& baseName) +std::string cmQtAutoMocUic::AbsoluteIncludePath( + std::string const& relativePath) const { - std::string res; - { - std::lock_guard<std::mutex> mocLock(MocMetaMutex_); - res = baseName; - res += ".cpp"; - if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) { - MocAutoFiles_.emplace(res); - } else { - // Append number suffix to the file name - for (unsigned int ii = 2; ii != 1024; ++ii) { - res = baseName; - res += '_'; - res += std::to_string(ii); - res += ".cpp"; - if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) { - MocAutoFiles_.emplace(res); - break; - } - } - } - } + std::string res(BaseConst().AutogenIncludeDir); + res += '/'; + res += relativePath; return res; } diff --git a/Source/cmQtAutoMocUic.h b/Source/cmQtAutoMocUic.h index 3902abb..8061c13 100644 --- a/Source/cmQtAutoMocUic.h +++ b/Source/cmQtAutoMocUic.h @@ -5,25 +5,28 @@ #include "cmConfigure.h" // IWYU pragma: keep +#include "cmFileTime.h" #include "cmQtAutoGen.h" #include "cmQtAutoGenerator.h" #include "cmWorkerPool.h" #include "cmsys/RegularExpression.hxx" -#include <array> #include <atomic> +#include <cstddef> #include <map> #include <memory> // IWYU pragma: keep -#include <mutex> #include <set> #include <string> +#include <unordered_map> #include <unordered_set> #include <utility> #include <vector> class cmMakefile; -// @brief AUTOMOC and AUTOUIC generator +/** \class cmQtAutoMocUic + * \brief AUTOMOC and AUTOUIC generator + */ class cmQtAutoMocUic : public cmQtAutoGenerator { public: @@ -35,10 +38,10 @@ public: public: // -- Types - typedef std::multimap<std::string, std::array<std::string, 2>> IncludesMap; - /// @brief Search key plus regular expression pair - /// + /** + * Search key plus regular expression pair + */ struct KeyExpT { KeyExpT() = default; @@ -59,35 +62,123 @@ public: cmsys::RegularExpression Exp; }; - /// @brief Common settings - /// - class BaseSettingsT + /** + * Include string with sub parts + */ + struct IncludeKeyT + { + IncludeKeyT(std::string const& key, std::size_t basePrefixLength); + + std::string Key; // Full include string + std::string Dir; // Include directory + std::string Base; // Base part of the include file name + }; + + /** + * Source file parsing cache + */ + class ParseCacheT + { + public: + // -- Types + /** + * Entry of the file parsing cache + */ + struct FileT + { + void Clear(); + + struct MocT + { + std::string Macro; + struct IncludeT + { + std::vector<IncludeKeyT> Underscore; + std::vector<IncludeKeyT> Dot; + } Include; + std::vector<std::string> Depends; + } Moc; + + struct UicT + { + std::vector<IncludeKeyT> Include; + std::vector<std::string> Depends; + } Uic; + }; + typedef std::shared_ptr<FileT> FileHandleT; + typedef std::pair<FileHandleT, bool> GetOrInsertT; + + public: + ParseCacheT(); + ~ParseCacheT(); + + void Clear(); + + bool ReadFromFile(std::string const& fileName); + bool WriteToFile(std::string const& fileName); + + //! Might return an invalid handle + FileHandleT Get(std::string const& fileName) const; + //! Always returns a valid handle + GetOrInsertT GetOrInsert(std::string const& fileName); + + private: + std::unordered_map<std::string, FileHandleT> Map_; + }; + + /** + * Source file data + */ + class SourceFileT { public: - // -- Volatile methods - BaseSettingsT(FileSystem* fileSystem) - : MultiConfig(false) - , IncludeProjectDirsBefore(false) - , QtVersionMajor(4) - , NumThreads(1) - , FileSys(fileSystem) + SourceFileT(std::string fileName) + : FileName(std::move(fileName)) { } + public: + std::string FileName; + cmFileTime FileTime; + ParseCacheT::FileHandleT ParseData; + std::string BuildPath; + bool Moc = false; + bool Uic = false; + }; + typedef std::shared_ptr<SourceFileT> SourceFileHandleT; + typedef std::map<std::string, SourceFileHandleT> SourceFileMapT; + + /** + * Meta compiler file mapping information + */ + struct MappingT + { + SourceFileHandleT SourceFile; + std::string OutputFile; + std::string IncludeString; + std::vector<SourceFileHandleT> IncluderFiles; + }; + typedef std::shared_ptr<MappingT> MappingHandleT; + typedef std::map<std::string, MappingHandleT> MappingMapT; + + /** + * Common settings + */ + class BaseSettingsT + { + public: + // -- Constructors + BaseSettingsT(); + ~BaseSettingsT(); + BaseSettingsT(BaseSettingsT const&) = delete; BaseSettingsT& operator=(BaseSettingsT const&) = delete; - // -- Const methods - std::string AbsoluteBuildPath(std::string const& relativePath) const; - bool FindHeader(std::string& header, - std::string const& testBasePath) const; - // -- Attributes // - Config - bool MultiConfig; - bool IncludeProjectDirsBefore; - unsigned int QtVersionMajor; - unsigned int NumThreads; + bool MultiConfig = false; + bool IncludeProjectDirsBefore = false; + unsigned int QtVersionMajor = 4; // - Directories std::string ProjectSourceDir; std::string ProjectBinaryDir; @@ -96,37 +187,50 @@ public: std::string AutogenBuildDir; std::string AutogenIncludeDir; // - Files + std::string CMakeExecutable; + cmFileTime CMakeExecutableTime; + std::string ParseCacheFile; std::vector<std::string> HeaderExtensions; - // - File system - FileSystem* FileSys; }; - /// @brief Moc settings - /// + /** + * Shared common variables + */ + class BaseEvalT + { + public: + // -- Parse Cache + bool ParseCacheChanged = false; + cmFileTime ParseCacheTime; + ParseCacheT ParseCache; + + // -- Sources + SourceFileMapT Headers; + SourceFileMapT Sources; + }; + + /** + * Moc settings + */ class MocSettingsT { public: - MocSettingsT(FileSystem* fileSys) - : FileSys(fileSys) - { - } + // -- Constructors + MocSettingsT(); + ~MocSettingsT(); MocSettingsT(MocSettingsT const&) = delete; MocSettingsT& operator=(MocSettingsT const&) = delete; // -- Const methods bool skipped(std::string const& fileName) const; - std::string FindMacro(std::string const& content) const; std::string MacrosString() const; - std::string FindIncludedFile(std::string const& sourcePath, - std::string const& includeString) const; - void FindDependencies(std::string const& content, - std::set<std::string>& depends) const; // -- Attributes bool Enabled = false; bool SettingsChanged = false; bool RelaxedMode = false; + cmFileTime ExecutableTime; std::string Executable; std::string CompFileAbs; std::string PredefsFileRel; @@ -141,16 +245,35 @@ public: std::vector<KeyExpT> DependFilters; std::vector<KeyExpT> MacroFilters; cmsys::RegularExpression RegExpInclude; - // - File system - FileSystem* FileSys; }; - /// @brief Uic settings - /// + /** + * Moc shared variables + */ + class MocEvalT + { + public: + // -- predefines file + cmFileTime PredefsTime; + // -- Mappings + MappingMapT HeaderMappings; + MappingMapT SourceMappings; + MappingMapT Includes; + // -- Discovered files + SourceFileMapT HeadersDiscovered; + // -- Mocs compilation + bool CompUpdated = false; + std::vector<std::string> CompFiles; + }; + + /** + * Uic settings + */ class UicSettingsT { public: - UicSettingsT() = default; + UicSettingsT(); + ~UicSettingsT(); UicSettingsT(UicSettingsT const&) = delete; UicSettingsT& operator=(UicSettingsT const&) = delete; @@ -161,6 +284,7 @@ public: // -- Attributes bool Enabled = false; bool SettingsChanged = false; + cmFileTime ExecutableTime; std::string Executable; std::unordered_set<std::string> SkipList; std::vector<std::string> TargetOptions; @@ -169,8 +293,19 @@ public: cmsys::RegularExpression RegExpInclude; }; - /// @brief Abstract job class for concurrent job processing - /// + /** + * Uic shared variables + */ + class UicEvalT + { + public: + SourceFileMapT UiFiles; + MappingMapT Includes; + }; + + /** + * Abstract job class for concurrent job processing + */ class JobT : public cmWorkerPool::JobT { protected: @@ -188,10 +323,14 @@ public: return static_cast<cmQtAutoMocUic*>(UserData()); }; - //! Get the file system interface. Only valid during Process() call! - FileSystem& FileSys() { return Gen()->FileSys(); } - //! Get the logger. Only valid during Process() call! - Logger& Log() { return Gen()->Log(); } + // -- Accessors. Only valid during Process() call! + Logger const& Log() const { return Gen()->Log(); } + BaseSettingsT const& BaseConst() const { return Gen()->BaseConst(); } + BaseEvalT& BaseEval() const { return Gen()->BaseEval(); } + MocSettingsT const& MocConst() const { return Gen()->MocConst(); } + MocEvalT& MocEval() const { return Gen()->MocEval(); } + UicSettingsT const& UicConst() const { return Gen()->UicConst(); } + UicEvalT& UicEval() const { return Gen()->UicEval(); } // -- Error logging with automatic abort void LogError(GenT genType, std::string const& message) const; @@ -205,11 +344,13 @@ public: * @brief Run an external process. Use only during Process() call! */ bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, - std::vector<std::string> const& command); + std::vector<std::string> const& command, + std::string* infoMessage = nullptr); }; - /// @brief Fence job utility class - /// + /** + * Fence job utility class + */ class JobFenceT : public JobT { public: @@ -220,121 +361,152 @@ public: void Process() override{}; }; - /// @brief Generate moc_predefs.h - /// - class JobMocPredefsT : public JobT + /** + * Generate moc_predefs.h + */ + class JobMocPredefsT : public JobFenceT { - private: void Process() override; + bool Update(std::string* reason) const; }; - /// @brief Parses a source file - /// + /** + * File parse job base class + */ class JobParseT : public JobT { public: - JobParseT(std::string fileName, bool moc, bool uic, bool header = false) - : FileName(std::move(fileName)) - , AutoMoc(moc) - , AutoUic(uic) - , Header(header) + JobParseT(SourceFileHandleT fileHandle) + : FileHandle(std::move(fileHandle)) { } - private: - struct MetaT - { - std::string Content; - std::string FileDir; - std::string FileBase; - }; + protected: + bool ReadFile(); + void CreateKeys(std::vector<IncludeKeyT>& container, + std::set<std::string> const& source, + std::size_t basePrefixLength); + void MocMacro(); + void MocDependecies(); + void MocIncludes(); + void UicIncludes(); + + protected: + SourceFileHandleT FileHandle; + std::string Content; + }; + /** + * Header file parse job + */ + class JobParseHeaderT : public JobParseT + { + public: + using JobParseT::JobParseT; void Process() override; - bool ParseMocSource(MetaT const& meta); - bool ParseMocHeader(MetaT const& meta); - std::string MocStringHeaders(std::string const& fileBase) const; - std::string MocFindIncludedHeader(std::string const& includerDir, - std::string const& includeBase); - bool ParseUic(MetaT const& meta); - bool ParseUicInclude(MetaT const& meta, std::string&& includeString); - std::string UicFindIncludedFile(MetaT const& meta, - std::string const& includeString); + }; - private: - std::string FileName; - bool AutoMoc = false; - bool AutoUic = false; - bool Header = false; + /** + * Source file parse job + */ + class JobParseSourceT : public JobParseT + { + public: + using JobParseT::JobParseT; + void Process() override; }; - /// @brief Generates additional jobs after all files have been parsed - /// - class JobPostParseT : public JobFenceT + /** + * Evaluate parsed files + */ + class JobEvaluateT : public JobFenceT { - private: void Process() override; + + // -- Moc + bool MocEvalHeader(SourceFileHandleT source); + bool MocEvalSource(SourceFileHandleT const& source); + SourceFileHandleT MocFindIncludedHeader( + std::string const& includerDir, std::string const& includeBase) const; + SourceFileHandleT MocFindHeader(std::string const& basePath) const; + std::string MocMessageTestHeaders(std::string const& fileBase) const; + bool MocRegisterIncluded(std::string const& includeString, + SourceFileHandleT includerFileHandle, + SourceFileHandleT sourceFileHandle, + bool sourceIsHeader) const; + void MocRegisterMapping(MappingHandleT mappingHandle, + bool sourceIsHeader) const; + + // -- Uic + bool UicEval(SourceFileMapT const& fileMap); + bool UicEvalFile(SourceFileHandleT sourceFileHandle); + SourceFileHandleT UicFindIncludedUi(std::string const& sourceFile, + std::string const& sourceDir, + IncludeKeyT const& incKey) const; + bool UicRegisterMapping(std::string const& includeString, + SourceFileHandleT uiFileHandle, + SourceFileHandleT includerFileHandle); }; - /// @brief Generate mocs_compilation.cpp - /// - class JobMocsCompilationT : public JobFenceT + /** + * Generates moc/uic jobs + */ + class JobGenerateT : public JobFenceT { - private: void Process() override; + // -- Moc + bool MocGenerate(MappingHandleT const& mapping, bool compFile) const; + bool MocUpdate(MappingT const& mapping, std::string* reason) const; + std::pair<std::string, cmFileTime> MocFindDependency( + std::string const& sourceDir, std::string const& includeString) const; + // -- Uic + bool UicGenerate(MappingHandleT const& mapping) const; + bool UicUpdate(MappingT const& mapping, std::string* reason) const; }; - /// @brief Moc a file job - /// - class JobMocT : public JobT + /** + * File compiling base job + */ + class JobCompileT : public JobT { public: - JobMocT(std::string sourceFile, std::string includerFile, - std::string includeString) - : SourceFile(std::move(sourceFile)) - , IncluderFile(std::move(includerFile)) - , IncludeString(std::move(includeString)) + JobCompileT(MappingHandleT uicMapping, std::unique_ptr<std::string> reason) + : Mapping(std::move(uicMapping)) + , Reason(std::move(reason)) { } - void FindDependencies(std::string const& content); + protected: + MappingHandleT Mapping; + std::unique_ptr<std::string> Reason; + }; - private: + /** + * moc compiles a file + */ + class JobMocT : public JobCompileT + { + public: + using JobCompileT::JobCompileT; void Process() override; - bool UpdateRequired(); - void GenerateMoc(); + }; + /** + * uic compiles a file + */ + class JobUicT : public JobCompileT + { public: - std::string SourceFile; - std::string IncluderFile; - std::string IncludeString; - std::string BuildFile; - bool DependsValid = false; - std::set<std::string> Depends; + using JobCompileT::JobCompileT; + void Process() override; }; - /// @brief Uic a file job + /// @brief Generate mocs_compilation.cpp /// - class JobUicT : public JobT + class JobMocsCompilationT : public JobFenceT { - public: - JobUicT(std::string sourceFile, std::string includerFile, - std::string includeString) - : SourceFile(std::move(sourceFile)) - , IncluderFile(std::move(includerFile)) - , IncludeString(std::move(includeString)) - { - } - private: void Process() override; - bool UpdateRequired(); - void GenerateUic(); - - public: - std::string SourceFile; - std::string IncluderFile; - std::string IncludeString; - std::string BuildFile; }; /// @brief The last job @@ -346,39 +518,37 @@ public: }; // -- Const settings interface - const BaseSettingsT& Base() const { return this->Base_; } - const MocSettingsT& Moc() const { return this->Moc_; } - const UicSettingsT& Uic() const { return this->Uic_; } + BaseSettingsT const& BaseConst() const { return this->BaseConst_; } + BaseEvalT& BaseEval() { return this->BaseEval_; } + MocSettingsT const& MocConst() const { return this->MocConst_; } + MocEvalT& MocEval() { return this->MocEval_; } + UicSettingsT const& UicConst() const { return this->UicConst_; } + UicEvalT& UicEval() { return this->UicEval_; } // -- Parallel job processing interface cmWorkerPool& WorkerPool() { return WorkerPool_; } void AbortError() { Abort(true); } void AbortSuccess() { Abort(false); } - bool ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle); - bool ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle); - // -- Mocs compilation include file updated flag - void ParallelMocAutoUpdated() { MocAutoFileUpdated_.store(true); } - bool MocAutoFileUpdated() const { return MocAutoFileUpdated_.load(); } - - // -- Mocs compilation file register - std::string ParallelMocAutoRegister(std::string const& baseName); - bool ParallelMocIncluded(std::string const& sourceFile); - std::set<std::string> const& MocAutoFiles() const - { - return this->MocAutoFiles_; - } + // -- Utility + std::string AbsoluteBuildPath(std::string const& relativePath) const; + std::string AbsoluteIncludePath(std::string const& relativePath) const; + template <class JOBTYPE> + void CreateParseJobs(SourceFileMapT const& sourceMap); private: // -- Utility accessors - Logger& Log() { return Logger_; } - FileSystem& FileSys() { return FileSys_; } + Logger const& Log() const { return Logger_; } // -- Abstract processing interface bool Init(cmMakefile* makefile) override; + void InitJobs(); bool Process() override; // -- Settings file void SettingsFileRead(); bool SettingsFileWrite(); + // -- Parse cache + void ParseCacheRead(); + bool ParseCacheWrite(); // -- Thread processing void Abort(bool error); // -- Generation @@ -387,25 +557,18 @@ private: private: // -- Utility Logger Logger_; - FileSystem FileSys_; // -- Settings - BaseSettingsT Base_; - MocSettingsT Moc_; - UicSettingsT Uic_; - // -- Moc meta - std::mutex MocMetaMutex_; - std::set<std::string> MocIncludedFiles_; - IncludesMap MocIncludes_; - std::set<std::string> MocAutoFiles_; - std::atomic<bool> MocAutoFileUpdated_ = ATOMIC_VAR_INIT(false); - // -- Uic meta - std::mutex UicMetaMutex_; - IncludesMap UicIncludes_; + BaseSettingsT BaseConst_; + BaseEvalT BaseEval_; + MocSettingsT MocConst_; + MocEvalT MocEval_; + UicSettingsT UicConst_; + UicEvalT UicEval_; // -- Settings file std::string SettingsFile_; std::string SettingsStringMoc_; std::string SettingsStringUic_; - // -- Thread pool and job queue + // -- Worker thread pool std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false); cmWorkerPool WorkerPool_; }; diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx index e58324d..7ac7339 100644 --- a/Source/cmQtAutoRcc.cxx +++ b/Source/cmQtAutoRcc.cxx @@ -3,6 +3,8 @@ #include "cmQtAutoRcc.h" #include "cmQtAutoGen.h" +#include <sstream> + #include "cmAlgorithms.h" #include "cmCryptoHash.h" #include "cmDuration.h" @@ -49,32 +51,41 @@ bool cmQtAutoRcc::Init(cmMakefile* makefile) cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); return list; }; + auto LogInfoError = [this](std::string const& msg) -> bool { + std::ostringstream err; + err << "In " << Quoted(this->InfoFile()) << ":\n" << msg; + this->Log().Error(GenT::RCC, err.str()); + return false; + }; // -- Read info file if (!makefile->ReadListFile(InfoFile())) { - Log().ErrorFile(GenT::RCC, InfoFile(), "File processing failed."); - return false; + return LogInfoError("File processing failed."); } // - Configurations - Log().RaiseVerbosity(InfoGet("ARCC_VERBOSITY")); + Logger_.RaiseVerbosity(InfoGet("ARCC_VERBOSITY")); MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG"); // - Directories AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR"); if (AutogenBuildDir_.empty()) { - Log().ErrorFile(GenT::RCC, InfoFile(), "Build directory empty."); - return false; + return LogInfoError("Build directory empty."); } IncludeDir_ = InfoGetConfig("ARCC_INCLUDE_DIR"); if (IncludeDir_.empty()) { - Log().ErrorFile(GenT::RCC, InfoFile(), "Include directory empty."); - return false; + return LogInfoError("Include directory empty."); } // - Rcc executable RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE"); + if (!RccExecutableTime_.Load(RccExecutable_)) { + std::string error = "The rcc executable "; + error += Quoted(RccExecutable_); + error += " does not exist."; + return LogInfoError(error); + } RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS"); // - Job @@ -92,28 +103,22 @@ bool cmQtAutoRcc::Init(cmMakefile* makefile) // - Validity checks if (LockFile_.empty()) { - Log().ErrorFile(GenT::RCC, InfoFile(), "Lock file name missing."); - return false; + return LogInfoError("Lock file name missing."); } if (SettingsFile_.empty()) { - Log().ErrorFile(GenT::RCC, InfoFile(), "Settings file name missing."); - return false; + return LogInfoError("Settings file name missing."); } if (AutogenBuildDir_.empty()) { - Log().ErrorFile(GenT::RCC, InfoFile(), "Autogen build directory missing."); - return false; + return LogInfoError("Autogen build directory missing."); } if (RccExecutable_.empty()) { - Log().ErrorFile(GenT::RCC, InfoFile(), "rcc executable missing."); - return false; + return LogInfoError("rcc executable missing."); } if (QrcFile_.empty()) { - Log().ErrorFile(GenT::RCC, InfoFile(), "rcc input file missing."); - return false; + return LogInfoError("rcc input file missing."); } if (RccFileName_.empty()) { - Log().ErrorFile(GenT::RCC, InfoFile(), "rcc output file missing."); - return false; + return LogInfoError("rcc output file missing."); } // Init derived information @@ -301,12 +306,10 @@ bool cmQtAutoRcc::TestQrcRccFiles(bool& generate) // Test if the rcc output file exists if (!RccFileTime_.Load(RccFileOutput_)) { if (Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(RccFileOutput_); - reason += " from its source file "; - reason += Quoted(QrcFile_); - reason += " because it doesn't exist"; - Log().Info(GenT::RCC, reason); + Reason = "Generating "; + Reason += Quoted(RccFileOutput_); + Reason += ", because it doesn't exist, from "; + Reason += Quoted(QrcFile_); } generate = true; return true; @@ -315,12 +318,10 @@ bool cmQtAutoRcc::TestQrcRccFiles(bool& generate) // Test if the settings changed if (SettingsChanged_) { if (Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(RccFileOutput_); - reason += " from "; - reason += Quoted(QrcFile_); - reason += " because the RCC settings changed"; - Log().Info(GenT::RCC, reason); + Reason = "Generating "; + Reason += Quoted(RccFileOutput_); + Reason += ", because the rcc settings changed, from "; + Reason += Quoted(QrcFile_); } generate = true; return true; @@ -329,11 +330,24 @@ bool cmQtAutoRcc::TestQrcRccFiles(bool& generate) // Test if the rcc output file is older than the .qrc file if (RccFileTime_.Older(QrcFileTime_)) { if (Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(RccFileOutput_); - reason += " because it is older than "; - reason += Quoted(QrcFile_); - Log().Info(GenT::RCC, reason); + Reason = "Generating "; + Reason += Quoted(RccFileOutput_); + Reason += ", because it is older than "; + Reason += Quoted(QrcFile_); + Reason += ", from "; + Reason += Quoted(QrcFile_); + } + generate = true; + return true; + } + + // Test if the rcc output file is older than the rcc executable + if (RccFileTime_.Older(RccExecutableTime_)) { + if (Log().Verbose()) { + Reason = "Generating "; + Reason += Quoted(RccFileOutput_); + Reason += ", because it is older than the rcc executable, from "; + Reason += Quoted(QrcFile_); } generate = true; return true; @@ -354,6 +368,7 @@ bool cmQtAutoRcc::TestResources(bool& generate) } } + // Check if any resource file is newer than the rcc output file for (std::string const& resFile : Inputs_) { // Check if the resource file exists cmFileTime fileTime; @@ -365,16 +380,15 @@ bool cmQtAutoRcc::TestResources(bool& generate) Log().ErrorFile(GenT::RCC, QrcFile_, error); return false; } - // Check if the resource file is newer than the build file + // Check if the resource file is newer than the rcc output file if (RccFileTime_.Older(fileTime)) { if (Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(RccFileOutput_); - reason += " from "; - reason += Quoted(QrcFile_); - reason += " because it is older than "; - reason += Quoted(resFile); - Log().Info(GenT::RCC, reason); + Reason = "Generating "; + Reason += Quoted(RccFileOutput_); + Reason += ", because it is older than "; + Reason += Quoted(resFile); + Reason += ", from "; + Reason += Quoted(QrcFile_); } generate = true; break; @@ -386,17 +400,7 @@ bool cmQtAutoRcc::TestResources(bool& generate) bool cmQtAutoRcc::TestInfoFile() { // Test if the rcc output file is older than the info file - - cmFileTime infoFileTime; - if (!infoFileTime.Load(InfoFile())) { - std::string error; - error = "Could not find the info file "; - error += Quoted(InfoFile()); - error += '\n'; - Log().ErrorFile(GenT::RCC, QrcFile_, error); - return false; - } - if (RccFileTime_.Older(infoFileTime)) { + if (RccFileTime_.Older(InfoFileTime())) { if (Log().Verbose()) { std::string reason = "Touching "; reason += Quoted(RccFileOutput_); @@ -424,7 +428,7 @@ bool cmQtAutoRcc::GenerateRcc() return false; } - // Start a rcc process + // Compose rcc command std::vector<std::string> cmd; cmd.push_back(RccExecutable_); cmd.insert(cmd.end(), Options_.begin(), Options_.end()); @@ -432,12 +436,15 @@ bool cmQtAutoRcc::GenerateRcc() cmd.push_back(RccFileOutput_); cmd.push_back(QrcFile_); - // Log command + // Log reason and command if (Log().Verbose()) { - std::string msg = "Running command:\n"; + std::string msg = Reason; + if (!msg.empty() && (msg.back() != '\n')) { + msg += '\n'; + } msg += QuotedCommand(cmd); msg += '\n'; - cmSystemTools::Stdout(msg); + Log().Info(GenT::RCC, msg); } std::string rccStdOut; diff --git a/Source/cmQtAutoRcc.h b/Source/cmQtAutoRcc.h index 8dc9179..636a667 100644 --- a/Source/cmQtAutoRcc.h +++ b/Source/cmQtAutoRcc.h @@ -26,7 +26,7 @@ public: private: // -- Utility - Logger& Log() { return Logger_; } + Logger const& Log() const { return Logger_; } bool IsMultiConfig() const { return MultiConfig_; } std::string MultiConfigOutput() const; @@ -54,6 +54,7 @@ private: std::string IncludeDir_; // -- Qt environment std::string RccExecutable_; + cmFileTime RccExecutableTime_; std::vector<std::string> RccListOptions_; // -- Job std::string LockFile_; @@ -67,6 +68,7 @@ private: std::string RccFileOutput_; std::string RccFilePublic_; cmFileTime RccFileTime_; + std::string Reason; std::vector<std::string> Options_; std::vector<std::string> Inputs_; // -- Settings file diff --git a/Source/cmUVHandlePtr.cxx b/Source/cmUVHandlePtr.cxx index 27069ee..db67463 100644 --- a/Source/cmUVHandlePtr.cxx +++ b/Source/cmUVHandlePtr.cxx @@ -211,7 +211,6 @@ uv_pipe_ptr::operator uv_stream_t*() const return reinterpret_cast<uv_stream_t*>(handle.get()); } -#ifdef CMAKE_BUILD_WITH_CMAKE int uv_process_ptr::spawn(uv_loop_t& loop, uv_process_options_t const& options, void* data) { @@ -231,6 +230,7 @@ int uv_timer_ptr::start(uv_timer_cb cb, uint64_t timeout, uint64_t repeat) return uv_timer_start(*this, cb, timeout, repeat); } +#ifdef CMAKE_BUILD_WITH_CMAKE uv_tty_ptr::operator uv_stream_t*() const { return reinterpret_cast<uv_stream_t*>(handle.get()); @@ -255,13 +255,13 @@ UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(pipe) UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(stream) -#ifdef CMAKE_BUILD_WITH_CMAKE -UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(async) - UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(process) UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(timer) +#ifdef CMAKE_BUILD_WITH_CMAKE +UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(async) + UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(tty) #endif } diff --git a/Source/cmUVProcessChain.cxx b/Source/cmUVProcessChain.cxx new file mode 100644 index 0000000..c4e30d4 --- /dev/null +++ b/Source/cmUVProcessChain.cxx @@ -0,0 +1,392 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmUVProcessChain.h" + +#include "cmAlgorithms.h" +#include "cmGetPipes.h" +#include "cmUVHandlePtr.h" +#include "cmUVStreambuf.h" +#include "cm_uv.h" + +#include <iterator> +#include <memory> +#include <utility> + +struct cmUVProcessChain::InternalData +{ + struct BasicStreamData + { + cmUVStreambuf Streambuf; + cm::uv_pipe_ptr BuiltinStream; + uv_stdio_container_t Stdio; + }; + + template <typename IOStream> + struct StreamData : public BasicStreamData + { + StreamData() + : BuiltinIOStream(&this->Streambuf) + { + } + + IOStream BuiltinIOStream; + + IOStream* GetBuiltinStream() + { + if (this->BuiltinStream.get()) { + return &this->BuiltinIOStream; + } + return nullptr; + } + }; + + struct ProcessData + { + cmUVProcessChain::InternalData* Data; + cm::uv_process_ptr Process; + cm::uv_pipe_ptr OutputPipe; + bool Finished = false; + Status ProcessStatus; + }; + + const cmUVProcessChainBuilder* Builder = nullptr; + + bool Valid = false; + + cm::uv_loop_ptr Loop; + + StreamData<std::istream> OutputStreamData; + StreamData<std::istream> ErrorStreamData; + + unsigned int ProcessesCompleted = 0; + std::vector<std::unique_ptr<ProcessData>> Processes; + + bool Prepare(const cmUVProcessChainBuilder* builder); + bool AddCommand(const cmUVProcessChainBuilder::ProcessConfiguration& config, + bool first, bool last); + bool Finish(); + + static const Status* GetStatus(const ProcessData& data); +}; + +cmUVProcessChainBuilder::cmUVProcessChainBuilder() +{ + this->SetNoStream(Stream_INPUT) + .SetNoStream(Stream_OUTPUT) + .SetNoStream(Stream_ERROR); +} + +cmUVProcessChainBuilder& cmUVProcessChainBuilder::AddCommand( + const std::vector<std::string>& arguments) +{ + if (!arguments.empty()) { + this->Processes.emplace_back(); + this->Processes.back().Arguments = arguments; + } + return *this; +} + +cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetNoStream(Stream stdio) +{ + switch (stdio) { + case Stream_INPUT: + case Stream_OUTPUT: + case Stream_ERROR: { + auto& streamData = this->Stdio[stdio]; + streamData.Type = None; + break; + } + } + return *this; +} + +cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetBuiltinStream( + Stream stdio) +{ + switch (stdio) { + case Stream_INPUT: + // FIXME + break; + + case Stream_OUTPUT: + case Stream_ERROR: { + auto& streamData = this->Stdio[stdio]; + streamData.Type = Builtin; + break; + } + } + return *this; +} + +cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetExternalStream( + Stream stdio, int fd) +{ + switch (stdio) { + case Stream_INPUT: + // FIXME + break; + + case Stream_OUTPUT: + case Stream_ERROR: { + auto& streamData = this->Stdio[stdio]; + streamData.Type = External; + streamData.FileDescriptor = fd; + break; + } + } + return *this; +} + +cmUVProcessChain cmUVProcessChainBuilder::Start() const +{ + cmUVProcessChain chain; + + if (!chain.Data->Prepare(this)) { + return chain; + } + + for (auto it = this->Processes.begin(); it != this->Processes.end(); ++it) { + if (!chain.Data->AddCommand(*it, it == this->Processes.begin(), + it == std::prev(this->Processes.end()))) { + return chain; + } + } + + chain.Data->Finish(); + + return chain; +} + +const cmUVProcessChain::Status* cmUVProcessChain::InternalData::GetStatus( + const cmUVProcessChain::InternalData::ProcessData& data) +{ + if (data.Finished) { + return &data.ProcessStatus; + } + return nullptr; +} + +bool cmUVProcessChain::InternalData::Prepare( + const cmUVProcessChainBuilder* builder) +{ + this->Builder = builder; + + auto const& output = + this->Builder->Stdio[cmUVProcessChainBuilder::Stream_OUTPUT]; + auto& outputData = this->OutputStreamData; + switch (output.Type) { + case cmUVProcessChainBuilder::None: + outputData.Stdio.flags = UV_IGNORE; + break; + + case cmUVProcessChainBuilder::Builtin: + outputData.BuiltinStream.init(*this->Loop, 0); + outputData.Stdio.flags = + static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); + outputData.Stdio.data.stream = outputData.BuiltinStream; + break; + + case cmUVProcessChainBuilder::External: + outputData.Stdio.flags = UV_INHERIT_FD; + outputData.Stdio.data.fd = output.FileDescriptor; + break; + } + + auto const& error = + this->Builder->Stdio[cmUVProcessChainBuilder::Stream_ERROR]; + auto& errorData = this->ErrorStreamData; + switch (error.Type) { + case cmUVProcessChainBuilder::None: + errorData.Stdio.flags = UV_IGNORE; + break; + + case cmUVProcessChainBuilder::Builtin: { + int pipeFd[2]; + if (cmGetPipes(pipeFd) < 0) { + return false; + } + + errorData.BuiltinStream.init(*this->Loop, 0); + if (uv_pipe_open(errorData.BuiltinStream, pipeFd[0]) < 0) { + return false; + } + errorData.Stdio.flags = UV_INHERIT_FD; + errorData.Stdio.data.fd = pipeFd[1]; + break; + } + + case cmUVProcessChainBuilder::External: + errorData.Stdio.flags = UV_INHERIT_FD; + errorData.Stdio.data.fd = error.FileDescriptor; + break; + } + + return true; +} + +bool cmUVProcessChain::InternalData::AddCommand( + const cmUVProcessChainBuilder::ProcessConfiguration& config, bool first, + bool last) +{ + this->Processes.emplace_back(cm::make_unique<ProcessData>()); + auto& process = *this->Processes.back(); + process.Data = this; + + auto options = uv_process_options_t(); + + // Bounds were checked at add time, first element is guaranteed to exist + options.file = config.Arguments[0].c_str(); + + std::vector<const char*> arguments; + for (auto const& arg : config.Arguments) { + arguments.push_back(arg.c_str()); + } + arguments.push_back(nullptr); + options.args = const_cast<char**>(arguments.data()); + options.flags = UV_PROCESS_WINDOWS_HIDE; + + std::array<uv_stdio_container_t, 3> stdio; + stdio[0] = uv_stdio_container_t(); + if (first) { + stdio[0].flags = UV_IGNORE; + } else { + auto& prev = **std::prev(this->Processes.end(), 2); + stdio[0].flags = UV_INHERIT_STREAM; + stdio[0].data.stream = prev.OutputPipe; + } + if (last) { + stdio[1] = this->OutputStreamData.Stdio; + } else { + if (process.OutputPipe.init(*this->Loop, 0) < 0) { + return false; + } + stdio[1] = uv_stdio_container_t(); + stdio[1].flags = + static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); + stdio[1].data.stream = process.OutputPipe; + } + stdio[2] = this->ErrorStreamData.Stdio; + + options.stdio = stdio.data(); + options.stdio_count = 3; + options.exit_cb = [](uv_process_t* handle, int64_t exitStatus, + int termSignal) { + auto* processData = static_cast<ProcessData*>(handle->data); + processData->Finished = true; + processData->ProcessStatus.ExitStatus = exitStatus; + processData->ProcessStatus.TermSignal = termSignal; + processData->Data->ProcessesCompleted++; + }; + + return process.Process.spawn(*this->Loop, options, &process) >= 0; +} + +bool cmUVProcessChain::InternalData::Finish() +{ + if (this->Builder->Stdio[cmUVProcessChainBuilder::Stream_OUTPUT].Type == + cmUVProcessChainBuilder::Builtin) { + this->OutputStreamData.Streambuf.open( + this->OutputStreamData.BuiltinStream); + } + + if (this->Builder->Stdio[cmUVProcessChainBuilder::Stream_ERROR].Type == + cmUVProcessChainBuilder::Builtin) { + cm::uv_pipe_ptr tmpPipe; + if (tmpPipe.init(*this->Loop, 0) < 0) { + return false; + } + if (uv_pipe_open(tmpPipe, this->ErrorStreamData.Stdio.data.fd) < 0) { + return false; + } + tmpPipe.reset(); + + this->ErrorStreamData.Streambuf.open(this->ErrorStreamData.BuiltinStream); + } + + this->Valid = true; + return true; +} + +cmUVProcessChain::cmUVProcessChain() + : Data(cm::make_unique<InternalData>()) +{ + this->Data->Loop.init(); +} + +cmUVProcessChain::cmUVProcessChain(cmUVProcessChain&& other) noexcept + : Data(std::move(other.Data)) +{ +} + +cmUVProcessChain::~cmUVProcessChain() = default; + +cmUVProcessChain& cmUVProcessChain::operator=( + cmUVProcessChain&& other) noexcept +{ + this->Data = std::move(other.Data); + return *this; +} + +uv_loop_t& cmUVProcessChain::GetLoop() +{ + return *this->Data->Loop; +} + +std::istream* cmUVProcessChain::OutputStream() +{ + return this->Data->OutputStreamData.GetBuiltinStream(); +} + +std::istream* cmUVProcessChain::ErrorStream() +{ + return this->Data->ErrorStreamData.GetBuiltinStream(); +} + +bool cmUVProcessChain::Valid() const +{ + return this->Data->Valid; +} + +bool cmUVProcessChain::Wait(int64_t milliseconds) +{ + bool timeout = false; + cm::uv_timer_ptr timer; + + if (milliseconds >= 0) { + timer.init(*this->Data->Loop, &timeout); + timer.start( + [](uv_timer_t* handle) { + auto* timeoutPtr = static_cast<bool*>(handle->data); + *timeoutPtr = true; + }, + milliseconds, 0); + } + + while (!timeout && + this->Data->ProcessesCompleted < this->Data->Processes.size()) { + uv_run(this->Data->Loop, UV_RUN_ONCE); + } + + return !timeout; +} + +std::vector<const cmUVProcessChain::Status*> cmUVProcessChain::GetStatus() + const +{ + std::vector<const cmUVProcessChain::Status*> statuses( + this->Data->Processes.size(), nullptr); + for (std::size_t i = 0; i < statuses.size(); i++) { + statuses[i] = this->GetStatus(i); + } + return statuses; +} + +const cmUVProcessChain::Status* cmUVProcessChain::GetStatus( + std::size_t index) const +{ + auto const& process = *this->Data->Processes[index]; + if (process.Finished) { + return &process.ProcessStatus; + } + return nullptr; +} diff --git a/Source/cmUVProcessChain.h b/Source/cmUVProcessChain.h new file mode 100644 index 0000000..2b33520 --- /dev/null +++ b/Source/cmUVProcessChain.h @@ -0,0 +1,100 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmUVProcessChain_h +#define cmUVProcessChain_h + +#include "cm_uv.h" + +#include <array> +#include <iosfwd> +#include <memory> // IWYU pragma: keep +#include <string> +#include <vector> + +#include <stdint.h> + +class cmUVProcessChain; + +class cmUVProcessChainBuilder +{ +public: + enum Stream + { + Stream_INPUT = 0, + Stream_OUTPUT = 1, + Stream_ERROR = 2, + }; + + cmUVProcessChainBuilder(); + + cmUVProcessChainBuilder& AddCommand( + const std::vector<std::string>& arguments); + cmUVProcessChainBuilder& SetNoStream(Stream stdio); + cmUVProcessChainBuilder& SetBuiltinStream(Stream stdio); + cmUVProcessChainBuilder& SetExternalStream(Stream stdio, int fd); + + cmUVProcessChain Start() const; + +private: + enum StdioType + { + None, + Builtin, + External, + }; + + friend class cmUVProcessChain; + + struct StdioConfiguration + { + StdioType Type; + int FileDescriptor; + }; + + struct ProcessConfiguration + { + std::vector<std::string> Arguments; + }; + + std::array<StdioConfiguration, 3> Stdio; + std::vector<ProcessConfiguration> Processes; +}; + +class cmUVProcessChain +{ +public: + struct Status + { + int64_t ExitStatus; + int TermSignal; + }; + + cmUVProcessChain(const cmUVProcessChain& other) = delete; + cmUVProcessChain(cmUVProcessChain&& other) noexcept; + + ~cmUVProcessChain(); + + cmUVProcessChain& operator=(const cmUVProcessChain& other) = delete; + cmUVProcessChain& operator=(cmUVProcessChain&& other) noexcept; + + uv_loop_t& GetLoop(); + + // FIXME: Add stdin support + std::istream* OutputStream(); + std::istream* ErrorStream(); + + bool Valid() const; + bool Wait(int64_t milliseconds = -1); + std::vector<const Status*> GetStatus() const; + const Status* GetStatus(std::size_t index) const; + +private: + friend class cmUVProcessChainBuilder; + + cmUVProcessChain(); + + struct InternalData; + std::unique_ptr<InternalData> Data; +}; + +#endif diff --git a/Source/cmUVStreambuf.h b/Source/cmUVStreambuf.h index 29e4fde..873352b 100644 --- a/Source/cmUVStreambuf.h +++ b/Source/cmUVStreambuf.h @@ -68,10 +68,10 @@ protected: private: uv_stream_t* Stream = nullptr; - void* OldStreamData; - const std::size_t PutBack; + void* OldStreamData = nullptr; + const std::size_t PutBack = 0; std::vector<CharT> InputBuffer; - bool EndOfFile; + bool EndOfFile = false; void StreamReadStartStop(); @@ -208,7 +208,7 @@ void cmBasicUVStreambuf<CharT, Traits>::StreamRead(ssize_t nread) this->setg(this->eback(), this->gptr(), this->egptr() + nread / sizeof(CharT)); uv_read_stop(this->Stream); - } else if (nread < 0 || nread == UV_EOF) { + } else if (nread < 0 /*|| nread == UV_EOF*/) { this->EndOfFile = true; uv_read_stop(this->Stream); } diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index e04bba2..a25f25a 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt @@ -15,11 +15,15 @@ set(CMakeLib_TESTS testXMLParser.cxx testXMLSafe.cxx testFindPackageCommand.cxx + testUVProcessChain.cxx testUVRAII.cxx testUVStreambuf.cxx ) +add_executable(testUVProcessChainHelper testUVProcessChainHelper.cxx) + set(testRST_ARGS ${CMAKE_CURRENT_SOURCE_DIR}) +set(testUVProcessChain_ARGS $<TARGET_FILE:testUVProcessChainHelper>) set(testUVStreambuf_ARGS $<TARGET_FILE:cmake>) if(WIN32) diff --git a/Tests/CMakeLib/testUVProcessChain.cxx b/Tests/CMakeLib/testUVProcessChain.cxx new file mode 100644 index 0000000..72ae602 --- /dev/null +++ b/Tests/CMakeLib/testUVProcessChain.cxx @@ -0,0 +1,335 @@ +#include "cmUVProcessChain.h" + +#include "cmAlgorithms.h" +#include "cmGetPipes.h" +#include "cmUVHandlePtr.h" +#include "cmUVStreambuf.h" + +#include "cm_uv.h" + +#include <algorithm> +#include <functional> +#include <iostream> +#include <sstream> +#include <string> +#include <vector> + +#include <csignal> + +struct ExpectedStatus +{ + bool Finished; + bool MatchExitStatus; + bool MatchTermSignal; + cmUVProcessChain::Status Status; +}; + +static const std::vector<ExpectedStatus> status1 = { + { false, false, false, { 0, 0 } }, + { false, false, false, { 0, 0 } }, + { false, false, false, { 0, 0 } }, +}; + +static const std::vector<ExpectedStatus> status2 = { + { true, true, true, { 0, 0 } }, + { false, false, false, { 0, 0 } }, + { false, false, false, { 0, 0 } }, +}; + +static const std::vector<ExpectedStatus> status3 = { + { true, true, true, { 0, 0 } }, + { true, true, true, { 1, 0 } }, +#ifdef _WIN32 + { true, true, true, { 2, 0 } }, +#else + { true, false, true, { 0, SIGABRT } }, +#endif +}; + +bool operator==(const cmUVProcessChain::Status* actual, + const ExpectedStatus& expected) +{ + if (!expected.Finished) { + return !actual; + } else if (!actual) { + return false; + } + if (expected.MatchExitStatus && + expected.Status.ExitStatus != actual->ExitStatus) { + return false; + } + if (expected.MatchTermSignal && + expected.Status.TermSignal != actual->TermSignal) { + return false; + } + return true; +} + +bool resultsMatch(const std::vector<const cmUVProcessChain::Status*>& actual, + const std::vector<ExpectedStatus>& expected) +{ + return actual.size() == expected.size() && + std::equal(actual.begin(), actual.end(), expected.begin()); +} + +std::string getInput(std::istream& input) +{ + char buffer[1024]; + std::ostringstream str; + do { + input.read(buffer, 1024); + str.write(buffer, input.gcount()); + } while (input.gcount() > 0); + return str.str(); +} + +template <typename T> +std::function<std::ostream&(std::ostream&)> printExpected(bool match, + const T& value) +{ + return [match, value](std::ostream& stream) -> std::ostream& { + if (match) { + stream << value; + } else { + stream << "*"; + } + return stream; + }; +} + +std::ostream& operator<<( + std::ostream& stream, + const std::function<std::ostream&(std::ostream&)>& func) +{ + return func(stream); +} + +void printResults(const std::vector<const cmUVProcessChain::Status*>& actual, + const std::vector<ExpectedStatus>& expected) +{ + std::cout << "Expected: " << std::endl; + for (auto const& e : expected) { + if (e.Finished) { + std::cout << " ExitStatus: " + << printExpected(e.MatchExitStatus, e.Status.ExitStatus) + << ", TermSignal: " + << printExpected(e.MatchTermSignal, e.Status.TermSignal) + << std::endl; + } else { + std::cout << " null" << std::endl; + } + } + std::cout << "Actual:" << std::endl; + for (auto const& a : actual) { + if (a) { + std::cout << " ExitStatus: " << a->ExitStatus + << ", TermSignal: " << a->TermSignal << std::endl; + } else { + std::cout << " null" << std::endl; + } + } +} + +bool checkExecution(cmUVProcessChainBuilder& builder, + std::unique_ptr<cmUVProcessChain>& chain) +{ + std::vector<const cmUVProcessChain::Status*> status; + + chain = cm::make_unique<cmUVProcessChain>(builder.Start()); + if (!chain->Valid()) { + std::cout << "Valid() returned false, should be true" << std::endl; + return false; + } + status = chain->GetStatus(); + if (!resultsMatch(status, status1)) { + std::cout << "GetStatus() did not produce expected output" << std::endl; + printResults(status, status1); + return false; + } + + if (chain->Wait(6000)) { + std::cout << "Wait() returned true, should be false" << std::endl; + return false; + } + status = chain->GetStatus(); + if (!resultsMatch(status, status2)) { + std::cout << "GetStatus() did not produce expected output" << std::endl; + printResults(status, status2); + return false; + } + + if (!chain->Wait()) { + std::cout << "Wait() returned false, should be true" << std::endl; + return false; + } + status = chain->GetStatus(); + if (!resultsMatch(status, status3)) { + std::cout << "GetStatus() did not produce expected output" << std::endl; + printResults(status, status3); + return false; + } + + return true; +} + +bool checkOutput(std::istream& outputStream, std::istream& errorStream) +{ + std::string output = getInput(outputStream); + if (output != "HELO WRD!") { + std::cout << "Output was \"" << output << "\", expected \"HELO WRD!\"" + << std::endl; + return false; + } + + std::string error = getInput(errorStream); + if (error.length() != 3 || error.find('1') == std::string::npos || + error.find('2') == std::string::npos || + error.find('3') == std::string::npos) { + std::cout << "Error was \"" << error << "\", expected \"123\"" + << std::endl; + return false; + } + + return true; +} + +bool testUVProcessChainBuiltin(const char* helperCommand) +{ + cmUVProcessChainBuilder builder; + std::unique_ptr<cmUVProcessChain> chain; + builder.AddCommand({ helperCommand, "echo" }) + .AddCommand({ helperCommand, "capitalize" }) + .AddCommand({ helperCommand, "dedup" }) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT) + .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR); + + if (!checkExecution(builder, chain)) { + return false; + } + + if (!chain->OutputStream()) { + std::cout << "OutputStream() was null, expecting not null" << std::endl; + return false; + } + if (!chain->ErrorStream()) { + std::cout << "ErrorStream() was null, expecting not null" << std::endl; + return false; + } + + if (!checkOutput(*chain->OutputStream(), *chain->ErrorStream())) { + return false; + } + + return true; +} + +bool testUVProcessChainExternal(const char* helperCommand) +{ + cmUVProcessChainBuilder builder; + std::unique_ptr<cmUVProcessChain> chain; + int outputPipe[2], errorPipe[2]; + cm::uv_pipe_ptr outputInPipe, outputOutPipe, errorInPipe, errorOutPipe; + + if (cmGetPipes(outputPipe) < 0) { + std::cout << "Error creating pipes" << std::endl; + return false; + } + if (cmGetPipes(errorPipe) < 0) { + std::cout << "Error creating pipes" << std::endl; + return false; + } + + builder.AddCommand({ helperCommand, "echo" }) + .AddCommand({ helperCommand, "capitalize" }) + .AddCommand({ helperCommand, "dedup" }) + .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, outputPipe[1]) + .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, errorPipe[1]); + + if (!checkExecution(builder, chain)) { + return false; + } + + if (chain->OutputStream()) { + std::cout << "OutputStream() was not null, expecting null" << std::endl; + return false; + } + if (chain->ErrorStream()) { + std::cout << "ErrorStream() was not null, expecting null" << std::endl; + return false; + } + + outputOutPipe.init(chain->GetLoop(), 0); + uv_pipe_open(outputOutPipe, outputPipe[1]); + outputOutPipe.reset(); + + errorOutPipe.init(chain->GetLoop(), 0); + uv_pipe_open(errorOutPipe, errorPipe[1]); + errorOutPipe.reset(); + + outputInPipe.init(chain->GetLoop(), 0); + uv_pipe_open(outputInPipe, outputPipe[0]); + cmUVStreambuf outputBuf; + outputBuf.open(outputInPipe); + std::istream outputStream(&outputBuf); + + errorInPipe.init(chain->GetLoop(), 0); + uv_pipe_open(errorInPipe, errorPipe[0]); + cmUVStreambuf errorBuf; + errorBuf.open(errorInPipe); + std::istream errorStream(&errorBuf); + + if (!checkOutput(outputStream, errorStream)) { + return false; + } + + return true; +} + +bool testUVProcessChainNone(const char* helperCommand) +{ + cmUVProcessChainBuilder builder; + std::unique_ptr<cmUVProcessChain> chain; + builder.AddCommand({ helperCommand, "echo" }) + .AddCommand({ helperCommand, "capitalize" }) + .AddCommand({ helperCommand, "dedup" }); + + if (!checkExecution(builder, chain)) { + return false; + } + + if (chain->OutputStream()) { + std::cout << "OutputStream() was not null, expecting null" << std::endl; + return false; + } + if (chain->ErrorStream()) { + std::cout << "ErrorStream() was not null, expecting null" << std::endl; + return false; + } + + return true; +} + +int testUVProcessChain(int argc, char** const argv) +{ + if (argc < 2) { + std::cout << "Invalid arguments.\n"; + return -1; + } + + if (!testUVProcessChainBuiltin(argv[1])) { + std::cout << "While executing testUVProcessChainBuiltin().\n"; + return -1; + } + + if (!testUVProcessChainExternal(argv[1])) { + std::cout << "While executing testUVProcessChainExternal().\n"; + return -1; + } + + if (!testUVProcessChainNone(argv[1])) { + std::cout << "While executing testUVProcessChainNone().\n"; + return -1; + } + + return 0; +} diff --git a/Tests/CMakeLib/testUVProcessChainHelper.cxx b/Tests/CMakeLib/testUVProcessChainHelper.cxx new file mode 100644 index 0000000..263665d --- /dev/null +++ b/Tests/CMakeLib/testUVProcessChainHelper.cxx @@ -0,0 +1,72 @@ +#include <chrono> +#include <iostream> +#include <set> +#include <sstream> +#include <string> +#include <thread> + +#include <cctype> +#include <cstdlib> + +std::string getStdin() +{ + char buffer[1024]; + std::ostringstream str; + do { + std::cin.read(buffer, 1024); + str.write(buffer, std::cin.gcount()); + } while (std::cin.gcount() > 0); + return str.str(); +} + +int main(int argc, char** argv) +{ + if (argc < 2) { + return -1; + } + + std::string command = argv[1]; + if (command == "echo") { + std::this_thread::sleep_for(std::chrono::milliseconds(3000)); + std::cout << "HELLO world!" << std::flush; + std::cerr << "1" << std::flush; + return 0; + } + if (command == "capitalize") { + std::this_thread::sleep_for(std::chrono::milliseconds(9000)); + std::string input = getStdin(); + for (auto& c : input) { + c = static_cast<char>(std::toupper(c)); + } + std::cout << input << std::flush; + std::cerr << "2" << std::flush; + return 1; + } + if (command == "dedup") { + // Use a nested scope to free all resources before aborting below. + { + std::string input = getStdin(); + std::set<char> seen; + std::string output; + for (auto c : input) { + if (!seen.count(c)) { + seen.insert(c); + output += c; + } + } + std::cout << output << std::flush; + std::cerr << "3" << std::flush; + } + + // On Windows, the exit code of abort() is different between debug and + // release builds, and does not yield a term_signal in libuv in either + // case. For the sake of simplicity, we just return another non-zero code. +#ifdef _WIN32 + return 2; +#else + std::abort(); +#endif + } + + return -1; +} diff --git a/Tests/FindBoost/CMakeLists.txt b/Tests/FindBoost/CMakeLists.txt index 58d795b..8489d85 100644 --- a/Tests/FindBoost/CMakeLists.txt +++ b/Tests/FindBoost/CMakeLists.txt @@ -21,7 +21,8 @@ add_test(NAME FindBoost.TestFail COMMAND ) set_tests_properties(FindBoost.TestFail PROPERTIES - PASS_REGULAR_EXPRESSION "Could not find the following Boost libraries:[ \t\n]+boost_foobar") + WILL_FAIL ON + PASS_REGULAR_EXPRESSION "Could NOT find Boost (missing: foobar)") add_test(NAME FindBoost.TestHeaders COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION> diff --git a/Tests/FindPython/CMakeLists.txt b/Tests/FindPython/CMakeLists.txt index d6f50e7..8dfcf40 100644 --- a/Tests/FindPython/CMakeLists.txt +++ b/Tests/FindPython/CMakeLists.txt @@ -79,6 +79,28 @@ if(CMake_TEST_FindPython) --build-options ${build_options} --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION> ) + + add_test(NAME FindPython.Python2Embedded COMMAND + ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION> + --build-and-test + "${CMake_SOURCE_DIR}/Tests/FindPython/Python2Embedded" + "${CMake_BINARY_DIR}/Tests/FindPython/Python2Embedded" + ${build_generator_args} + --build-project TestPython2Embedded + --build-options ${build_options} + --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION> + ) + add_test(NAME FindPython.Python3Embedded COMMAND + ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION> + --build-and-test + "${CMake_SOURCE_DIR}/Tests/FindPython/Python3Embedded" + "${CMake_BINARY_DIR}/Tests/FindPython/Python3Embedded" + ${build_generator_args} + --build-project TestPython3Embedded + --build-options ${build_options} + --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION> + ) + endif() if(CMake_TEST_FindPython_NumPy) diff --git a/Tests/FindPython/Python/CMakeLists.txt b/Tests/FindPython/Python/CMakeLists.txt index f7fc243..62c805e 100644 --- a/Tests/FindPython/Python/CMakeLists.txt +++ b/Tests/FindPython/Python/CMakeLists.txt @@ -16,6 +16,9 @@ endif() if(NOT TARGET Python::Python) message(SEND_ERROR "Python::Python not found") endif() +if(NOT TARGET Python::Module) + message(SEND_ERROR "Python::Module not found") +endif() Python_add_library (spam3 MODULE ../spam.c) target_compile_definitions (spam3 PRIVATE PYTHON3) diff --git a/Tests/FindPython/Python2/CMakeLists.txt b/Tests/FindPython/Python2/CMakeLists.txt index a0753f6..274745a 100644 --- a/Tests/FindPython/Python2/CMakeLists.txt +++ b/Tests/FindPython/Python2/CMakeLists.txt @@ -21,6 +21,9 @@ endif() if(NOT TARGET Python2::Python) message(SEND_ERROR "Python2::Python not found") endif() +if(NOT TARGET Python2::Module) + message(SEND_ERROR "Python2::Module not found") +endif() Python2_add_library (spam2 MODULE ../spam.c) target_compile_definitions (spam2 PRIVATE PYTHON2) diff --git a/Tests/FindPython/Python2Embedded/CMakeLists.txt b/Tests/FindPython/Python2Embedded/CMakeLists.txt new file mode 100644 index 0000000..0115dea --- /dev/null +++ b/Tests/FindPython/Python2Embedded/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.1) + +project(TestPython2Embedded C) + +include(CTest) + +find_package(Python2 REQUIRED COMPONENTS Development) +if (NOT Python2_FOUND) + message (FATAL_ERROR "Fail to found Python 2") +endif() + +if(NOT TARGET Python2::Python) + message(SEND_ERROR "Python2::Python not found") +endif() + +Python2_add_library (display_time2 SHARED ../display_time.c) +set_property (TARGET display_time2 PROPERTY WINDOWS_EXPORT_ALL_SYMBOLS ON) +target_compile_definitions (display_time2 PRIVATE PYTHON2) + +add_executable (main2 ../main.c) +target_link_libraries (main2 PRIVATE display_time2) + +if (WIN32 OR CYGWIN OR MSYS OR MINGW) + list (JOIN Python2_RUNTIME_LIBRARY_DIRS "$<SEMICOLON>" RUNTIME_DIRS) + add_test (NAME Python2.Embedded COMMAND "${CMAKE_COMMAND}" -E env "PATH=${RUNTIME_DIRS}" $<TARGET_FILE:main2>) +else() + add_test (NAME Python2.Embedded COMMAND main2) +endif() +set_property (TEST Python2.Embedded PROPERTY PASS_REGULAR_EXPRESSION "Today is") diff --git a/Tests/FindPython/Python3/CMakeLists.txt b/Tests/FindPython/Python3/CMakeLists.txt index 65eea4c..b21a15b 100644 --- a/Tests/FindPython/Python3/CMakeLists.txt +++ b/Tests/FindPython/Python3/CMakeLists.txt @@ -21,6 +21,9 @@ endif() if(NOT TARGET Python3::Python) message(SEND_ERROR "Python2::Python not found") endif() +if(NOT TARGET Python3::Module) + message(SEND_ERROR "Python2::Module not found") +endif() Python3_add_library (spam3 MODULE ../spam.c) target_compile_definitions (spam3 PRIVATE PYTHON3) diff --git a/Tests/FindPython/Python3Embedded/CMakeLists.txt b/Tests/FindPython/Python3Embedded/CMakeLists.txt new file mode 100644 index 0000000..4eb7ebc --- /dev/null +++ b/Tests/FindPython/Python3Embedded/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.1) + +project(TestPython3Embedded C) + +include(CTest) + +find_package(Python3 REQUIRED COMPONENTS Development) +if (NOT Python3_FOUND) + message (FATAL_ERROR "Fail to found Python 3") +endif() + +if(NOT TARGET Python3::Python) + message(SEND_ERROR "Python3::Python not found") +endif() + +Python3_add_library (display_time3 SHARED ../display_time.c) +set_property (TARGET display_time3 PROPERTY WINDOWS_EXPORT_ALL_SYMBOLS ON) +target_compile_definitions (display_time3 PRIVATE PYTHON3) + +add_executable (main3 ../main.c) +target_link_libraries (main3 PRIVATE display_time3) + +if (WIN32 OR CYGWIN OR MSYS OR MINGW) + list (JOIN Python3_RUNTIME_LIBRARY_DIRS "$<SEMICOLON>" RUNTIME_DIRS) + add_test (NAME Python3.Embedded COMMAND "${CMAKE_COMMAND}" -E env "PATH=${RUNTIME_DIRS}" $<TARGET_FILE:main3>) +else() + add_test (NAME Python3.Embedded COMMAND main3) +endif() +set_property (TEST Python3.Embedded PROPERTY PASS_REGULAR_EXPRESSION "Today is") diff --git a/Tests/FindPython/display_time.c b/Tests/FindPython/display_time.c new file mode 100644 index 0000000..0e78434 --- /dev/null +++ b/Tests/FindPython/display_time.c @@ -0,0 +1,36 @@ + +#include <stdio.h> + +#define PY_SSIZE_T_CLEAN +#include <Python.h> + +#include "display_time.h" + +void display_time() +{ +#if defined(PYTHON3) + wchar_t* program = Py_DecodeLocale("display_time", NULL); + if (program == NULL) { + fprintf(stderr, "Fatal error: cannot decode argv[0]\n"); + exit(1); + } + char* cmd = "from time import time,ctime\n" + "print('Today is', ctime(time()))\n"; +#else + char* program = "display_time"; + char* cmd = "from time import time,ctime\n" + "print 'Today is', ctime(time())\n"; +#endif + + Py_SetProgramName(program); /* optional but recommended */ + Py_Initialize(); + PyRun_SimpleString(cmd); +#if defined(PYTHON3) + if (Py_FinalizeEx() < 0) { + exit(120); + } + PyMem_RawFree(program); +#else + Py_Finalize(); +#endif +} diff --git a/Tests/FindPython/display_time.h b/Tests/FindPython/display_time.h new file mode 100644 index 0000000..d825e02 --- /dev/null +++ b/Tests/FindPython/display_time.h @@ -0,0 +1,2 @@ + +void display_time(); diff --git a/Tests/FindPython/main.c b/Tests/FindPython/main.c new file mode 100644 index 0000000..0acba29 --- /dev/null +++ b/Tests/FindPython/main.c @@ -0,0 +1,7 @@ + +#include "display_time.h" + +int main() +{ + display_time(); +} diff --git a/Tests/RunCMake/FindBoost/CMakePackage-stdout.txt b/Tests/RunCMake/FindBoost/CMakePackage-stdout.txt index 664e4a5..0a67488 100644 --- a/Tests/RunCMake/FindBoost/CMakePackage-stdout.txt +++ b/Tests/RunCMake/FindBoost/CMakePackage-stdout.txt @@ -1,3 +1,2 @@ --- Boost 1\.12345 found\. --- Found Boost components: - date_time +-- Found Boost: [^ +]* \(found suitable version "1\.12345", minimum required is "1\.12345"\) found components: date_time diff --git a/Tests/RunCMake/FindBoost/CMakePackage/BoostConfig.cmake b/Tests/RunCMake/FindBoost/CMakePackage/BoostConfig.cmake index e69de29..d13bbf3 100644 --- a/Tests/RunCMake/FindBoost/CMakePackage/BoostConfig.cmake +++ b/Tests/RunCMake/FindBoost/CMakePackage/BoostConfig.cmake @@ -0,0 +1 @@ +set(Boost_DATE_TIME_FOUND 1) diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/Boost-1.70.0/BoostConfig.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/Boost-1.70.0/BoostConfig.cmake new file mode 100644 index 0000000..539267d --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/Boost-1.70.0/BoostConfig.cmake @@ -0,0 +1,140 @@ +# Copyright 2019 Peter Dimov +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt) + +# This CMake configuration file, installed as part of the Boost build +# and installation procedure done by `b2 install`, provides support +# for find_package(Boost). +# +# It's roughly, but not perfectly, compatible with the behavior +# of find_package(Boost) as provided by FindBoost.cmake. +# +# A typical use might be +# +# find_package(Boost 1.70 REQUIRED COMPONENTS filesystem regex PATHS C:/Boost) +# +# On success, the above invocation would define the targets Boost::headers, +# Boost::filesystem and Boost::regex. Boost::headers represents all +# header-only libraries. An alias, Boost::boost, for Boost::headers is +# provided for compatibility. +# +# Since Boost libraries can coexist in many variants - 32/64 bit, +# static/dynamic runtime, debug/release, the following variables can be used +# to control which variant is chosen: +# +# Boost_USE_DEBUG_LIBS: When OFF, disables debug libraries. +# Boost_USE_RELEASE_LIBS: When OFF, disables release libraries. +# Boost_USE_STATIC_LIBS: When ON, uses static Boost libraries; when OFF, +# uses shared Boost libraries; when not set, the +# default is to use shared when BUILD_SHARED_LIBS is +# ON, static otherwise. +# Boost_USE_STATIC_RUNTIME: When ON, uses Boost libraries linked against the +# static runtime. The default is shared runtime. +# Boost_USE_DEBUG_RUNTIME: When ON, uses Boost libraries linked against the +# debug runtime. When OFF, against the release +# runtime. The default is to use either. +# Boost_COMPILER: The compiler that has been used to build Boost, +# such as vc141, gcc7, clang37. The default is +# determined from CMAKE_CXX_COMPILER_ID. + +message(STATUS "Found Boost ${Boost_VERSION} at ${Boost_DIR}") + +# Output requested configuration (f.ex. "REQUIRED COMPONENTS filesystem") + +if(Boost_FIND_QUIETLY) + set(_BOOST_CONFIG "${_BOOST_CONFIG} QUIET") +endif() + +if(Boost_FIND_REQUIRED) + set(_BOOST_CONFIG "${_BOOST_CONFIG} REQUIRED") +endif() + +foreach(__boost_comp IN LISTS Boost_FIND_COMPONENTS) + if(${Boost_FIND_REQUIRED_${__boost_comp}}) + list(APPEND _BOOST_COMPONENTS ${__boost_comp}) + else() + list(APPEND _BOOST_OPTIONAL_COMPONENTS ${__boost_comp}) + endif() +endforeach() + +if(_BOOST_COMPONENTS) + set(_BOOST_CONFIG "${_BOOST_CONFIG} COMPONENTS ${_BOOST_COMPONENTS}") +endif() + +if(_BOOST_OPTIONAL_COMPONENTS) + set(_BOOST_CONFIG "${_BOOST_CONFIG} OPTIONAL_COMPONENTS ${_BOOST_OPTIONAL_COMPONENTS}") +endif() + +if(_BOOST_CONFIG) + message(STATUS " Requested configuration:${_BOOST_CONFIG}") +endif() + +unset(_BOOST_CONFIG) +unset(_BOOST_COMPONENTS) +unset(_BOOST_OPTIONAL_COMPONENTS) + +# find_dependency doesn't forward arguments until 3.9, so we have to roll our own + +macro(boost_find_dependency dep req) + + set(_BOOST_QUIET) + if(Boost_FIND_QUIETLY) + set(_BOOST_QUIET QUIET) + endif() + + set(_BOOST_REQUIRED) + if(${req} AND Boost_FIND_REQUIRED) + set(_BOOST_REQUIRED REQUIRED) + endif() + + get_filename_component(_BOOST_CMAKEDIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE) + + if(Boost_DEBUG) + message(STATUS "BoostConfig: find_package(boost_${dep} ${Boost_VERSION} EXACT CONFIG ${_BOOST_REQUIRED} ${_BOOST_QUIET} HINTS ${_BOOST_CMAKEDIR})") + endif() + find_package(boost_${dep} ${Boost_VERSION} EXACT CONFIG ${_BOOST_REQUIRED} ${_BOOST_QUIET} HINTS ${_BOOST_CMAKEDIR}) + + string(TOUPPER ${dep} _BOOST_DEP) + set(Boost_${_BOOST_DEP}_FOUND ${boost_${dep}_FOUND}) + + unset(_BOOST_REQUIRED) + unset(_BOOST_QUIET) + unset(_BOOST_CMAKEDIR) + unset(_BOOST_DEP) + +endmacro() + +# Find boost_headers + +boost_find_dependency(headers 1) + +if(NOT boost_headers_FOUND) + + set(Boost_FOUND 0) + set(Boost_NOT_FOUND_MESSAGE "A required dependency, boost_headers, has not been found.") + + return() + +endif() + +# Find components + +foreach(__boost_comp IN LISTS Boost_FIND_COMPONENTS) + + boost_find_dependency(${__boost_comp} ${Boost_FIND_REQUIRED_${__boost_comp}}) + +endforeach() + +# Compatibility targets + +if(NOT TARGET Boost::boost) + + add_library(Boost::boost INTERFACE IMPORTED) + set_property(TARGET Boost::boost APPEND PROPERTY INTERFACE_LINK_LIBRARIES Boost::headers) + + # All Boost:: targets already disable autolink + add_library(Boost::diagnostic_definitions INTERFACE IMPORTED) + add_library(Boost::disable_autolinking INTERFACE IMPORTED) + add_library(Boost::dynamic_linking INTERFACE IMPORTED) + +endif() diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/Boost-1.70.0/BoostConfigVersion.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/Boost-1.70.0/BoostConfigVersion.cmake new file mode 100644 index 0000000..114bba0 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/Boost-1.70.0/BoostConfigVersion.cmake @@ -0,0 +1,12 @@ +# Generated by Boost 1.70.0 + +set(PACKAGE_VERSION 1.70.0) + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/BoostDetectToolset-1.70.0.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/BoostDetectToolset-1.70.0.cmake new file mode 100644 index 0000000..2f1ac01 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/BoostDetectToolset-1.70.0.cmake @@ -0,0 +1 @@ +set(BOOST_DETECTED_TOOLSET "gcc7") diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_chrono-1.70.0/boost_chrono-config-version.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_chrono-1.70.0/boost_chrono-config-version.cmake new file mode 100644 index 0000000..114bba0 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_chrono-1.70.0/boost_chrono-config-version.cmake @@ -0,0 +1,12 @@ +# Generated by Boost 1.70.0 + +set(PACKAGE_VERSION 1.70.0) + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_chrono-1.70.0/boost_chrono-config.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_chrono-1.70.0/boost_chrono-config.cmake new file mode 100644 index 0000000..5d798b5 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_chrono-1.70.0/boost_chrono-config.cmake @@ -0,0 +1,98 @@ +# Generated by Boost 1.70.0 + +if(TARGET Boost::chrono) + return() +endif() + +message(STATUS "Found boost_chrono ${boost_chrono_VERSION} at ${boost_chrono_DIR}") + +# Compute the include and library directories relative to this file. +get_filename_component(_BOOST_CMAKEDIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE) +get_filename_component(_BOOST_INCLUDEDIR "${_BOOST_CMAKEDIR}/../../include/" ABSOLUTE) +get_filename_component(_BOOST_LIBDIR "${_BOOST_CMAKEDIR}/../" ABSOLUTE) + +# Create imported target Boost::chrono +add_library(Boost::chrono UNKNOWN IMPORTED) + +set_target_properties(Boost::chrono PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_BOOST_INCLUDEDIR}" + INTERFACE_COMPILE_DEFINITIONS "BOOST_ALL_NO_LIB" +) + +include(${CMAKE_CURRENT_LIST_DIR}/../BoostDetectToolset-1.70.0.cmake) + +if(Boost_DEBUG) + message(STATUS "Scanning ${CMAKE_CURRENT_LIST_DIR}/libboost_chrono-variant*.cmake") +endif() + +file(GLOB __boost_variants "${CMAKE_CURRENT_LIST_DIR}/libboost_chrono-variant*.cmake") + +macro(_BOOST_SKIPPED fname reason) + if(Boost_DEBUG) + message(STATUS " ... skipped ${fname} (${reason})") + endif() + list(APPEND __boost_skipped "${fname} (${reason})") +endmacro() + +foreach(f IN LISTS __boost_variants) + if(Boost_DEBUG) + message(STATUS " Including ${f}") + endif() + include(${f}) +endforeach() + +unset(_BOOST_LIBDIR) +unset(_BOOST_INCLUDEDIR) +unset(_BOOST_CMAKEDIR) + +get_target_property(__boost_configs Boost::chrono IMPORTED_CONFIGURATIONS) + +if(__boost_variants AND NOT __boost_configs) + message(STATUS "No suitable boost_chrono variant has been identified!") + if(NOT Boost_DEBUG) + foreach(s IN LISTS __boost_skipped) + message(STATUS " ${s}") + endforeach() + endif() + set(boost_chrono_FOUND 0) + set(boost_chrono_NOT_FOUND_MESSAGE "No suitable build variant has been found.") + unset(__boost_skipped) + unset(__boost_configs) + unset(__boost_variants) + unset(_BOOST_CHRONO_DEPS) + return() +endif() + +unset(__boost_skipped) +unset(__boost_configs) +unset(__boost_variants) + +if(_BOOST_CHRONO_DEPS) + list(REMOVE_DUPLICATES _BOOST_CHRONO_DEPS) + message(STATUS "Adding boost_chrono dependencies: ${_BOOST_CHRONO_DEPS}") +endif() + +foreach(dep_boost_chrono IN LISTS _BOOST_CHRONO_DEPS) + set(_BOOST_QUIET) + if(boost_chrono_FIND_QUIETLY) + set(_BOOST_QUIET QUIET) + endif() + set(_BOOST_REQUIRED) + if(boost_chrono_FIND_REQUIRED) + set(_BOOST_REQUIRED REQUIRED) + endif() + get_filename_component(_BOOST_CMAKEDIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE) + find_package(boost_${dep_boost_chrono} 1.70.0 EXACT CONFIG ${_BOOST_REQUIRED} ${_BOOST_QUIET} HINTS ${_BOOST_CMAKEDIR}) + set_property(TARGET Boost::chrono APPEND PROPERTY INTERFACE_LINK_LIBRARIES Boost::${dep_boost_chrono}) + unset(_BOOST_QUIET) + unset(_BOOST_REQUIRED) + unset(_BOOST_CMAKEDIR) + if(NOT boost_${dep_boost_chrono}_FOUND) + set(boost_chrono_FOUND 0) + set(boost_chrono_NOT_FOUND_MESSAGE "A required dependency, boost_${dep_boost_chrono}, has not been found.") + unset(_BOOST_CHRONO_DEPS) + return() + endif() +endforeach() + +unset(_BOOST_CHRONO_DEPS) diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_chrono-1.70.0/libboost_chrono-variant-shared.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_chrono-1.70.0/libboost_chrono-variant-shared.cmake new file mode 100644 index 0000000..7f07aed --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_chrono-1.70.0/libboost_chrono-variant-shared.cmake @@ -0,0 +1,62 @@ +# Generated by Boost 1.70.0 + +# address-model=64 + +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + _BOOST_SKIPPED("libboost_chrono.so.1.70.0" "64 bit, need 32") + return() +endif() + +# layout=system + +# toolset=gcc8 + +# link=shared + +if(NOT "${Boost_USE_STATIC_LIBS}" STREQUAL "" AND Boost_USE_STATIC_LIBS) + _BOOST_SKIPPED("libboost_chrono.so.1.70.0" "shared, Boost_USE_STATIC_LIBS=${Boost_USE_STATIC_LIBS}") + return() +endif() + +if(NOT BUILD_SHARED_LIBS) + _BOOST_SKIPPED("libboost_chrono.so.1.70.0" "shared, BUILD_SHARED_LIBS not ON, set Boost_USE_STATIC_LIBS=OFF to override") + return() +endif() + +# runtime-link=shared + +if(Boost_USE_STATIC_RUNTIME) + _BOOST_SKIPPED("libboost_chrono.so.1.70.0" "shared runtime, Boost_USE_STATIC_RUNTIME=${Boost_USE_STATIC_RUNTIME}") + return() +endif() + +# runtime-debugging=off + +if(Boost_USE_DEBUG_RUNTIME) + _BOOST_SKIPPED("libboost_chrono.so.1.70.0" "release runtime, Boost_USE_DEBUG_RUNTIME=${Boost_USE_DEBUG_RUNTIME}") + return() +endif() + +# threading=multi + +# variant=release + +if(NOT "${Boost_USE_RELEASE_LIBS}" STREQUAL "" AND NOT Boost_USE_RELEASE_LIBS) + _BOOST_SKIPPED("libboost_chrono.so.1.70.0" "release, Boost_USE_RELEASE_LIBS=${Boost_USE_RELEASE_LIBS}") + return() +endif() + +message(STATUS " libboost_chrono.so.1.70.0") + +# Target file name: libboost_chrono.so.1.70.0 +set_property(TARGET Boost::chrono APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(Boost::chrono PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE CXX + IMPORTED_LOCATION_RELEASE "${_BOOST_LIBDIR}/libboost_chrono.so.1.70.0" + ) + +set_target_properties(Boost::chrono PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "BOOST_CHRONO_DYN_LINK" + ) + +list(APPEND _BOOST_CHRONO_DEPS headers) diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_chrono-1.70.0/libboost_chrono-variant-static.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_chrono-1.70.0/libboost_chrono-variant-static.cmake new file mode 100644 index 0000000..d7477b4 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_chrono-1.70.0/libboost_chrono-variant-static.cmake @@ -0,0 +1,58 @@ +# Generated by Boost 1.70.0 + +# address-model=64 + +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + _BOOST_SKIPPED("libboost_chrono.a" "64 bit, need 32") + return() +endif() + +# layout=system + +# toolset=gcc8 + +# link=static + +if(NOT "${Boost_USE_STATIC_LIBS}" STREQUAL "" AND NOT Boost_USE_STATIC_LIBS) + _BOOST_SKIPPED("libboost_chrono.a" "static, Boost_USE_STATIC_LIBS=${Boost_USE_STATIC_LIBS}") + return() +endif() + +if(BUILD_SHARED_LIBS) + _BOOST_SKIPPED("libboost_chrono.a" "static, BUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}, set Boost_USE_STATIC_LIBS=ON to override") + return() +endif() + +# runtime-link=shared + +if(Boost_USE_STATIC_RUNTIME) + _BOOST_SKIPPED("libboost_chrono.a" "shared runtime, Boost_USE_STATIC_RUNTIME=${Boost_USE_STATIC_RUNTIME}") + return() +endif() + +# runtime-debugging=off + +if(Boost_USE_DEBUG_RUNTIME) + _BOOST_SKIPPED("libboost_chrono.a" "release runtime, Boost_USE_DEBUG_RUNTIME=${Boost_USE_DEBUG_RUNTIME}") + return() +endif() + +# threading=multi + +# variant=release + +if(NOT "${Boost_USE_RELEASE_LIBS}" STREQUAL "" AND NOT Boost_USE_RELEASE_LIBS) + _BOOST_SKIPPED("libboost_chrono.a" "release, Boost_USE_RELEASE_LIBS=${Boost_USE_RELEASE_LIBS}") + return() +endif() + +message(STATUS " libboost_chrono.a") + +# Target file name: libboost_chrono.a +set_property(TARGET Boost::chrono APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(Boost::chrono PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE CXX + IMPORTED_LOCATION_RELEASE "${_BOOST_LIBDIR}/libboost_chrono.a" + ) + +list(APPEND _BOOST_CHRONO_DEPS headers) diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_headers-1.70.0/boost_headers-config-version.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_headers-1.70.0/boost_headers-config-version.cmake new file mode 100644 index 0000000..114bba0 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_headers-1.70.0/boost_headers-config-version.cmake @@ -0,0 +1,12 @@ +# Generated by Boost 1.70.0 + +set(PACKAGE_VERSION 1.70.0) + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_headers-1.70.0/boost_headers-config.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_headers-1.70.0/boost_headers-config.cmake new file mode 100644 index 0000000..0305ecd --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_headers-1.70.0/boost_headers-config.cmake @@ -0,0 +1,20 @@ +# Generated by Boost 1.70.0 + +if(TARGET Boost::headers) + return() +endif() + +message(STATUS "Found boost_headers ${boost_headers_VERSION} at ${boost_headers_DIR}") + +# Compute the include and library directories relative to this file. +get_filename_component(_BOOST_CMAKEDIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE) +get_filename_component(_BOOST_INCLUDEDIR "${_BOOST_CMAKEDIR}/../../include/" ABSOLUTE) +get_filename_component(_BOOST_LIBDIR "${_BOOST_CMAKEDIR}/../" ABSOLUTE) + +# Create imported target Boost::headers +add_library(Boost::headers INTERFACE IMPORTED) + +set_target_properties(Boost::headers PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_BOOST_INCLUDEDIR}" + INTERFACE_COMPILE_DEFINITIONS "BOOST_ALL_NO_LIB" +) diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_system-1.70.0/boost_system-config-version.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_system-1.70.0/boost_system-config-version.cmake new file mode 100644 index 0000000..114bba0 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_system-1.70.0/boost_system-config-version.cmake @@ -0,0 +1,12 @@ +# Generated by Boost 1.70.0 + +set(PACKAGE_VERSION 1.70.0) + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_system-1.70.0/boost_system-config.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_system-1.70.0/boost_system-config.cmake new file mode 100644 index 0000000..40b10d0 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_system-1.70.0/boost_system-config.cmake @@ -0,0 +1,98 @@ +# Generated by Boost 1.70.0 + +if(TARGET Boost::system) + return() +endif() + +message(STATUS "Found boost_system ${boost_system_VERSION} at ${boost_system_DIR}") + +# Compute the include and library directories relative to this file. +get_filename_component(_BOOST_CMAKEDIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE) +get_filename_component(_BOOST_INCLUDEDIR "${_BOOST_CMAKEDIR}/../../include/" ABSOLUTE) +get_filename_component(_BOOST_LIBDIR "${_BOOST_CMAKEDIR}/../" ABSOLUTE) + +# Create imported target Boost::system +add_library(Boost::system UNKNOWN IMPORTED) + +set_target_properties(Boost::system PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_BOOST_INCLUDEDIR}" + INTERFACE_COMPILE_DEFINITIONS "BOOST_ALL_NO_LIB" +) + +include(${CMAKE_CURRENT_LIST_DIR}/../BoostDetectToolset-1.70.0.cmake) + +if(Boost_DEBUG) + message(STATUS "Scanning ${CMAKE_CURRENT_LIST_DIR}/libboost_system-variant*.cmake") +endif() + +file(GLOB __boost_variants "${CMAKE_CURRENT_LIST_DIR}/libboost_system-variant*.cmake") + +macro(_BOOST_SKIPPED fname reason) + if(Boost_DEBUG) + message(STATUS " ... skipped ${fname} (${reason})") + endif() + list(APPEND __boost_skipped "${fname} (${reason})") +endmacro() + +foreach(f IN LISTS __boost_variants) + if(Boost_DEBUG) + message(STATUS " Including ${f}") + endif() + include(${f}) +endforeach() + +unset(_BOOST_LIBDIR) +unset(_BOOST_INCLUDEDIR) +unset(_BOOST_CMAKEDIR) + +get_target_property(__boost_configs Boost::system IMPORTED_CONFIGURATIONS) + +if(__boost_variants AND NOT __boost_configs) + message(STATUS "No suitable boost_system variant has been identified!") + if(NOT Boost_DEBUG) + foreach(s IN LISTS __boost_skipped) + message(STATUS " ${s}") + endforeach() + endif() + set(boost_system_FOUND 0) + set(boost_system_NOT_FOUND_MESSAGE "No suitable build variant has been found.") + unset(__boost_skipped) + unset(__boost_configs) + unset(__boost_variants) + unset(_BOOST_SYSTEM_DEPS) + return() +endif() + +unset(__boost_skipped) +unset(__boost_configs) +unset(__boost_variants) + +if(_BOOST_SYSTEM_DEPS) + list(REMOVE_DUPLICATES _BOOST_SYSTEM_DEPS) + message(STATUS "Adding boost_system dependencies: ${_BOOST_SYSTEM_DEPS}") +endif() + +foreach(dep_boost_system IN LISTS _BOOST_SYSTEM_DEPS) + set(_BOOST_QUIET) + if(boost_system_FIND_QUIETLY) + set(_BOOST_QUIET QUIET) + endif() + set(_BOOST_REQUIRED) + if(boost_system_FIND_REQUIRED) + set(_BOOST_REQUIRED REQUIRED) + endif() + get_filename_component(_BOOST_CMAKEDIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE) + find_package(boost_${dep_boost_system} 1.70.0 EXACT CONFIG ${_BOOST_REQUIRED} ${_BOOST_QUIET} HINTS ${_BOOST_CMAKEDIR}) + set_property(TARGET Boost::system APPEND PROPERTY INTERFACE_LINK_LIBRARIES Boost::${dep_boost_system}) + unset(_BOOST_QUIET) + unset(_BOOST_REQUIRED) + unset(_BOOST_CMAKEDIR) + if(NOT boost_${dep_boost_system}_FOUND) + set(boost_system_FOUND 0) + set(boost_system_NOT_FOUND_MESSAGE "A required dependency, boost_${dep_boost_system}, has not been found.") + unset(_BOOST_SYSTEM_DEPS) + return() + endif() +endforeach() + +unset(_BOOST_SYSTEM_DEPS) diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_system-1.70.0/libboost_system-variant-shared.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_system-1.70.0/libboost_system-variant-shared.cmake new file mode 100644 index 0000000..751db32 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_system-1.70.0/libboost_system-variant-shared.cmake @@ -0,0 +1,62 @@ +# Generated by Boost 1.70.0 + +# address-model=64 + +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + _BOOST_SKIPPED("libboost_system.so.1.70.0" "64 bit, need 32") + return() +endif() + +# layout=system + +# toolset=gcc8 + +# link=shared + +if(NOT "${Boost_USE_STATIC_LIBS}" STREQUAL "" AND Boost_USE_STATIC_LIBS) + _BOOST_SKIPPED("libboost_system.so.1.70.0" "shared, Boost_USE_STATIC_LIBS=${Boost_USE_STATIC_LIBS}") + return() +endif() + +if(NOT BUILD_SHARED_LIBS) + _BOOST_SKIPPED("libboost_system.so.1.70.0" "shared, BUILD_SHARED_LIBS not ON, set Boost_USE_STATIC_LIBS=OFF to override") + return() +endif() + +# runtime-link=shared + +if(Boost_USE_STATIC_RUNTIME) + _BOOST_SKIPPED("libboost_system.so.1.70.0" "shared runtime, Boost_USE_STATIC_RUNTIME=${Boost_USE_STATIC_RUNTIME}") + return() +endif() + +# runtime-debugging=off + +if(Boost_USE_DEBUG_RUNTIME) + _BOOST_SKIPPED("libboost_system.so.1.70.0" "release runtime, Boost_USE_DEBUG_RUNTIME=${Boost_USE_DEBUG_RUNTIME}") + return() +endif() + +# threading=multi + +# variant=release + +if(NOT "${Boost_USE_RELEASE_LIBS}" STREQUAL "" AND NOT Boost_USE_RELEASE_LIBS) + _BOOST_SKIPPED("libboost_system.so.1.70.0" "release, Boost_USE_RELEASE_LIBS=${Boost_USE_RELEASE_LIBS}") + return() +endif() + +message(STATUS " libboost_system.so.1.70.0") + +# Target file name: libboost_system.so.1.70.0 +set_property(TARGET Boost::system APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(Boost::system PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE CXX + IMPORTED_LOCATION_RELEASE "${_BOOST_LIBDIR}/libboost_system.so.1.70.0" + ) + +set_target_properties(Boost::system PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "BOOST_SYSTEM_DYN_LINK" + ) + +list(APPEND _BOOST_SYSTEM_DEPS headers) diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_system-1.70.0/libboost_system-variant-static.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_system-1.70.0/libboost_system-variant-static.cmake new file mode 100644 index 0000000..b10e0b9 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_system-1.70.0/libboost_system-variant-static.cmake @@ -0,0 +1,58 @@ +# Generated by Boost 1.70.0 + +# address-model=64 + +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + _BOOST_SKIPPED("libboost_system.a" "64 bit, need 32") + return() +endif() + +# layout=system + +# toolset=gcc8 + +# link=static + +if(NOT "${Boost_USE_STATIC_LIBS}" STREQUAL "" AND NOT Boost_USE_STATIC_LIBS) + _BOOST_SKIPPED("libboost_system.a" "static, Boost_USE_STATIC_LIBS=${Boost_USE_STATIC_LIBS}") + return() +endif() + +if(BUILD_SHARED_LIBS) + _BOOST_SKIPPED("libboost_system.a" "static, BUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}, set Boost_USE_STATIC_LIBS=ON to override") + return() +endif() + +# runtime-link=shared + +if(Boost_USE_STATIC_RUNTIME) + _BOOST_SKIPPED("libboost_system.a" "shared runtime, Boost_USE_STATIC_RUNTIME=${Boost_USE_STATIC_RUNTIME}") + return() +endif() + +# runtime-debugging=off + +if(Boost_USE_DEBUG_RUNTIME) + _BOOST_SKIPPED("libboost_system.a" "release runtime, Boost_USE_DEBUG_RUNTIME=${Boost_USE_DEBUG_RUNTIME}") + return() +endif() + +# threading=multi + +# variant=release + +if(NOT "${Boost_USE_RELEASE_LIBS}" STREQUAL "" AND NOT Boost_USE_RELEASE_LIBS) + _BOOST_SKIPPED("libboost_system.a" "release, Boost_USE_RELEASE_LIBS=${Boost_USE_RELEASE_LIBS}") + return() +endif() + +message(STATUS " libboost_system.a") + +# Target file name: libboost_system.a +set_property(TARGET Boost::system APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(Boost::system PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE CXX + IMPORTED_LOCATION_RELEASE "${_BOOST_LIBDIR}/libboost_system.a" + ) + +list(APPEND _BOOST_SYSTEM_DEPS headers) diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_timer-1.70.0/boost_timer-config-version.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_timer-1.70.0/boost_timer-config-version.cmake new file mode 100644 index 0000000..114bba0 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_timer-1.70.0/boost_timer-config-version.cmake @@ -0,0 +1,12 @@ +# Generated by Boost 1.70.0 + +set(PACKAGE_VERSION 1.70.0) + +if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION) + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if(PACKAGE_FIND_VERSION STREQUAL PACKAGE_VERSION) + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_timer-1.70.0/boost_timer-config.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_timer-1.70.0/boost_timer-config.cmake new file mode 100644 index 0000000..b0d0e54 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_timer-1.70.0/boost_timer-config.cmake @@ -0,0 +1,98 @@ +# Generated by Boost 1.70.0 + +if(TARGET Boost::timer) + return() +endif() + +message(STATUS "Found boost_timer ${boost_timer_VERSION} at ${boost_timer_DIR}") + +# Compute the include and library directories relative to this file. +get_filename_component(_BOOST_CMAKEDIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE) +get_filename_component(_BOOST_INCLUDEDIR "${_BOOST_CMAKEDIR}/../../include/" ABSOLUTE) +get_filename_component(_BOOST_LIBDIR "${_BOOST_CMAKEDIR}/../" ABSOLUTE) + +# Create imported target Boost::timer +add_library(Boost::timer UNKNOWN IMPORTED) + +set_target_properties(Boost::timer PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${_BOOST_INCLUDEDIR}" + INTERFACE_COMPILE_DEFINITIONS "BOOST_ALL_NO_LIB" +) + +include(${CMAKE_CURRENT_LIST_DIR}/../BoostDetectToolset-1.70.0.cmake) + +if(Boost_DEBUG) + message(STATUS "Scanning ${CMAKE_CURRENT_LIST_DIR}/libboost_timer-variant*.cmake") +endif() + +file(GLOB __boost_variants "${CMAKE_CURRENT_LIST_DIR}/libboost_timer-variant*.cmake") + +macro(_BOOST_SKIPPED fname reason) + if(Boost_DEBUG) + message(STATUS " ... skipped ${fname} (${reason})") + endif() + list(APPEND __boost_skipped "${fname} (${reason})") +endmacro() + +foreach(f IN LISTS __boost_variants) + if(Boost_DEBUG) + message(STATUS " Including ${f}") + endif() + include(${f}) +endforeach() + +unset(_BOOST_LIBDIR) +unset(_BOOST_INCLUDEDIR) +unset(_BOOST_CMAKEDIR) + +get_target_property(__boost_configs Boost::timer IMPORTED_CONFIGURATIONS) + +if(__boost_variants AND NOT __boost_configs) + message(STATUS "No suitable boost_timer variant has been identified!") + if(NOT Boost_DEBUG) + foreach(s IN LISTS __boost_skipped) + message(STATUS " ${s}") + endforeach() + endif() + set(boost_timer_FOUND 0) + set(boost_timer_NOT_FOUND_MESSAGE "No suitable build variant has been found.") + unset(__boost_skipped) + unset(__boost_configs) + unset(__boost_variants) + unset(_BOOST_TIMER_DEPS) + return() +endif() + +unset(__boost_skipped) +unset(__boost_configs) +unset(__boost_variants) + +if(_BOOST_TIMER_DEPS) + list(REMOVE_DUPLICATES _BOOST_TIMER_DEPS) + message(STATUS "Adding boost_timer dependencies: ${_BOOST_TIMER_DEPS}") +endif() + +foreach(dep_boost_timer IN LISTS _BOOST_TIMER_DEPS) + set(_BOOST_QUIET) + if(boost_timer_FIND_QUIETLY) + set(_BOOST_QUIET QUIET) + endif() + set(_BOOST_REQUIRED) + if(boost_timer_FIND_REQUIRED) + set(_BOOST_REQUIRED REQUIRED) + endif() + get_filename_component(_BOOST_CMAKEDIR "${CMAKE_CURRENT_LIST_DIR}/../" ABSOLUTE) + find_package(boost_${dep_boost_timer} 1.70.0 EXACT CONFIG ${_BOOST_REQUIRED} ${_BOOST_QUIET} HINTS ${_BOOST_CMAKEDIR}) + set_property(TARGET Boost::timer APPEND PROPERTY INTERFACE_LINK_LIBRARIES Boost::${dep_boost_timer}) + unset(_BOOST_QUIET) + unset(_BOOST_REQUIRED) + unset(_BOOST_CMAKEDIR) + if(NOT boost_${dep_boost_timer}_FOUND) + set(boost_timer_FOUND 0) + set(boost_timer_NOT_FOUND_MESSAGE "A required dependency, boost_${dep_boost_timer}, has not been found.") + unset(_BOOST_TIMER_DEPS) + return() + endif() +endforeach() + +unset(_BOOST_TIMER_DEPS) diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_timer-1.70.0/libboost_timer-variant-shared.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_timer-1.70.0/libboost_timer-variant-shared.cmake new file mode 100644 index 0000000..b35b59d --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_timer-1.70.0/libboost_timer-variant-shared.cmake @@ -0,0 +1,62 @@ +# Generated by Boost 1.70.0 + +# address-model=64 + +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + _BOOST_SKIPPED("libboost_timer.so.1.70.0" "64 bit, need 32") + return() +endif() + +# layout=system + +# toolset=gcc8 + +# link=shared + +if(NOT "${Boost_USE_STATIC_LIBS}" STREQUAL "" AND Boost_USE_STATIC_LIBS) + _BOOST_SKIPPED("libboost_timer.so.1.70.0" "shared, Boost_USE_STATIC_LIBS=${Boost_USE_STATIC_LIBS}") + return() +endif() + +if(NOT BUILD_SHARED_LIBS) + _BOOST_SKIPPED("libboost_timer.so.1.70.0" "shared, BUILD_SHARED_LIBS not ON, set Boost_USE_STATIC_LIBS=OFF to override") + return() +endif() + +# runtime-link=shared + +if(Boost_USE_STATIC_RUNTIME) + _BOOST_SKIPPED("libboost_timer.so.1.70.0" "shared runtime, Boost_USE_STATIC_RUNTIME=${Boost_USE_STATIC_RUNTIME}") + return() +endif() + +# runtime-debugging=off + +if(Boost_USE_DEBUG_RUNTIME) + _BOOST_SKIPPED("libboost_timer.so.1.70.0" "release runtime, Boost_USE_DEBUG_RUNTIME=${Boost_USE_DEBUG_RUNTIME}") + return() +endif() + +# threading=multi + +# variant=release + +if(NOT "${Boost_USE_RELEASE_LIBS}" STREQUAL "" AND NOT Boost_USE_RELEASE_LIBS) + _BOOST_SKIPPED("libboost_timer.so.1.70.0" "release, Boost_USE_RELEASE_LIBS=${Boost_USE_RELEASE_LIBS}") + return() +endif() + +message(STATUS " libboost_timer.so.1.70.0") + +# Target file name: libboost_timer.so.1.70.0 +set_property(TARGET Boost::timer APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(Boost::timer PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE CXX + IMPORTED_LOCATION_RELEASE "${_BOOST_LIBDIR}/libboost_timer.so.1.70.0" + ) + +set_target_properties(Boost::timer PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "BOOST_TIMER_DYN_LINK" + ) + +list(APPEND _BOOST_TIMER_DEPS chrono headers) diff --git a/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_timer-1.70.0/libboost_timer-variant-static.cmake b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_timer-1.70.0/libboost_timer-variant-static.cmake new file mode 100644 index 0000000..37fa9c0 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CMakePackageFixtures/boost_timer-1.70.0/libboost_timer-variant-static.cmake @@ -0,0 +1,58 @@ +# Generated by Boost 1.70.0 + +# address-model=64 + +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + _BOOST_SKIPPED("libboost_timer.a" "64 bit, need 32") + return() +endif() + +# layout=system + +# toolset=gcc8 + +# link=static + +if(NOT "${Boost_USE_STATIC_LIBS}" STREQUAL "" AND NOT Boost_USE_STATIC_LIBS) + _BOOST_SKIPPED("libboost_timer.a" "static, Boost_USE_STATIC_LIBS=${Boost_USE_STATIC_LIBS}") + return() +endif() + +if(BUILD_SHARED_LIBS) + _BOOST_SKIPPED("libboost_timer.a" "static, BUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}, set Boost_USE_STATIC_LIBS=ON to override") + return() +endif() + +# runtime-link=shared + +if(Boost_USE_STATIC_RUNTIME) + _BOOST_SKIPPED("libboost_timer.a" "shared runtime, Boost_USE_STATIC_RUNTIME=${Boost_USE_STATIC_RUNTIME}") + return() +endif() + +# runtime-debugging=off + +if(Boost_USE_DEBUG_RUNTIME) + _BOOST_SKIPPED("libboost_timer.a" "release runtime, Boost_USE_DEBUG_RUNTIME=${Boost_USE_DEBUG_RUNTIME}") + return() +endif() + +# threading=multi + +# variant=release + +if(NOT "${Boost_USE_RELEASE_LIBS}" STREQUAL "" AND NOT Boost_USE_RELEASE_LIBS) + _BOOST_SKIPPED("libboost_timer.a" "release, Boost_USE_RELEASE_LIBS=${Boost_USE_RELEASE_LIBS}") + return() +endif() + +message(STATUS " libboost_timer.a") + +# Target file name: libboost_timer.a +set_property(TARGET Boost::timer APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) +set_target_properties(Boost::timer PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE CXX + IMPORTED_LOCATION_RELEASE "${_BOOST_LIBDIR}/libboost_timer.a" + ) + +list(APPEND _BOOST_TIMER_DEPS chrono headers) diff --git a/Tests/RunCMake/FindBoost/CommonNotFound-stderr.txt b/Tests/RunCMake/FindBoost/CommonNotFound-stderr.txt new file mode 100644 index 0000000..8d98f9d --- /dev/null +++ b/Tests/RunCMake/FindBoost/CommonNotFound-stderr.txt @@ -0,0 +1 @@ +.* diff --git a/Tests/RunCMake/FindBoost/CommonNotFound-stdout.txt b/Tests/RunCMake/FindBoost/CommonNotFound-stdout.txt new file mode 100644 index 0000000..644469e --- /dev/null +++ b/Tests/RunCMake/FindBoost/CommonNotFound-stdout.txt @@ -0,0 +1 @@ +-- Could NOT find Boost diff --git a/Tests/RunCMake/FindBoost/CommonNotFound.cmake b/Tests/RunCMake/FindBoost/CommonNotFound.cmake new file mode 100644 index 0000000..864a549 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CommonNotFound.cmake @@ -0,0 +1,2 @@ +# Make sure to use the module mode signature here to not bypass FindBoost +find_package(Boost 1.80 COMPONENTS timer foobar) diff --git a/Tests/RunCMake/FindBoost/CommonResults-stdout.txt b/Tests/RunCMake/FindBoost/CommonResults-stdout.txt new file mode 100644 index 0000000..a560843 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CommonResults-stdout.txt @@ -0,0 +1,13 @@ +-- Found Boost: [^ +]* \(found suitable version "1\.70\.0", minimum required is "1\.60"\) found components: .*timer.* +-- Boost_FOUND +-- Boost_VERSION=(107000|1\.70\.0) +-- Boost_VERSION_MAJOR=1 +-- Boost_VERSION_MINOR=70 +-- Boost_VERSION_PATCH=0 +-- Boost_VERSION_COUNT=3 +-- Boost::headers +-- Boost::boost +-- Boost::chrono +-- Boost::timer +(-- Boost::system)? diff --git a/Tests/RunCMake/FindBoost/CommonResults.cmake b/Tests/RunCMake/FindBoost/CommonResults.cmake new file mode 100644 index 0000000..6876800 --- /dev/null +++ b/Tests/RunCMake/FindBoost/CommonResults.cmake @@ -0,0 +1,25 @@ +# Make sure to use the module mode signature here to not bypass FindBoost +find_package(Boost 1.60 COMPONENTS timer MODULE) +if(Boost_FOUND) + message(STATUS "Boost_FOUND") +endif() +message(STATUS "Boost_VERSION=${Boost_VERSION}") +message(STATUS "Boost_VERSION_MAJOR=${Boost_VERSION_MAJOR}") +message(STATUS "Boost_VERSION_MINOR=${Boost_VERSION_MINOR}") +message(STATUS "Boost_VERSION_PATCH=${Boost_VERSION_PATCH}") +message(STATUS "Boost_VERSION_COUNT=${Boost_VERSION_COUNT}") +if(TARGET Boost::headers) + message(STATUS "Boost::headers") +endif() +if(TARGET Boost::boost) + message(STATUS "Boost::boost") +endif() +if(TARGET Boost::chrono) + message(STATUS "Boost::chrono") +endif() +if(TARGET Boost::timer) + message(STATUS "Boost::timer") +endif() +if(TARGET Boost::system) + message(STATUS "Boost::system") +endif() diff --git a/Tests/RunCMake/FindBoost/ConfigMode.cmake b/Tests/RunCMake/FindBoost/ConfigMode.cmake new file mode 100644 index 0000000..5600658 --- /dev/null +++ b/Tests/RunCMake/FindBoost/ConfigMode.cmake @@ -0,0 +1,2 @@ +set(Boost_DIR ${CMAKE_CURRENT_SOURCE_DIR}/CMakePackageFixtures/Boost-1.70.0) +include(${CMAKE_CURRENT_SOURCE_DIR}/CommonResults.cmake) diff --git a/Tests/RunCMake/FindBoost/ConfigModeNotFound.cmake b/Tests/RunCMake/FindBoost/ConfigModeNotFound.cmake new file mode 100644 index 0000000..7e90a3c --- /dev/null +++ b/Tests/RunCMake/FindBoost/ConfigModeNotFound.cmake @@ -0,0 +1,2 @@ +set(Boost_DIR ${CMAKE_CURRENT_SOURCE_DIR}/CMakePackageFixtures/Boost-1.70.0) +include(${CMAKE_CURRENT_SOURCE_DIR}/CommonNotFound.cmake) diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/chrono.hpp b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/chrono.hpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/chrono.hpp diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/config.hpp b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/config.hpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/config.hpp diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/system/config.hpp b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/system/config.hpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/system/config.hpp diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/timer.hpp b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/timer.hpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/timer.hpp diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/version.hpp b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/version.hpp new file mode 100644 index 0000000..3ca02b3 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/include/boost/version.hpp @@ -0,0 +1,34 @@ +// Boost version.hpp configuration header file +// ------------------------------// + +// (C) Copyright John maddock 1999. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +// See http://www.boost.org/libs/config for documentation + +#ifndef BOOST_VERSION_HPP +#define BOOST_VERSION_HPP + +// +// Caution: this is the only Boost header that is guaranteed +// to change with every Boost release. Including this header +// will cause a recompile every time a new Boost version is +// used. +// +// BOOST_VERSION % 100 is the patch level +// BOOST_VERSION / 100 % 1000 is the minor version +// BOOST_VERSION / 100000 is the major version + +#define BOOST_VERSION 107000 + +// +// BOOST_LIB_VERSION must be defined to be the same as BOOST_VERSION +// but as a *string* in the form "x_y[_z]" where x is the major version +// number, y is the minor version number, and z is the patch level if not 0. +// This is used by <config/auto_link.hpp> to select which library version to +// link to. + +#define BOOST_LIB_VERSION "1_70" + +#endif diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/boost_chrono-mt-1_70.lib b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/boost_chrono-mt-1_70.lib new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/boost_chrono-mt-1_70.lib diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/boost_system-mt-1_70.lib b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/boost_system-mt-1_70.lib new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/boost_system-mt-1_70.lib diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/boost_timer-mt-1_70.lib b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/boost_timer-mt-1_70.lib new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/boost_timer-mt-1_70.lib diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_chrono-mt-1_70.lib b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_chrono-mt-1_70.lib new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_chrono-mt-1_70.lib diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_chrono.a b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_chrono.a new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_chrono.a diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_chrono.so.1.70.0 b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_chrono.so.1.70.0 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_chrono.so.1.70.0 diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_system-mt-1_70.lib b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_system-mt-1_70.lib new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_system-mt-1_70.lib diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_system.a b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_system.a new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_system.a diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_system.so.1.70.0 b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_system.so.1.70.0 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_system.so.1.70.0 diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_timer-mt-1_70.lib b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_timer-mt-1_70.lib new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_timer-mt-1_70.lib diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_timer.a b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_timer.a new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_timer.a diff --git a/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_timer.so.1.70.0 b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_timer.so.1.70.0 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/Tests/RunCMake/FindBoost/MockInstalls/1.70.0/lib/libboost_timer.so.1.70.0 diff --git a/Tests/RunCMake/FindBoost/ModuleMode.cmake b/Tests/RunCMake/FindBoost/ModuleMode.cmake new file mode 100644 index 0000000..823fc53 --- /dev/null +++ b/Tests/RunCMake/FindBoost/ModuleMode.cmake @@ -0,0 +1,4 @@ +set(BOOST_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/MockInstalls/1.70.0) +set(Boost_NO_BOOST_CMAKE ON) +set(Boost_NO_SYSTEM_PATHS ON) +include(${CMAKE_CURRENT_SOURCE_DIR}/CommonResults.cmake) diff --git a/Tests/RunCMake/FindBoost/ModuleModeNotFound.cmake b/Tests/RunCMake/FindBoost/ModuleModeNotFound.cmake new file mode 100644 index 0000000..aaffbf2 --- /dev/null +++ b/Tests/RunCMake/FindBoost/ModuleModeNotFound.cmake @@ -0,0 +1,4 @@ +set(BOOST_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/MockInstalls/1.70.0) +set(Boost_NO_BOOST_CMAKE ON) +set(Boost_NO_SYSTEM_PATHS ON) +include(${CMAKE_CURRENT_SOURCE_DIR}/CommonNotFound.cmake) diff --git a/Tests/RunCMake/FindBoost/RunCMakeTest.cmake b/Tests/RunCMake/FindBoost/RunCMakeTest.cmake index eef350c..d66bda1 100644 --- a/Tests/RunCMake/FindBoost/RunCMakeTest.cmake +++ b/Tests/RunCMake/FindBoost/RunCMakeTest.cmake @@ -3,3 +3,14 @@ unset(ENV{Boost_ROOT}) run_cmake(CMakePackage) run_cmake(NoCXX) + +set(RunCMake-stdout-file CommonResults-stdout.txt) +run_cmake(ConfigMode) +run_cmake(ModuleMode) +unset(RunCMake-stdout-file) +set(RunCMake-stdout-file CommonNotFound-stdout.txt) +set(RunCMake-stderr-file CommonNotFound-stderr.txt) +run_cmake(ConfigModeNotFound) +run_cmake(ModuleModeNotFound) +unset(RunCMake-stdout-file) +unset(RunCMake-stderr-file) diff --git a/Tests/RunCMake/UseSWIG/CMP0086-common.cmake b/Tests/RunCMake/UseSWIG/CMP0086-common.cmake index c02592a..ef90218 100644 --- a/Tests/RunCMake/UseSWIG/CMP0086-common.cmake +++ b/Tests/RunCMake/UseSWIG/CMP0086-common.cmake @@ -8,4 +8,4 @@ include(UseSWIG) set_property (SOURCE example.i PROPERTY SWIG_MODULE_NAME "new_example") swig_add_library(example LANGUAGE python TYPE MODULE SOURCES example.i) -target_link_libraries(example PRIVATE Python::Python) +target_link_libraries(example PRIVATE Python::Module) diff --git a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake index 4918f7c..191f56d 100644 --- a/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake +++ b/Tests/RunCMake/XcodeProject/RunCMakeTest.cmake @@ -256,4 +256,19 @@ if(XCODE_VERSION VERSION_GREATER_EQUAL 8) deployment_target_test(watchOS watchsimulator) endif() +if(XCODE_VERSION VERSION_GREATER_EQUAL 8) + function(xctest_lookup_test SystemName SDK) + set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/XCTestLookup-${SDK}-build) + set(RunCMake_TEST_OPTIONS "-DCMAKE_SYSTEM_NAME=${SystemName}" "-DCMAKE_OSX_SYSROOT=${SDK}") + + run_cmake(XCTestLookup) + endfunction() + + xctest_lookup_test(Darwin macosx) + xctest_lookup_test(iOS iphoneos) + xctest_lookup_test(iOS iphonesimulator) + xctest_lookup_test(tvOS appletvos) + xctest_lookup_test(tvOS appletvsimulator) +endif() + # Please add macOS-only tests above before the device-specific tests. diff --git a/Tests/RunCMake/XcodeProject/XCTestLookup.cmake b/Tests/RunCMake/XcodeProject/XCTestLookup.cmake new file mode 100644 index 0000000..77676e5 --- /dev/null +++ b/Tests/RunCMake/XcodeProject/XCTestLookup.cmake @@ -0,0 +1,3 @@ +enable_language(C) + +find_package(XCTest REQUIRED) diff --git a/Tests/UseSWIG/ModuleName/CMakeLists.txt b/Tests/UseSWIG/ModuleName/CMakeLists.txt index de63883..435b441 100644 --- a/Tests/UseSWIG/ModuleName/CMakeLists.txt +++ b/Tests/UseSWIG/ModuleName/CMakeLists.txt @@ -34,7 +34,7 @@ set_target_properties (example1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example1" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example1" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example1") -target_link_libraries(example1 PRIVATE Python2::Python) +target_link_libraries(example1 PRIVATE Python2::Module) add_test (NAME ModuleName.example1 diff --git a/Tests/UseSWIG/ModuleVersion2/CMakeLists.txt b/Tests/UseSWIG/ModuleVersion2/CMakeLists.txt index a7ee210..093e858 100644 --- a/Tests/UseSWIG/ModuleVersion2/CMakeLists.txt +++ b/Tests/UseSWIG/ModuleVersion2/CMakeLists.txt @@ -33,7 +33,7 @@ set_target_properties (example1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2") -target_link_libraries(example1 PRIVATE Python2::Python) +target_link_libraries(example1 PRIVATE Python2::Module) # re-use sample interface file for another plugin swig_add_library(example2 @@ -44,7 +44,7 @@ set_target_properties (example2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3") -target_link_libraries(example2 PRIVATE Python3::Python) +target_link_libraries(example2 PRIVATE Python3::Module) add_test (NAME ModuleVersion2.example1 diff --git a/Tests/UseSWIG/MultipleModules/CMakeLists.txt b/Tests/UseSWIG/MultipleModules/CMakeLists.txt index f1dc379..4380080 100644 --- a/Tests/UseSWIG/MultipleModules/CMakeLists.txt +++ b/Tests/UseSWIG/MultipleModules/CMakeLists.txt @@ -36,7 +36,7 @@ set_target_properties (example1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python") -target_link_libraries(example1 PRIVATE Python::Python) +target_link_libraries(example1 PRIVATE Python::Module) # re-use sample interface file for another plugin set_property(SOURCE "../example.i" APPEND PROPERTY diff --git a/Tests/UseSWIG/MultiplePython/CMakeLists.txt b/Tests/UseSWIG/MultiplePython/CMakeLists.txt index 8f87755..cf6c80e 100644 --- a/Tests/UseSWIG/MultiplePython/CMakeLists.txt +++ b/Tests/UseSWIG/MultiplePython/CMakeLists.txt @@ -34,7 +34,7 @@ set_target_properties (example1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python2") -target_link_libraries(example1 PRIVATE Python2::Python) +target_link_libraries(example1 PRIVATE Python2::Module) # re-use sample interface file for another plugin swig_add_library(example2 @@ -46,7 +46,7 @@ set_target_properties (example2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/Python3") -target_link_libraries(example2 PRIVATE Python3::Python) +target_link_libraries(example2 PRIVATE Python3::Module) diff --git a/Tests/UseSWIG/SwigSrcFileExtension/CMakeLists.txt b/Tests/UseSWIG/SwigSrcFileExtension/CMakeLists.txt index 7eb73d4..f70ce49 100644 --- a/Tests/UseSWIG/SwigSrcFileExtension/CMakeLists.txt +++ b/Tests/UseSWIG/SwigSrcFileExtension/CMakeLists.txt @@ -16,11 +16,11 @@ set(SWIG_SOURCE_FILE_EXTENSIONS ".i" ".swg") # Generate a Python module out of `.i` swig_add_library(my_add LANGUAGE python SOURCES my_add.i) -target_link_libraries(my_add Python::Python) +target_link_libraries(my_add Python::Module) # Generate a Python module out of `.swg` swig_add_library(my_sub LANGUAGE python SOURCES my_sub.swg) -target_link_libraries(my_sub Python::Python) +target_link_libraries(my_sub Python::Module) # Add a test add_test(NAME SwigSrcFileExtension diff --git a/Tests/UseSWIG/UseTargetINCLUDE_DIRECTORIES/CMakeLists.txt b/Tests/UseSWIG/UseTargetINCLUDE_DIRECTORIES/CMakeLists.txt index fbb72d5..80a2e16 100644 --- a/Tests/UseSWIG/UseTargetINCLUDE_DIRECTORIES/CMakeLists.txt +++ b/Tests/UseSWIG/UseTargetINCLUDE_DIRECTORIES/CMakeLists.txt @@ -25,7 +25,7 @@ set_target_properties (example1 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example1" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example1" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example1") -target_link_libraries(example1 PRIVATE Python3::Python) +target_link_libraries(example1 PRIVATE Python3::Module) # Check that source property override target property @@ -42,4 +42,4 @@ set_target_properties (example2 PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example2" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example2" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/example2") -target_link_libraries(example2 PRIVATE Python3::Python) +target_link_libraries(example2 PRIVATE Python3::Module) diff --git a/Utilities/Doxygen/CMakeLists.txt b/Utilities/Doxygen/CMakeLists.txt index 2cdd767..ce4cfaf 100644 --- a/Utilities/Doxygen/CMakeLists.txt +++ b/Utilities/Doxygen/CMakeLists.txt @@ -3,7 +3,7 @@ if(NOT CMake_SOURCE_DIR) set(CMakeDeveloperReference_STANDALONE 1) - cmake_minimum_required(VERSION 3.1...3.12 FATAL_ERROR) + cmake_minimum_required(VERSION 3.1...3.14 FATAL_ERROR) get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH) get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH) include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake) diff --git a/Utilities/Release/linux64_release.cmake b/Utilities/Release/linux64_release.cmake index fa585d7..958ed25 100644 --- a/Utilities/Release/linux64_release.cmake +++ b/Utilities/Release/linux64_release.cmake @@ -44,6 +44,7 @@ CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES:STRING=${qt_xcb_libs} set(ENV [[ export CMAKE_PREFIX_PATH=/opt/binutils-2.31 ]]) +set(SIGN "") # Exclude Qt5 tests because our Qt5 is static. set(EXTRA_CTEST_ARGS "-E Qt5") diff --git a/Utilities/Release/osx_release.cmake b/Utilities/Release/osx_release.cmake index be11d47..ac35872 100644 --- a/Utilities/Release/osx_release.cmake +++ b/Utilities/Release/osx_release.cmake @@ -29,5 +29,6 @@ CMake_TEST_NO_FindPackageModeMakefileTest:BOOL=TRUE set(ENV [[ export CMAKE_PREFIX_PATH='/Users/kitware/SDKs/qt-5.6.2-clang-x64' ]]) +set(SIGN "") get_filename_component(path "${CMAKE_CURRENT_LIST_FILE}" PATH) include(${path}/release_cmake.cmake) diff --git a/Utilities/Release/release_cmake.sh.in b/Utilities/Release/release_cmake.sh.in index f363b3d..696a3f4 100755 --- a/Utilities/Release/release_cmake.sh.in +++ b/Utilities/Release/release_cmake.sh.in @@ -150,7 +150,7 @@ for GEN in $generators; do check_exit_value $? "Create $GEN package" || exit 1 done - +@SIGN@ echo "End release" date diff --git a/Utilities/Release/win32_release.cmake b/Utilities/Release/win32_release.cmake index 974c402..468e5f4 100644 --- a/Utilities/Release/win32_release.cmake +++ b/Utilities/Release/win32_release.cmake @@ -45,5 +45,9 @@ set(GIT_EXTRA "git config core.autocrlf true") if(CMAKE_CREATE_VERSION STREQUAL "nightly") # Some tests fail spuriously too often. set(EXTRA_CTEST_ARGS "-E 'ConsoleBuf|Module.ExternalData'") + set(SIGN "") +else() + string(APPEND INITIAL_CACHE "CMake_INSTALL_SIGNTOOL:STRING=signtool\n") + set(SIGN [[signtool sign -v -a -tr http://timestamp.digicert.com -fd sha256 -td sha256 -d "CMake Windows Installer" cmake-*.msi]]) endif() include(${path}/release_cmake.cmake) diff --git a/Utilities/Release/win64_release.cmake b/Utilities/Release/win64_release.cmake index 20529f0..5a93ce6 100644 --- a/Utilities/Release/win64_release.cmake +++ b/Utilities/Release/win64_release.cmake @@ -45,5 +45,9 @@ set(GIT_EXTRA "git config core.autocrlf true") if(CMAKE_CREATE_VERSION STREQUAL "nightly") # Some tests fail spuriously too often. set(EXTRA_CTEST_ARGS "-E 'ConsoleBuf|Module.ExternalData'") + set(SIGN "") +else() + string(APPEND INITIAL_CACHE "CMake_INSTALL_SIGNTOOL:STRING=signtool\n") + set(SIGN [[signtool sign -v -a -tr http://timestamp.digicert.com -fd sha256 -td sha256 -d "CMake Windows Installer" cmake-*.msi]]) endif() include(${path}/release_cmake.cmake) diff --git a/Utilities/Sphinx/CMakeLists.txt b/Utilities/Sphinx/CMakeLists.txt index 15204d6..c5b2bfe 100644 --- a/Utilities/Sphinx/CMakeLists.txt +++ b/Utilities/Sphinx/CMakeLists.txt @@ -3,7 +3,7 @@ if(NOT CMake_SOURCE_DIR) set(CMakeHelp_STANDALONE 1) - cmake_minimum_required(VERSION 3.1...3.12 FATAL_ERROR) + cmake_minimum_required(VERSION 3.1...3.14 FATAL_ERROR) get_filename_component(tmp "${CMAKE_CURRENT_SOURCE_DIR}" PATH) get_filename_component(CMake_SOURCE_DIR "${tmp}" PATH) include(${CMake_SOURCE_DIR}/Modules/CTestUseLaunchers.cmake) @@ -329,6 +329,7 @@ CMAKE_CXX_SOURCES="\ cmGetCMakePropertyCommand \ cmGetDirectoryPropertyCommand \ cmGetFilenameComponentCommand \ + cmGetPipes \ cmGetPropertyCommand \ cmGetSourceFilePropertyCommand \ cmGetTargetPropertyCommand \ @@ -427,6 +428,7 @@ CMAKE_CXX_SOURCES="\ cmUnexpectedCommand \ cmUnsetCommand \ cmUVHandlePtr \ + cmUVProcessChain \ cmVersion \ cmWhileCommand \ cmWorkingDirectory \ |