diff options
83 files changed, 3410 insertions, 1886 deletions
diff --git a/Help/command/ctest_update.rst b/Help/command/ctest_update.rst index df1a4e5..96a11c9 100644 --- a/Help/command/ctest_update.rst +++ b/Help/command/ctest_update.rst @@ -35,4 +35,5 @@ The options are: The update always follows the version control branch currently checked out in the source directory. See the :ref:`CTest Update Step` -documentation for more information. +documentation for information about variables that change the behavior +of ``ctest_update()``. diff --git a/Help/command/project.rst b/Help/command/project.rst index 7e33ccd..41e1112 100644 --- a/Help/command/project.rst +++ b/Help/command/project.rst @@ -112,12 +112,13 @@ Usage The top-level ``CMakeLists.txt`` file for a project must contain a literal, direct call to the :command:`project` command; loading one through the :command:`include` command is not sufficient. If no such -call exists CMake will implicitly add one to the top that enables the -default languages (``C`` and ``CXX``). +call exists, CMake will issue a warning and pretend there is a +``project(Project)`` at the top to enable the default languages +(``C`` and ``CXX``). .. note:: - Call the :command:`cmake_minimum_required` command at the beginning - of the top-level ``CMakeLists.txt`` file even before calling the - :command:`project()` command. It is important to establish version and - policy settings before invoking other commands whose behavior they - may affect. See also policy :policy:`CMP0000`. + Call the :command:`project()` command near the top of the top-level + ``CMakeLists.txt``, but *after* calling :command:`cmake_minimum_required`. + It is important to establish version and policy settings before invoking + other commands whose behavior they may affect. + See also policy :policy:`CMP0000`. diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 6d93ae9..86dc186 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -583,6 +583,7 @@ Variables for CTest /variable/CTEST_UPDATE_COMMAND /variable/CTEST_UPDATE_OPTIONS /variable/CTEST_UPDATE_VERSION_ONLY + /variable/CTEST_UPDATE_VERSION_OVERRIDE /variable/CTEST_USE_LAUNCHERS Variables for CPack diff --git a/Help/manual/ctest.1.rst b/Help/manual/ctest.1.rst index 5a6b329..5773176 100644 --- a/Help/manual/ctest.1.rst +++ b/Help/manual/ctest.1.rst @@ -822,6 +822,8 @@ Configuration settings to specify the version control tool include: * :module:`CTest` module variable: ``UPDATE_TYPE`` if set, else ``CTEST_UPDATE_TYPE`` +.. _`UpdateVersionOnly`: + ``UpdateVersionOnly`` Specify that you want the version control update command to only discover the current version that is checked out, and not to update @@ -829,6 +831,18 @@ Configuration settings to specify the version control tool include: * `CTest Script`_ variable: :variable:`CTEST_UPDATE_VERSION_ONLY` +.. _`UpdateVersionOverride`: + +``UpdateVersionOverride`` + Specify the current version of your source tree. + + When this variable is set to a non-empty string, CTest will report the value + you specified rather than using the update command to discover the current + version that is checked out. Use of this variable supersedes + ``UpdateVersionOnly``. Like ``UpdateVersionOnly``, using this variable tells + CTest not to update the source tree to a different version. + + * `CTest Script`_ variable: :variable:`CTEST_UPDATE_VERSION_OVERRIDE` Additional configuration settings include: 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/ctest_update_version_override.rst b/Help/release/dev/ctest_update_version_override.rst new file mode 100644 index 0000000..03b47db --- /dev/null +++ b/Help/release/dev/ctest_update_version_override.rst @@ -0,0 +1,7 @@ +CTEST_UPDATE_VERSION_OVERRIDE +----------------------------- + +* The :command:`ctest_update` command learned to honor a new variable: + :variable:`CTEST_UPDATE_VERSION_OVERRIDE`. This can be used to specify + the current version of your source tree rather than using the update + command to discover the current version that is checked out. diff --git a/Help/variable/CTEST_UPDATE_VERSION_ONLY.rst b/Help/variable/CTEST_UPDATE_VERSION_ONLY.rst index e646e6e..a862baa 100644 --- a/Help/variable/CTEST_UPDATE_VERSION_ONLY.rst +++ b/Help/variable/CTEST_UPDATE_VERSION_ONLY.rst @@ -1,5 +1,5 @@ CTEST_UPDATE_VERSION_ONLY ------------------------- -Specify the CTest ``UpdateVersionOnly`` setting +Specify the CTest :ref:`UpdateVersionOnly <UpdateVersionOnly>` setting in a :manual:`ctest(1)` dashboard client script. diff --git a/Help/variable/CTEST_UPDATE_VERSION_OVERRIDE.rst b/Help/variable/CTEST_UPDATE_VERSION_OVERRIDE.rst new file mode 100644 index 0000000..39fbaba --- /dev/null +++ b/Help/variable/CTEST_UPDATE_VERSION_OVERRIDE.rst @@ -0,0 +1,5 @@ +CTEST_UPDATE_VERSION_OVERRIDE +----------------------------- + +Specify the CTest :ref:`UpdateVersionOverride <UpdateVersionOverride>` setting +in a :manual:`ctest(1)` dashboard client script. diff --git a/Modules/CTestCoverageCollectGCOV.cmake b/Modules/CTestCoverageCollectGCOV.cmake index 2258271..655827a 100644 --- a/Modules/CTestCoverageCollectGCOV.cmake +++ b/Modules/CTestCoverageCollectGCOV.cmake @@ -52,7 +52,7 @@ After generating this tar file, it can be sent to CDash for display with the ``GCOV_OPTIONS <options>...`` Specify options to be passed to gcov. The ``gcov`` command is run as ``gcov <options>... -o <gcov-dir> <file>.gcda``. - If not specified, the default option is just ``-b``. + If not specified, the default option is just ``-b -x``. ``GLOB`` Recursively search for .gcda files in build_dir rather than @@ -95,7 +95,7 @@ function(ctest_coverage_collect_gcov) set(gcda_files) set(label_files) if (GCOV_GLOB) - file(GLOB_RECURSE gfiles RELATIVE ${binary_dir} "${binary_dir}/*.gcda") + file(GLOB_RECURSE gfiles "${binary_dir}/*.gcda") list(LENGTH gfiles len) # if we have gcda files then also grab the labels file for that target if(${len} GREATER 0) @@ -109,7 +109,7 @@ function(ctest_coverage_collect_gcov) file(STRINGS "${binary_dir}/CMakeFiles/TargetDirectories.txt" target_dirs ENCODING UTF-8) foreach(target_dir ${target_dirs}) - file(GLOB_RECURSE gfiles RELATIVE ${binary_dir} "${target_dir}/*.gcda") + file(GLOB_RECURSE gfiles "${target_dir}/*.gcda") list(LENGTH gfiles len) # if we have gcda files then also grab the labels file for that target if(${len} GREATER 0) @@ -132,27 +132,21 @@ function(ctest_coverage_collect_gcov) # setup the dir for the coverage files set(coverage_dir "${binary_dir}/Testing/CoverageInfo") file(MAKE_DIRECTORY "${coverage_dir}") - # call gcov on each .gcda file - foreach (gcda_file ${gcda_files}) - # get the directory of the gcda file - get_filename_component(gcda_file ${binary_dir}/${gcda_file} ABSOLUTE) - get_filename_component(gcov_dir ${gcda_file} DIRECTORY) - # run gcov, this will produce the .gcov file in the current - # working directory - if(NOT DEFINED GCOV_GCOV_OPTIONS) - set(GCOV_GCOV_OPTIONS -b) - endif() - execute_process(COMMAND - ${gcov_command} ${GCOV_GCOV_OPTIONS} -o ${gcov_dir} ${gcda_file} - OUTPUT_VARIABLE out - RESULT_VARIABLE res - WORKING_DIRECTORY ${coverage_dir}) - - if (GCOV_DELETE) - file(REMOVE ${gcda_file}) - endif() + # run gcov, this will produce the .gcov files in the current + # working directory + if(NOT DEFINED GCOV_GCOV_OPTIONS) + set(GCOV_GCOV_OPTIONS -b -x) + endif() + execute_process(COMMAND + ${gcov_command} ${GCOV_GCOV_OPTIONS} ${gcda_files} + OUTPUT_VARIABLE out + RESULT_VARIABLE res + WORKING_DIRECTORY ${coverage_dir}) + + if (GCOV_DELETE) + file(REMOVE ${gcda_files}) + endif() - endforeach() if(NOT "${res}" EQUAL 0) if (NOT GCOV_QUIET) message(STATUS "Error running gcov: ${res} ${out}") diff --git a/Modules/FindBLAS.cmake b/Modules/FindBLAS.cmake index 77e689f..8d55ac7 100644 --- a/Modules/FindBLAS.cmake +++ b/Modules/FindBLAS.cmake @@ -239,7 +239,8 @@ if (BLA_VENDOR MATCHES "Intel" OR BLA_VENDOR STREQUAL "All") set(BLAS_mkl_DLL_SUFFIX "_dll") endif() else() - if(CMAKE_Fortran_COMPILER_LOADED AND CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + # Switch to GNU Fortran support layer if needed (but not on Apple, where MKL does not provide it) + if(CMAKE_Fortran_COMPILER_LOADED AND CMAKE_Fortran_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) set(BLAS_mkl_INTFACE "gf") set(BLAS_mkl_THREADING "gnu") set(BLAS_mkl_OMP "gomp") 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/FindMPI.cmake b/Modules/FindMPI.cmake index 5288640..fe09764 100644 --- a/Modules/FindMPI.cmake +++ b/Modules/FindMPI.cmake @@ -216,7 +216,7 @@ If the following variables are set to true, the respective search will be perfor and ``MPI_Fortran_<binding>_ASYNCPROT``, where ``<binding>`` is one of ``F77_HEADER``, ``F90_MODULE`` and ``F08_MODULE``. ``MPI_DETERMINE_LIBRARY_VERSION`` - For each language, find the output of ``MPI_Get_library_version`` and make it available as ``MPI_<lang>_LIBRARY_VERSION``. + For each language, find the output of ``MPI_Get_library_version`` and make it available as ``MPI_<lang>_LIBRARY_VERSION_STRING``. This information is usually tied to the runtime component of an MPI implementation and might differ depending on ``<lang>``. Note that the return value is entirely implementation defined. This information might be used to identify the MPI vendor and for example pick the correct one of multiple third party binaries that matches the MPI vendor. @@ -1155,7 +1155,7 @@ macro(_MPI_create_imported_target LANG) set_property(TARGET MPI::MPI_${LANG} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${MPI_${LANG}_INCLUDE_DIRS}") endmacro() -function(_MPI_try_staged_settings LANG MPI_TEST_FILE_NAME MODE RUN_BINARY) +function(_MPI_try_staged_settings LANG MPI_TEST_FILE_NAME MODE RUN_BINARY SUPPRESS_ERRORS) set(WORK_DIR "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindMPI") set(SRC_DIR "${CMAKE_ROOT}/Modules/FindMPI") set(BIN_FILE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/FindMPI/${MPI_TEST_FILE_NAME}_${LANG}.bin") @@ -1184,18 +1184,29 @@ function(_MPI_try_staged_settings LANG MPI_TEST_FILE_NAME MODE RUN_BINARY) "${CMAKE_BINARY_DIR}" SOURCES "${MPI_TEST_SOURCE_FILE}" COMPILE_DEFINITIONS ${MPI_TEST_COMPILE_DEFINITIONS} LINK_LIBRARIES MPI::MPI_${LANG} - RUN_OUTPUT_VARIABLE MPI_RUN_OUTPUT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE}) + RUN_OUTPUT_VARIABLE MPI_RUN_OUTPUT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE} + COMPILE_OUTPUT_VARIABLE _MPI_TRY_${MPI_TEST_FILE_NAME}_${MODE}_OUTPUT) set(MPI_RUN_OUTPUT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE} "${MPI_RUN_OUTPUT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE}}" PARENT_SCOPE) else() try_compile(MPI_RESULT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE} "${CMAKE_BINARY_DIR}" SOURCES "${MPI_TEST_SOURCE_FILE}" COMPILE_DEFINITIONS ${MPI_TEST_COMPILE_DEFINITIONS} LINK_LIBRARIES MPI::MPI_${LANG} - COPY_FILE "${BIN_FILE}") + COPY_FILE "${BIN_FILE}" + OUTPUT_VARIABLE _MPI_TRY_${MPI_TEST_FILE_NAME}_${MODE}_OUTPUT) + endif() + if(NOT SUPPRESS_ERRORS) + if(NOT MPI_RESULT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE}) + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "The MPI test ${MPI_TEST_FILE_NAME} for ${LANG} in mode ${MODE} failed to compile with the following output:\n${_MPI_TRY_${MPI_TEST_FILE_NAME}_${MODE}_OUTPUT}\n\n") + elseif(DEFINED MPI_RUN_RESULT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE} AND MPI_RUN_RESULT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE}) + file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log + "The MPI test ${MPI_TEST_FILE_NAME} for ${LANG} in mode ${MODE} failed to run with the following output:\n${MPI_RUN_OUTPUT_${LANG}_${MPI_TEST_FILE_NAME}_${MODE}}\n\n") + endif() endif() endfunction() -macro(_MPI_check_lang_works LANG) +macro(_MPI_check_lang_works LANG SUPPRESS_ERRORS) # For Fortran we may have by the MPI-3 standard an implementation that provides: # - the mpi_f08 module # - *both*, the mpi module and 'mpif.h' @@ -1203,9 +1214,9 @@ macro(_MPI_check_lang_works LANG) if( NOT MPI_${LANG}_WORKS ) if("${LANG}" STREQUAL "Fortran") set(MPI_Fortran_INTEGER_LINE "(kind=MPI_INTEGER_KIND)") - _MPI_try_staged_settings(${LANG} test_mpi F77_HEADER FALSE) - _MPI_try_staged_settings(${LANG} test_mpi F90_MODULE FALSE) - _MPI_try_staged_settings(${LANG} test_mpi F08_MODULE FALSE) + _MPI_try_staged_settings(${LANG} test_mpi F77_HEADER FALSE ${SUPPRESS_ERRORS}) + _MPI_try_staged_settings(${LANG} test_mpi F90_MODULE FALSE ${SUPPRESS_ERRORS}) + _MPI_try_staged_settings(${LANG} test_mpi F08_MODULE FALSE ${SUPPRESS_ERRORS}) set(MPI_${LANG}_WORKS FALSE) @@ -1221,14 +1232,14 @@ macro(_MPI_check_lang_works LANG) # However, MPI-1 also did not define the Fortran 90 and 08 modules, so we only try the F77 header. unset(MPI_Fortran_INTEGER_LINE) if(NOT MPI_${LANG}_WORKS) - _MPI_try_staged_settings(${LANG} test_mpi F77_HEADER_NOKIND FALSE) + _MPI_try_staged_settings(${LANG} test_mpi F77_HEADER_NOKIND FALSE ${SUPPRESS_ERRORS}) if(MPI_RESULT_${LANG}_test_mpi_F77_HEADER_NOKIND) set(MPI_${LANG}_WORKS TRUE) set(MPI_${LANG}_HAVE_F77_HEADER TRUE) endif() endif() else() - _MPI_try_staged_settings(${LANG} test_mpi normal FALSE) + _MPI_try_staged_settings(${LANG} test_mpi normal FALSE ${SUPPRESS_ERRORS}) # If 'test_mpi' built correctly, we've found valid MPI settings. There might not be MPI-2 C++ support, but there can't # be MPI-2 C++ support without the C bindings being present, so checking for them is sufficient. set(MPI_${LANG}_WORKS "${MPI_RESULT_${LANG}_test_mpi_normal}") @@ -1390,7 +1401,7 @@ foreach(LANG IN ITEMS C CXX Fortran) # Should the imported targets be empty, we effectively try whether the compiler supports MPI on its own, which is the case on e.g. # Cray PrgEnv. _MPI_create_imported_target(${LANG}) - _MPI_check_lang_works(${LANG}) + _MPI_check_lang_works(${LANG} TRUE) # If the compiler can build MPI code on its own, it functions as an MPI compiler and we'll set the variable to point to it. if(MPI_${LANG}_WORKS) @@ -1442,7 +1453,7 @@ foreach(LANG IN ITEMS C CXX Fortran) # If we haven't made the implicit compiler test yet, perform it now. if(NOT MPI_${LANG}_TRIED_IMPLICIT) _MPI_create_imported_target(${LANG}) - _MPI_check_lang_works(${LANG}) + _MPI_check_lang_works(${LANG} TRUE) endif() # Should the MPI compiler not work implicitly for MPI, still interrogate it. @@ -1488,7 +1499,7 @@ foreach(LANG IN ITEMS C CXX Fortran) _MPI_create_imported_target(${LANG}) if(NOT MPI_${LANG}_WORKS) - _MPI_check_lang_works(${LANG}) + _MPI_check_lang_works(${LANG} FALSE) endif() # Next, we'll initialize the MPI variables that have not been previously set. @@ -1507,7 +1518,7 @@ foreach(LANG IN ITEMS C CXX Fortran) if(MPI_${LANG}_WORKS) if("${LANG}" STREQUAL "CXX" AND NOT DEFINED MPI_MPICXX_FOUND) if(NOT MPI_CXX_SKIP_MPICXX AND NOT MPI_CXX_VALIDATE_SKIP_MPICXX) - _MPI_try_staged_settings(${LANG} test_mpi MPICXX FALSE) + _MPI_try_staged_settings(${LANG} test_mpi MPICXX FALSE FALSE) if(MPI_RESULT_${LANG}_test_mpi_MPICXX) set(MPI_MPICXX_FOUND TRUE) else() @@ -1542,7 +1553,7 @@ foreach(LANG IN ITEMS C CXX Fortran) # and MPI_SUBVERSION are provided. These defines did not exist in MPI 1.0 and 1.1 and therefore might not # exist. For C/C++, test_mpi.c will handle the MPI_VERSION extraction, but for Fortran, we need mpiver.f90. if(NOT DEFINED MPI_${LANG}_VERSION) - _MPI_try_staged_settings(${LANG} mpiver ${MPI_${LANG}_HIGHEST_METHOD} FALSE) + _MPI_try_staged_settings(${LANG} mpiver ${MPI_${LANG}_HIGHEST_METHOD} FALSE FALSE) if(MPI_RESULT_${LANG}_mpiver_${MPI_${LANG}_HIGHEST_METHOD}) file(STRINGS ${MPI_BIN_FOLDER}/mpiver_${LANG}.bin _MPI_VERSION_STRING LIMIT_COUNT 1 REGEX "INFO:MPI-VER") if("${_MPI_VERSION_STRING}" MATCHES ".*INFO:MPI-VER\\[([0-9]+)\\.([0-9]+)\\].*") @@ -1561,7 +1572,7 @@ foreach(LANG IN ITEMS C CXX Fortran) if(MPI_${LANG}_HAVE_${mpimethod}) set(MPI_${LANG}_${mpimethod}_SUBARRAYS FALSE) set(MPI_${LANG}_${mpimethod}_ASYNCPROT FALSE) - _MPI_try_staged_settings(${LANG} fortranparam_mpi ${mpimethod} TRUE) + _MPI_try_staged_settings(${LANG} fortranparam_mpi ${mpimethod} TRUE FALSE) if(MPI_RESULT_${LANG}_fortranparam_mpi_${mpimethod} AND NOT "${MPI_RUN_RESULT_${LANG}_fortranparam_mpi_${mpimethod}}" STREQUAL "FAILED_TO_RUN") if("${MPI_RUN_OUTPUT_${LANG}_fortranparam_mpi_${mpimethod}}" MATCHES @@ -1602,7 +1613,7 @@ foreach(LANG IN ITEMS C CXX Fortran) # It's also worth noting that the installed version string can depend on the language, or on the system the binary # runs on if MPI is not statically linked. if(MPI_DETERMINE_LIBRARY_VERSION AND NOT MPI_${LANG}_LIBRARY_VERSION_STRING) - _MPI_try_staged_settings(${LANG} libver_mpi ${MPI_${LANG}_HIGHEST_METHOD} TRUE) + _MPI_try_staged_settings(${LANG} libver_mpi ${MPI_${LANG}_HIGHEST_METHOD} TRUE FALSE) if(MPI_RESULT_${LANG}_libver_mpi_${MPI_${LANG}_HIGHEST_METHOD} AND "${MPI_RUN_RESULT_${LANG}_libver_mpi_${MPI_${LANG}_HIGHEST_METHOD}}" EQUAL "0") string(STRIP "${MPI_RUN_OUTPUT_${LANG}_libver_mpi_${MPI_${LANG}_HIGHEST_METHOD}}" diff --git a/Modules/FindMPI/libver_mpi.c b/Modules/FindMPI/libver_mpi.c index 18d4a60..d89328a 100644 --- a/Modules/FindMPI/libver_mpi.c +++ b/Modules/FindMPI/libver_mpi.c @@ -16,4 +16,5 @@ int main(int argc, char* argv[]) #else puts(mpilibver_str); #endif + return 0; } diff --git a/Modules/FindMPI/test_mpi.c b/Modules/FindMPI/test_mpi.c index 05cec89..7c96d54 100644 --- a/Modules/FindMPI/test_mpi.c +++ b/Modules/FindMPI/test_mpi.c @@ -34,4 +34,5 @@ int main(int argc, char* argv[]) MPI_Init(&argc, &argv); MPI_Finalize(); #endif + return 0; } diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 4e8066a..31a220f 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 20190507) +set(CMake_VERSION_PATCH 20190510) #set(CMake_VERSION_RC 1) diff --git a/Source/CTest/cmCTestGlobalVC.cxx b/Source/CTest/cmCTestGlobalVC.cxx index a2d4d2c..54ebd4f 100644 --- a/Source/CTest/cmCTestGlobalVC.cxx +++ b/Source/CTest/cmCTestGlobalVC.cxx @@ -117,3 +117,8 @@ bool cmCTestGlobalVC::WriteXMLUpdates(cmXMLWriter& xml) return result; } + +void cmCTestGlobalVC::SetNewRevision(std::string const& revision) +{ + this->NewRevision = revision; +} diff --git a/Source/CTest/cmCTestGlobalVC.h b/Source/CTest/cmCTestGlobalVC.h index 76377ed..9c57215 100644 --- a/Source/CTest/cmCTestGlobalVC.h +++ b/Source/CTest/cmCTestGlobalVC.h @@ -32,6 +32,8 @@ protected: // Implement cmCTestVC internal API. bool WriteXMLUpdates(cmXMLWriter& xml) override; + void SetNewRevision(std::string const& revision) override; + /** Represent a vcs-reported action for one path in a revision. */ struct Change { diff --git a/Source/CTest/cmCTestUpdateCommand.cxx b/Source/CTest/cmCTestUpdateCommand.cxx index a2f1462..65dc921 100644 --- a/Source/CTest/cmCTestUpdateCommand.cxx +++ b/Source/CTest/cmCTestUpdateCommand.cxx @@ -62,6 +62,9 @@ cmCTestGenericHandler* cmCTestUpdateCommand::InitializeHandler() this->Makefile, "UpdateVersionOnly", "CTEST_UPDATE_VERSION_ONLY", this->Quiet); this->CTest->SetCTestConfigurationFromCMakeVariable( + this->Makefile, "UpdateVersionOverride", "CTEST_UPDATE_VERSION_OVERRIDE", + this->Quiet); + this->CTest->SetCTestConfigurationFromCMakeVariable( this->Makefile, "HGCommand", "CTEST_HG_COMMAND", this->Quiet); this->CTest->SetCTestConfigurationFromCMakeVariable( this->Makefile, "HGUpdateOptions", "CTEST_HG_UPDATE_OPTIONS", this->Quiet); diff --git a/Source/CTest/cmCTestVC.cxx b/Source/CTest/cmCTestVC.cxx index 374e73f..eea41cf 100644 --- a/Source/CTest/cmCTestVC.cxx +++ b/Source/CTest/cmCTestVC.cxx @@ -141,6 +141,15 @@ void cmCTestVC::CleanupImpl() bool cmCTestVC::Update() { bool result = true; + + // Use the explicitly specified version. + std::string versionOverride = + this->CTest->GetCTestConfiguration("UpdateVersionOverride"); + if (!versionOverride.empty()) { + this->SetNewRevision(versionOverride); + return true; + } + // if update version only is on then do not actually update, // just note the current version and finish if (!cmSystemTools::IsOn( @@ -166,6 +175,11 @@ bool cmCTestVC::NoteNewRevision() return true; } +void cmCTestVC::SetNewRevision(std::string const& /*unused*/) +{ + // We do nothing by default. +} + bool cmCTestVC::UpdateImpl() { cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, diff --git a/Source/CTest/cmCTestVC.h b/Source/CTest/cmCTestVC.h index 69a3bf0..2a4765d 100644 --- a/Source/CTest/cmCTestVC.h +++ b/Source/CTest/cmCTestVC.h @@ -70,6 +70,7 @@ protected: virtual bool NoteOldRevision(); virtual bool UpdateImpl(); virtual bool NoteNewRevision(); + virtual void SetNewRevision(std::string const& revision); virtual bool WriteXMLUpdates(cmXMLWriter& xml); #if defined(__SUNPRO_CC) && __SUNPRO_CC <= 0x510 diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 3832427..6c390f7 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -1599,6 +1599,16 @@ void cmMakefile::Configure() } // if no project command is found, add one if (!hasProject) { + this->GetCMakeInstance()->IssueMessage( + MessageType::AUTHOR_WARNING, + "No project() command is present. The top-level CMakeLists.txt " + "file must contain a literal, direct call to the project() command. " + "Add a line of code such as\n" + " project(ProjectName)\n" + "near the top of the file, but after cmake_minimum_required().\n" + "CMake is pretending there is a \"project(Project)\" command on " + "the first line.", + this->Backtrace); cmListFileFunction project; project.Name.Lower = "project"; project.Arguments.emplace_back("Project", cmListFileArgument::Unquoted, diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index af47fa7..ebb7c15 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -535,6 +535,20 @@ std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd() std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar); cmSystemTools::ExpandListArgument(linkCmd, linkCmds); } +#ifdef __APPLE__ + // On macOS ranlib truncates the fractional part of the static archive + // file modification time. If the archive and at least one contained + // object file were created within the same second this will make look + // the archive older than the object file. On subsequent ninja runs this + // leads to re-achiving and updating dependent targets. + // As a work-around we touch the archive after ranlib (see #19222). + { + std::string cmakeCommand = + this->GetLocalGenerator()->ConvertToOutputFormat( + cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); + linkCmds.push_back(cmakeCommand + " -E touch $TARGET_FILE"); + } +#endif return linkCmds; } case cmStateEnums::SHARED_LIBRARY: 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..f7e377d 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,12 @@ 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); + } 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..bb40c39 100644 --- a/Source/cmQtAutoRcc.cxx +++ b/Source/cmQtAutoRcc.cxx @@ -57,7 +57,7 @@ bool cmQtAutoRcc::Init(cmMakefile* makefile) } // - Configurations - Log().RaiseVerbosity(InfoGet("ARCC_VERBOSITY")); + Logger_.RaiseVerbosity(InfoGet("ARCC_VERBOSITY")); MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG"); // - Directories diff --git a/Source/cmQtAutoRcc.h b/Source/cmQtAutoRcc.h index 8dc9179..01c3fb9 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; diff --git a/Tests/CTestCoverageCollectGCOV/fakegcov.cmake b/Tests/CTestCoverageCollectGCOV/fakegcov.cmake index b0c3a9b..6df4292 100644 --- a/Tests/CTestCoverageCollectGCOV/fakegcov.cmake +++ b/Tests/CTestCoverageCollectGCOV/fakegcov.cmake @@ -1,14 +1,17 @@ +function(create_gcov_file gcda_full_path) + get_filename_component(gcda_name ${gcda_full_path} NAME) + string(REPLACE ".gcda" ".gcov" gcov_name "${gcda_name}") + + file(STRINGS "${gcda_full_path}" source_file LIMIT_COUNT 1 ENCODING UTF-8) + + file(WRITE "${CMAKE_SOURCE_DIR}/${gcov_name}" + " -: 0:Source:${source_file}" + ) +endfunction() + foreach(I RANGE 0 ${CMAKE_ARGC}) if("${CMAKE_ARGV${I}}" MATCHES ".*\\.gcda") set(gcda_file "${CMAKE_ARGV${I}}") + create_gcov_file(${gcda_file}) endif() endforeach() - -get_filename_component(gcda_name ${gcda_file} NAME) -string(REPLACE ".gcda" ".gcov" gcov_name "${gcda_name}") - -file(STRINGS "${gcda_file}" source_file LIMIT_COUNT 1 ENCODING UTF-8) - -file(WRITE "${CMAKE_SOURCE_DIR}/${gcov_name}" - " -: 0:Source:${source_file}" -) 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/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/ctest_update/RunCMakeTest.cmake b/Tests/RunCMake/ctest_update/RunCMakeTest.cmake index 0e1748f..f8d7665 100644 --- a/Tests/RunCMake/ctest_update/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_update/RunCMakeTest.cmake @@ -5,13 +5,21 @@ function(run_ctest_update CASE_NAME) run_ctest(${CASE_NAME}) endfunction() -run_ctest_update(TestQuiet QUIET) +run_ctest_update(UpdateQuiet QUIET) -function(run_TestChangeId) +function(run_UpdateChangeId) set(CASE_TEST_PREFIX_CODE [[ set(CTEST_CHANGE_ID "<>1") ]]) - run_ctest(TestChangeId) + run_ctest(UpdateChangeId) endfunction() -run_TestChangeId() +run_UpdateChangeId() + +function(run_UpdateVersionOverride) + set(CASE_TEST_PREFIX_CODE [[ + set(CTEST_UPDATE_VERSION_OVERRIDE "qwertyuiop") + ]]) + run_ctest(UpdateVersionOverride) +endfunction() +run_UpdateVersionOverride() diff --git a/Tests/RunCMake/ctest_update/UpdateActualVersion-check.cmake b/Tests/RunCMake/ctest_update/UpdateActualVersion-check.cmake new file mode 100644 index 0000000..12bd5a0 --- /dev/null +++ b/Tests/RunCMake/ctest_update/UpdateActualVersion-check.cmake @@ -0,0 +1,12 @@ +file(GLOB update_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Update.xml") +if(update_xml_file) + file(READ "${update_xml_file}" update_xml LIMIT 4096) + if(NOT update_xml MATCHES "qwertyuiop") + string(REPLACE "\n" "\n " update_xml " ${update_xml}") + set(RunCMake_TEST_FAILED + "Did not find 'qwertyuiop' in Update.xml:\n${update_xml}" + ) + endif() +else() + set(RunCMake_TEST_FAILED "Update.xml not found") +endif() diff --git a/Tests/RunCMake/ctest_update/UpdateChangeId-check.cmake b/Tests/RunCMake/ctest_update/UpdateChangeId-check.cmake index 9269fbc..382fbec 100644 --- a/Tests/RunCMake/ctest_update/UpdateChangeId-check.cmake +++ b/Tests/RunCMake/ctest_update/UpdateChangeId-check.cmake @@ -1,7 +1,7 @@ file(GLOB update_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Update.xml") if(update_xml_file) file(READ "${update_xml_file}" update_xml LIMIT 4096) - if(NOT update_xml MATCHES [[ChangeId="<>1"]]) + if(NOT update_xml MATCHES [[ChangeId><>1]]) string(REPLACE "\n" "\n " update_xml " ${update_xml}") set(RunCMake_TEST_FAILED "Update.xml does not have expected ChangeId:\n${update_xml}" diff --git a/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt b/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt new file mode 100644 index 0000000..fc1a02b --- /dev/null +++ b/Tests/RunCMake/project_injected/CMP0048-WARN-stderr.txt @@ -0,0 +1,12 @@ +^CMake Warning \(dev\) in CMakeLists.txt: + No project\(\) command is present. The top-level CMakeLists.txt file must + contain a literal, direct call to the project\(\) command. Add a line of + code such as + + project\(ProjectName\) + + near the top of the file, but after cmake_minimum_required\(\). + + CMake is pretending there is a "project\(Project\)" command on the first + line. +This warning is for project developers. Use -Wno-dev to suppress it.$ |