diff options
119 files changed, 3467 insertions, 1881 deletions
diff --git a/Help/command/string.rst b/Help/command/string.rst index 893fb43..2e89d7b 100644 --- a/Help/command/string.rst +++ b/Help/command/string.rst @@ -28,6 +28,7 @@ Synopsis string(`SUBSTRING`_ <string> <begin> <length> <out-var>) string(`STRIP`_ <string> <out-var>) string(`GENEX_STRIP`_ <string> <out-var>) + string(`REPEAT`_ <string> <count> <out-var>) `Comparison`_ string(`COMPARE`_ <op> <string1> <string2> <out-var>) @@ -269,6 +270,14 @@ trailing spaces removed. Strip any :manual:`generator expressions <cmake-generator-expressions(7)>` from the ``input string`` and store the result in the ``output variable``. +.. _REPEAT: + +.. code-block:: cmake + + string(REPEAT <input string> <count> <output variable>) + +Produce the output string as repetion of ``input string`` ``count`` times. + Comparison ^^^^^^^^^^ diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index e9b3f4c..7f4761f 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -387,14 +387,25 @@ Target-Dependent Queries ``$<TARGET_NAME_IF_EXISTS:tgt>`` Expands to the ``tgt`` if the given target exists, an empty string otherwise. -``$<TARGET_OUTPUT_NAME:tgt>`` +``$<TARGET_FILE:tgt>`` + Full path to main file (.exe, .so.1.2, .a) where ``tgt`` is the name of a + target. +``$<TARGET_FILE_BASE_NAME:tgt>`` Base name of main file where ``tgt`` is the name of a target. + The base name corresponds to the target file name (see + ``$<TARGET_FILE_NAME:tgt>``) without prefix and suffix. For example, if + target file name is ``libbase.so``, the base name is ``base``. + + See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`, + :prop_tgt:`LIBRARY_OUTPUT_NAME` and :prop_tgt:`RUNTIME_OUTPUT_NAME` + target properties and their configuration specific variants + :prop_tgt:`OUTPUT_NAME_<CONFIG>`, :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>`, + :prop_tgt:`LIBRARY_OUTPUT_NAME_<CONFIG>` and + :prop_tgt:`RUNTIME_OUTPUT_NAME_<CONFIG>`. + Note that ``tgt`` is not added as a dependency of the target this expression is evaluated on. -``$<TARGET_FILE:tgt>`` - Full path to main file (.exe, .so.1.2, .a) where ``tgt`` is the name of a - target. ``$<TARGET_FILE_PREFIX:tgt>`` Prefix of main file where ``tgt`` is the name of a target. @@ -409,13 +420,23 @@ Target-Dependent Queries Name of main file (.exe, .so.1.2, .a). ``$<TARGET_FILE_DIR:tgt>`` Directory of main file (.exe, .so.1.2, .a). -``$<TARGET_LINKER_OUTPUT_NAME:tgt>`` +``$<TARGET_LINKER_FILE:tgt>`` + File used to link (.a, .lib, .so) where ``tgt`` is the name of a target. +``$<TARGET_LINKER_FILE_BASE_NAME:tgt>`` Base name of file used to link where ``tgt`` is the name of a target. + The base name corresponds to the target linker file name (see + ``$<TARGET_LINKER_FILE_NAME:tgt>``) without prefix and suffix. For example, + if target file name is ``libbase.a``, the base name is ``base``. + + See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`, + and :prop_tgt:`LIBRARY_OUTPUT_NAME` target properties and their configuration + specific variants :prop_tgt:`OUTPUT_NAME_<CONFIG>`, + :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>` and + :prop_tgt:`LIBRARY_OUTPUT_NAME_<CONFIG>`. + Note that ``tgt`` is not added as a dependency of the target this expression is evaluated on. -``$<TARGET_LINKER_FILE:tgt>`` - File used to link (.a, .lib, .so) where ``tgt`` is the name of a target. ``$<TARGET_LINKER_FILE_PREFIX:tgt>`` Prefix of file used to link where ``tgt`` is the name of a target. @@ -436,22 +457,26 @@ Target-Dependent Queries Name of file with soname (.so.3). ``$<TARGET_SONAME_FILE_DIR:tgt>`` Directory of with soname (.so.3). -``$<TARGET_PDB_OUTPUT_NAME:tgt>`` +``$<TARGET_PDB_FILE:tgt>`` + Full path to the linker generated program database file (.pdb) + where ``tgt`` is the name of a target. + + See also the :prop_tgt:`PDB_NAME` and :prop_tgt:`PDB_OUTPUT_DIRECTORY` + target properties and their configuration specific variants + :prop_tgt:`PDB_NAME_<CONFIG>` and :prop_tgt:`PDB_OUTPUT_DIRECTORY_<CONFIG>`. +``$<TARGET_PDB_FILE_BASE_NAME:tgt>`` Base name of the linker generated program database file (.pdb) where ``tgt`` is the name of a target. + The base name corresponds to the target PDB file name (see + ``$<TARGET_PDB_FILE_NAME:tgt>``) without prefix and suffix. For example, + if target file name is ``base.pdb``, the base name is ``base``. + See also the :prop_tgt:`PDB_NAME` target property and its configuration specific variant :prop_tgt:`PDB_NAME_<CONFIG>`. Note that ``tgt`` is not added as a dependency of the target this expression is evaluated on. -``$<TARGET_PDB_FILE:tgt>`` - Full path to the linker generated program database file (.pdb) - where ``tgt`` is the name of a target. - - See also the :prop_tgt:`PDB_NAME` and :prop_tgt:`PDB_OUTPUT_DIRECTORY` - target properties and their configuration specific variants - :prop_tgt:`PDB_NAME_<CONFIG>` and :prop_tgt:`PDB_OUTPUT_DIRECTORY_<CONFIG>`. ``$<TARGET_PDB_FILE_NAME:tgt>`` Name of the linker generated program database file (.pdb). ``$<TARGET_PDB_FILE_DIR:tgt>`` diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 4d4b9ff..bd19ccf 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -335,6 +335,7 @@ Properties on Targets /prop_tgt/VS_KEYWORD /prop_tgt/VS_MOBILE_EXTENSIONS_VERSION /prop_tgt/VS_NO_SOLUTION_DEPLOY + /prop_tgt/VS_PROJECT_IMPORT /prop_tgt/VS_SCC_AUXPATH /prop_tgt/VS_SCC_LOCALPATH /prop_tgt/VS_SCC_PROJECTNAME diff --git a/Help/prop_tgt/VS_PROJECT_IMPORT.rst b/Help/prop_tgt/VS_PROJECT_IMPORT.rst new file mode 100644 index 0000000..569c8ea --- /dev/null +++ b/Help/prop_tgt/VS_PROJECT_IMPORT.rst @@ -0,0 +1,8 @@ +VS_PROJECT_IMPORT +----------------- + +Visual Studio managed project imports + +Adds to a generated Visual Studio project one or more semicolon-delimited paths +to .props files needed when building projects from some NuGet packages. +For example, ``my_packages_path/MyPackage.1.0.0/build/MyPackage.props``. diff --git a/Help/release/dev/genex-TARGET_FILE_BASE_NAME.rst b/Help/release/dev/genex-TARGET_FILE_BASE_NAME.rst new file mode 100644 index 0000000..d8b2b21 --- /dev/null +++ b/Help/release/dev/genex-TARGET_FILE_BASE_NAME.rst @@ -0,0 +1,7 @@ +genex-TARGET_FILE_BASE_NAME +--------------------------- + +* New ``$<TARGET_FILE_BASE_NAME:...>``, ``$<TARGET_LINKER_FILE_BASE_NAME:...>`` + and ``$<TARGET_PDB_FILE_BASE_NAME:...>`` + :manual:`generator expressions <cmake-generator-expressions(7)>` have been + added to retrieve the base name of various artifacts. diff --git a/Help/release/dev/genex-TARGET_OUTPUT_NAME.rst b/Help/release/dev/genex-TARGET_OUTPUT_NAME.rst deleted file mode 100644 index e3ffe57..0000000 --- a/Help/release/dev/genex-TARGET_OUTPUT_NAME.rst +++ /dev/null @@ -1,7 +0,0 @@ -genex-TARGET_OUTPUT_NAME ------------------------- - -* New ``$<TARGET_OUTPUT_NAME:...>``, ``$<TARGET_LINKER_OUTPUT_NAME:...>`` and - ``$<TARGET_PDB_OUTPUT_NAME:...>`` - :manual:`generator expressions <cmake-generator-expressions(7)>` have been - added to retrieve the base name of various artifacts. diff --git a/Help/release/dev/ghs-custom-commands.rst b/Help/release/dev/ghs-custom-commands.rst new file mode 100644 index 0000000..a29ef88 --- /dev/null +++ b/Help/release/dev/ghs-custom-commands.rst @@ -0,0 +1,5 @@ +ghs_custom_commands +------------------- + +* The :generator:`Green Hills MULTI` generator now supports + :command:`add_custom_command` and :command:`add_custom_target` diff --git a/Help/release/dev/string-repeat.rst b/Help/release/dev/string-repeat.rst new file mode 100644 index 0000000..4be0d5c --- /dev/null +++ b/Help/release/dev/string-repeat.rst @@ -0,0 +1,4 @@ +string-repeat +-------------- + +* The :command:`string` learned a new sub-command ``REPEAT``. diff --git a/Help/release/dev/vs-project-import.rst b/Help/release/dev/vs-project-import.rst new file mode 100644 index 0000000..de6024d --- /dev/null +++ b/Help/release/dev/vs-project-import.rst @@ -0,0 +1,5 @@ +vs-project-import +----------------- + +* The :prop_tgt:`VS_PROJECT_IMPORT` target property was added which allows + to import external .props files in managed Visual Studio targets. diff --git a/Modules/CMakeDetermineASMCompiler.cmake b/Modules/CMakeDetermineASMCompiler.cmake index b8c8c5d..eabb8b5 100644 --- a/Modules/CMakeDetermineASMCompiler.cmake +++ b/Modules/CMakeDetermineASMCompiler.cmake @@ -119,35 +119,40 @@ if(NOT CMAKE_ASM${ASM_DIALECT}_COMPILER_ID) CMAKE_DETERMINE_COMPILER_ID_VENDOR(ASM${ASM_DIALECT} "${userflags}") if("x${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}" STREQUAL "xIAR") # primary necessary to detect architecture, so the right archiver and linker can be picked - # eg. IAR Assembler V8.10.1.12857/W32 for ARM + # eg. "IAR Assembler V8.10.1.12857/W32 for ARM" or "IAR Assembler V4.11.1.4666 for Renesas RX" # Cut out identification first, newline handling is a pain string(REGEX MATCH "IAR Assembler[^\r\n]*" _compileid "${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_OUTPUT}") if("${_compileid}" MATCHES "V([0-9]+\\.[0-9]+\\.[0-9]+)") set(CMAKE_ASM${ASM_DIALECT}_COMPILER_VERSION ${CMAKE_MATCH_1}) endif() - if("${_compileid}" MATCHES "for[ ]+([A-Za-z0-9]+)") - set(CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID ${CMAKE_MATCH_1}) + string(REGEX MATCHALL "([A-Za-z0-9]+)" _all_compileid_matches "${_compileid}") + if(_all_compileid_matches) + list(GET _all_compileid_matches "-1" CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID) endif() endif() unset(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID_OUTPUT) + unset(_all_compileid_matches) unset(_compileid) endif() - if(CMAKE_ASM${ASM_DIALECT}_COMPILER_ID) if(CMAKE_ASM${ASM_DIALECT}_COMPILER_VERSION) set(_version " ${CMAKE_ASM${ASM_DIALECT}_COMPILER_VERSION}") else() set(_version "") endif() - message(STATUS "The ASM${ASM_DIALECT} compiler identification is ${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}${_version}") + if(CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID) + set(_archid " ${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}") + else() + set(_archid "") + endif() + message(STATUS "The ASM${ASM_DIALECT} compiler identification is ${CMAKE_ASM${ASM_DIALECT}_COMPILER_ID}${_archid}${_version}") + unset(_archid) unset(_version) else() message(STATUS "The ASM${ASM_DIALECT} compiler identification is unknown") endif() - - # If we have a gas/as cross compiler, they have usually some prefix, like # e.g. powerpc-linux-gas, arm-elf-gas or i586-mingw32msvc-gas , optionally # with a 3-component version number at the end diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake index 594f85b..c1c9982 100644 --- a/Modules/CMakeDetermineCompilerId.cmake +++ b/Modules/CMakeDetermineCompilerId.cmake @@ -110,8 +110,15 @@ function(CMAKE_DETERMINE_COMPILER_ID lang flagvar src) else() set(_version "") endif() + if(CMAKE_${lang}_COMPILER_ARCHITECTURE_ID) + set(_archid " ${CMAKE_${lang}_COMPILER_ARCHITECTURE_ID}") + else() + set(_archid "") + endif() message(STATUS "The ${lang} compiler identification is " - "${CMAKE_${lang}_COMPILER_ID}${_version}") + "${CMAKE_${lang}_COMPILER_ID}${_archid}${_version}") + unset(_archid) + unset(_version) else() message(STATUS "The ${lang} compiler identification is unknown") endif() diff --git a/Modules/CMakePlatformId.h.in b/Modules/CMakePlatformId.h.in index 3cb7f24..c88094a 100644 --- a/Modules/CMakePlatformId.h.in +++ b/Modules/CMakePlatformId.h.in @@ -156,6 +156,9 @@ # if defined(__ICCARM__) # define ARCHITECTURE_ID "ARM" +# elif defined(__ICCRX__) +# define ARCHITECTURE_ID "RX" + # elif defined(__ICCAVR__) # define ARCHITECTURE_ID "AVR" diff --git a/Modules/Compiler/IAR-ASM.cmake b/Modules/Compiler/IAR-ASM.cmake index e12bfd1..f9c0ced 100644 --- a/Modules/Compiler/IAR-ASM.cmake +++ b/Modules/Compiler/IAR-ASM.cmake @@ -3,21 +3,20 @@ include(Compiler/IAR) if("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM") -set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>") - __compiler_iar_ARM(ASM) + set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>") + __compiler_iar_ilink(ASM) set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa) - string(APPEND CMAKE_ASM_FLAGS_INIT " ") - string(APPEND CMAKE_ASM_FLAGS_DEBUG_INIT " -r") - string(APPEND CMAKE_ASM_FLAGS_MINSIZEREL_INIT " -DNDEBUG") - string(APPEND CMAKE_ASM_FLAGS_RELEASE_INIT " -DNDEBUG") - string(APPEND CMAKE_ASM_FLAGS_RELWITHDEBINFO_INIT " -r -DNDEBUG") +elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX") + set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>") + __compiler_iar_ilink(ASM) + set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s;asm;msa) elseif("${CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") set(CMAKE_ASM_COMPILE_OBJECT "<CMAKE_ASM_COMPILER> -S <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>") - __compiler_iar_AVR(ASM) + __compiler_iar_xlink(ASM) set(CMAKE_ASM_SOURCE_FILE_EXTENSIONS s90;asm;msa) else() - message(FATAL_ERROR "CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID not detected as \"AVR\" or \"ARM\". This should be automatic.") + message(FATAL_ERROR "CMAKE_ASM${ASM_DIALECT}_COMPILER_ARCHITECTURE_ID not detected. This should be automatic.") endif() diff --git a/Modules/Compiler/IAR-C.cmake b/Modules/Compiler/IAR-C.cmake index b5e61f0..cb10020 100644 --- a/Modules/Compiler/IAR-C.cmake +++ b/Modules/Compiler/IAR-C.cmake @@ -3,44 +3,39 @@ include(Compiler/IAR) include(Compiler/CMakeCommonCompilerMacros) -# The toolchains for ARM and AVR are quite different: -if("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM") - if(NOT CMAKE_C_COMPILER_VERSION) - message(FATAL_ERROR "CMAKE_C_COMPILER_VERSION not detected. This should be automatic.") - endif() +# Common +if(NOT CMAKE_C_COMPILER_VERSION) + message(FATAL_ERROR "CMAKE_C_COMPILER_VERSION not detected. This should be automatic.") +endif() - set(CMAKE_C_EXTENSION_COMPILE_OPTION -e) +set(CMAKE_C_EXTENSION_COMPILE_OPTION -e) +if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 7) + set(CMAKE_C90_STANDARD_COMPILE_OPTION --c89) + set(CMAKE_C90_EXTENSION_COMPILE_OPTION --c89 -e) + set(CMAKE_C99_STANDARD_COMPILE_OPTION "") + set(CMAKE_C99_EXTENSION_COMPILE_OPTION -e) +elseif() set(CMAKE_C90_STANDARD_COMPILE_OPTION "") set(CMAKE_C90_EXTENSION_COMPILE_OPTION -e) +endif() - if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.10) - set(CMAKE_C90_STANDARD_COMPILE_OPTION --c89) - set(CMAKE_C90_EXTENSION_COMPILE_OPTION --c89 -e) - set(CMAKE_C99_STANDARD_COMPILE_OPTION "") - set(CMAKE_C99_EXTENSION_COMPILE_OPTION -e) - endif() - if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 8.10) - set(CMAKE_C11_STANDARD_COMPILE_OPTION "") - set(CMAKE_C11_EXTENSION_COMPILE_OPTION -e) - endif() +if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 8) + set(CMAKE_C11_STANDARD_COMPILE_OPTION "") + set(CMAKE_C11_EXTENSION_COMPILE_OPTION -e) +endif() - __compiler_iar_ARM(C) +# Architecture specific +if("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM") + __compiler_iar_ilink(C) __compiler_check_default_language_standard(C 1.10 90 6.10 99 8.10 11) -elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") - if(NOT CMAKE_C_COMPILER_VERSION) - message(FATAL_ERROR "CMAKE_C_COMPILER_VERSION not detected. This should be automatic.") - endif() - - set(CMAKE_C_EXTENSION_COMPILE_OPTION -e) - - set(CMAKE_C90_STANDARD_COMPILE_OPTION --c89) - set(CMAKE_C90_EXTENSION_COMPILE_OPTION -e) - set(CMAKE_C99_STANDARD_COMPILE_OPTION "") - set(CMAKE_C99_EXTENSION_COMPILE_OPTION -e) +elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX") + __compiler_iar_ilink(C) + __compiler_check_default_language_standard(C 1.10 90 2.10 99 4.10 11) - __compiler_iar_AVR(C) +elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") + __compiler_iar_xlink(C) __compiler_check_default_language_standard(C 7.10 99) set(CMAKE_C_OUTPUT_EXTENSION ".r90") @@ -48,9 +43,6 @@ elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") set(CMAKE_C_LINK_FLAGS "-Fmotorola") endif() - set(CMAKE_C_LINK_EXECUTABLE "<CMAKE_LINKER> <OBJECTS> <CMAKE_C_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>") - set(CMAKE_C_CREATE_STATIC_LIBRARY "<CMAKE_AR> -o <TARGET> <OBJECTS> ") - # add the target specific include directory: get_filename_component(_compilerDir "${CMAKE_C_COMPILER}" PATH) get_filename_component(_compilerDir "${_compilerDir}" PATH) @@ -58,5 +50,5 @@ elseif("${CMAKE_C_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") include_directories("${_compilerDir}/inc/Atmel" ) else() - message(FATAL_ERROR "CMAKE_C_COMPILER_ARCHITECTURE_ID not detected as \"AVR\" or \"ARM\". This should be automatic.") + message(FATAL_ERROR "CMAKE_C_COMPILER_ARCHITECTURE_ID not detected. This should be automatic.") endif() diff --git a/Modules/Compiler/IAR-CXX.cmake b/Modules/Compiler/IAR-CXX.cmake index b7076f5..eb27e3c 100644 --- a/Modules/Compiler/IAR-CXX.cmake +++ b/Modules/Compiler/IAR-CXX.cmake @@ -3,60 +3,45 @@ include(Compiler/IAR) include(Compiler/CMakeCommonCompilerMacros) -if("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM") - # "(extended) embedded C++" Mode - # old version: --ec++ or --eec++ - # since 8.10: --c++ --no_exceptions --no_rtti - # - # --c++ is full C++ and supported since 6.10 - if(NOT CMAKE_IAR_CXX_FLAG) - if(NOT CMAKE_CXX_COMPILER_VERSION) - message(FATAL_ERROR "CMAKE_CXX_COMPILER_VERSION not detected. This should be automatic.") - endif() - if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.10) - set(CMAKE_IAR_CXX_FLAG --c++) - else() - set(CMAKE_IAR_CXX_FLAG --eec++) - endif() +# Common +if(NOT CMAKE_IAR_CXX_FLAG) + if(NOT CMAKE_CXX_COMPILER_VERSION) + message(FATAL_ERROR "CMAKE_CXX_COMPILER_VERSION not detected. This should be automatic.") + endif() + if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 8) + set(CMAKE_IAR_CXX_FLAG --c++) + else() + set(CMAKE_IAR_CXX_FLAG --eec++) endif() +endif() - set(CMAKE_CXX_EXTENSION_COMPILE_OPTION -e) +set(CMAKE_CXX_EXTENSION_COMPILE_OPTION -e) +if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 7) set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "") set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION -e) - - if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.10) set(CMAKE_CXX03_STANDARD_COMPILE_OPTION "") set(CMAKE_CXX03_EXTENSION_COMPILE_OPTION -e) +endif() + +if(CMAKE_CXX_COMPILER_VERSION_INTERNAL VERSION_GREATER 8) set(CMAKE_CXX11_STANDARD_COMPILE_OPTION "") set(CMAKE_CXX11_EXTENSION_COMPILE_OPTION -e) set(CMAKE_CXX14_STANDARD_COMPILE_OPTION "") set(CMAKE_CXX14_EXTENSION_COMPILE_OPTION -e) - endif() +endif() - __compiler_iar_ARM(CXX) +# Architecture specific +if("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM") + __compiler_iar_ilink(CXX) __compiler_check_default_language_standard(CXX 6.10 98 8.10 14) -elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") - # "embedded C++" --EC++ is probably closest to CXX98 but with no support for: - # Templates, multiple inheritance, virtual inheritance, exceptions, RTTI, C++ style casts, - # Namespaces, the mutable attribute, no STL, any library features related to the above features. - # - # "(extended) embedded C++" --EEC++ Mode but DOES NOT support any normal C++ standard - # probably closest to CXX98 but with no RTTI and no exceptions, and the library - # provided is not in the standard namespace - if(NOT CMAKE_IAR_CXX_FLAG) - if(NOT CMAKE_CXX_COMPILER_VERSION) - message(FATAL_ERROR "CMAKE_CXX_COMPILER_VERSION not detected. This should be automatic.") - endif() - set(CMAKE_IAR_CXX_FLAG --eec++) - endif() +elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX") + __compiler_iar_ilink(CXX) + __compiler_check_default_language_standard(CXX 2.10 98 4.10 14) - set(CMAKE_CXX_EXTENSION_COMPILE_OPTION -e) - set(CMAKE_CXX98_STANDARD_COMPILE_OPTION "") - set(CMAKE_CXX98_EXTENSION_COMPILE_OPTION -e) - - __compiler_iar_AVR(CXX) +elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") + __compiler_iar_xlink(CXX) __compiler_check_default_language_standard(CXX 7.10 98) set(CMAKE_CXX_OUTPUT_EXTENSION ".r90") @@ -64,15 +49,12 @@ elseif("${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") set(CMAKE_CXX_LINK_FLAGS "-Fmotorola") endif() - set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <OBJECTS> <CMAKE_CXX_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>") - set(CMAKE_CXX_CREATE_STATIC_LIBRARY "<CMAKE_AR> -o <TARGET> <OBJECTS> ") - # add the target specific include directory: get_filename_component(_compilerDir "${CMAKE_C_COMPILER}" PATH) get_filename_component(_compilerDir "${_compilerDir}" PATH) - include_directories("${_compilerDir}/inc") - include_directories("${_compilerDir}/inc/Atmel") + include_directories("${_compilerDir}/inc" ) + include_directories("${_compilerDir}/inc/Atmel" ) else() - message(FATAL_ERROR "CMAKE_CXX_COMPILER_ARCHITECTURE_ID not detected as \"AVR\" or \"ARM\". This should be automatic." ) + message(FATAL_ERROR "CMAKE_CXX_COMPILER_ARCHITECTURE_ID not detected. This should be automatic." ) endif() diff --git a/Modules/Compiler/IAR-DetermineCompiler.cmake b/Modules/Compiler/IAR-DetermineCompiler.cmake index 43477ac..cdfb095 100644 --- a/Modules/Compiler/IAR-DetermineCompiler.cmake +++ b/Modules/Compiler/IAR-DetermineCompiler.cmake @@ -31,7 +31,7 @@ set(_compiler_id_version_compute " # define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@(((__VER__) / 1000) % 1000) # define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@((__VER__) % 1000) # define @PREFIX@COMPILER_VERSION_INTERNAL @MACRO_DEC@(__IAR_SYSTEMS_ICC__) -# elif defined(__VER__) && defined(__ICCAVR__) +# elif defined(__VER__) && (defined(__ICCAVR__) || defined(__ICCRX__)) # define @PREFIX@COMPILER_VERSION_MAJOR @MACRO_DEC@((__VER__) / 100) # define @PREFIX@COMPILER_VERSION_MINOR @MACRO_DEC@((__VER__) - (((__VER__) / 100)*100)) # define @PREFIX@COMPILER_VERSION_PATCH @MACRO_DEC@(__SUBVERSION__) diff --git a/Modules/Compiler/IAR-FindBinUtils.cmake b/Modules/Compiler/IAR-FindBinUtils.cmake index 5fecb26..e8f5e6b 100644 --- a/Modules/Compiler/IAR-FindBinUtils.cmake +++ b/Modules/Compiler/IAR-FindBinUtils.cmake @@ -10,39 +10,39 @@ get_filename_component(__iar_hint_2 "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPI set(__iar_hints "${__iar_hint_1}" "${__iar_hint_2}") -if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM") - # could allow using normal binutils ar, since objects are normal ELF files? - find_program(CMAKE_IAR_LINKARM ilinkarm.exe HINTS ${__iar_hints} - DOC "The IAR ARM linker") +if("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "ARM" OR + "${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "RX") + # could allow using normal binutils ar, since objects are normal ELF files? + find_program(CMAKE_IAR_LINKER ilink${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}.exe HINTS ${__iar_hints} + DOC "The IAR ILINK linker") find_program(CMAKE_IAR_ARCHIVE iarchive.exe HINTS ${__iar_hints} DOC "The IAR archiver") # find auxiliary tools find_program(CMAKE_IAR_ELFTOOL ielftool.exe HINTS ${__iar_hints} DOC "The IAR ELF Tool") - find_program(CMAKE_IAR_ELFDUMP ielfdumparm.exe HINTS ${__iar_hints} + find_program(CMAKE_IAR_ELFDUMP ielfdump${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}.exe HINTS ${__iar_hints} DOC "The IAR ELF Dumper") find_program(CMAKE_IAR_OBJMANIP iobjmanip.exe HINTS ${__iar_hints} DOC "The IAR ELF Object Tool") find_program(CMAKE_IAR_SYMEXPORT isymexport.exe HINTS ${__iar_hints} DOC "The IAR Absolute Symbol Exporter") - mark_as_advanced(CMAKE_IAR_LINKARM CMAKE_IAR_ARCHIVE CMAKE_IAR_ELFTOOL CMAKE_IAR_ELFDUMP CMAKE_IAR_OBJMANIP CMAKE_IAR_SYMEXPORT) + mark_as_advanced(CMAKE_IAR_LINKER CMAKE_IAR_ARCHIVE CMAKE_IAR_ELFTOOL CMAKE_IAR_ELFDUMP CMAKE_IAR_OBJMANIP CMAKE_IAR_SYMEXPORT) set(CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_CUSTOM_CODE -"set(CMAKE_IAR_LINKARM \"${CMAKE_IAR_LINKARM}\") +"set(CMAKE_IAR_LINKER \"${CMAKE_IAR_LINKER}\") set(CMAKE_IAR_ARCHIVE \"${CMAKE_IAR_ARCHIVE}\") set(CMAKE_IAR_ELFTOOL \"${CMAKE_IAR_ELFTOOL}\") set(CMAKE_IAR_ELFDUMP \"${CMAKE_IAR_ELFDUMP}\") set(CMAKE_IAR_OBJMANIP \"${CMAKE_IAR_OBJMANIP}\") -set(CMAKE_IAR_LINKARM \"${CMAKE_IAR_LINKARM}\") +set(CMAKE_IAR_LINKER \"${CMAKE_IAR_LINKER}\") ") - elseif("${CMAKE_${_CMAKE_PROCESSING_LANGUAGE}_COMPILER_ARCHITECTURE_ID}" STREQUAL "AVR") # For AVR and AVR32, IAR uses the "xlink" linker and the "xar" archiver: find_program(CMAKE_IAR_LINKER xlink.exe HINTS ${__iar_hints} - DOC "The IAR AVR linker") + DOC "The IAR XLINK linker") find_program(CMAKE_IAR_AR xar.exe HINTS ${__iar_hints} DOC "The IAR archiver") mark_as_advanced(CMAKE_IAR_LINKER CMAKE_IAR_AR) diff --git a/Modules/Compiler/IAR.cmake b/Modules/Compiler/IAR.cmake index bbcdea2..8e75caa 100644 --- a/Modules/Compiler/IAR.cmake +++ b/Modules/Compiler/IAR.cmake @@ -2,11 +2,16 @@ # Documentation can be downloaded here: http://www.iar.com/website1/1.0.1.0/675/1/ # The initial feature request is here: https://gitlab.kitware.com/cmake/cmake/issues/10176 # It also contains additional links and information. -# See USER GUIDES -> C/C++ Development Guide and ReleaseNotes for: +# See USER GUIDES -> C/C++ Development Guide and ReleaseNotes for EWARM: # version 6.30.8: http://supp.iar.com/FilesPublic/UPDINFO/006607/arm/doc/infocenter/index.ENU.html # version 7.60.1: http://supp.iar.com/FilesPublic/UPDINFO/011006/arm/doc/infocenter/index.ENU.html # version 8.10.1: http://netstorage.iar.com/SuppDB/Public/UPDINFO/011854/arm/doc/infocenter/index.ENU.html +# The IAR internal compiler platform generations (Predefined symbol __IAR_SYSTEMS_ICC__): +# 9 and higher means C11 and C++14 as language default (EWARM v8.x, EWRX v4.x and higher) +# 8 means C99 and C++03 as language default (EWARM v6.x, v7.x. EWRX v2.x, 3.x) +# 7 and lower means C89 and EC++ as language default. (EWARM v5.x and lower) + # C/C++ Standard versions # # IAR typically only supports one C and C++ Standard version, @@ -33,15 +38,11 @@ # code and data size printouts (that can be inspected with common tools). # This module is shared by multiple languages; use include blocker. -if(_IARARM_CMAKE_LOADED) - return() -endif() -set(_IARARM_CMAKE_LOADED 1) +include_guard() -macro(__compiler_iar_ARM lang) +macro(__compiler_iar_ilink lang) set(CMAKE_EXECUTABLE_SUFFIX ".elf") if (${lang} STREQUAL "C" OR ${lang} STREQUAL "CXX") - set(CMAKE_${lang}_COMPILE_OBJECT "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT>") set(CMAKE_${lang}_CREATE_PREPROCESSED_SOURCE "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> --preprocess=cnl <PREPROCESSED_SOURCE>") set(CMAKE_${lang}_CREATE_ASSEMBLY_SOURCE "<CMAKE_${lang}_COMPILER> ${CMAKE_IAR_${lang}_FLAG} --silent <SOURCE> <DEFINES> <INCLUDES> <FLAGS> -lAH <ASSEMBLY_SOURCE> -o <OBJECT>.dummy") @@ -56,17 +57,25 @@ macro(__compiler_iar_ARM lang) string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -Oh -r -DNDEBUG") endif() - set(CMAKE_${lang}_LINK_EXECUTABLE "\"${CMAKE_IAR_LINKARM}\" --silent <OBJECTS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>") + if (${lang} STREQUAL "ASM") + string(APPEND CMAKE_ASM_FLAGS_INIT " ") + string(APPEND CMAKE_ASM_FLAGS_DEBUG_INIT " -r") + string(APPEND CMAKE_ASM_FLAGS_MINSIZEREL_INIT " -DNDEBUG") + string(APPEND CMAKE_ASM_FLAGS_RELEASE_INIT " -DNDEBUG") + string(APPEND CMAKE_ASM_FLAGS_RELWITHDEBINFO_INIT " -r -DNDEBUG") + endif() + + set(CMAKE_${lang}_LINK_EXECUTABLE "\"${CMAKE_IAR_LINKER}\" --silent <OBJECTS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>") set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "\"${CMAKE_IAR_ARCHIVE}\" <TARGET> --create <LINK_FLAGS> <OBJECTS>") set(CMAKE_${lang}_ARCHIVE_CREATE "\"${CMAKE_IAR_ARCHIVE}\" <TARGET> --create <LINK_FLAGS> <OBJECTS>") set(CMAKE_${lang}_ARCHIVE_APPEND "\"${CMAKE_IAR_ARCHIVE}\" <TARGET> --replace <LINK_FLAGS> <OBJECTS>") set(CMAKE_${lang}_ARCHIVE_FINISH "") - set(CMAKE_LINKER "${CMAKE_IAR_LINKARM}" CACHE FILEPATH "The IAR linker" FORCE) + set(CMAKE_LINKER "${CMAKE_IAR_LINKER}" CACHE FILEPATH "The IAR linker" FORCE) set(CMAKE_AR "${CMAKE_IAR_ARCHIVE}" CACHE FILEPATH "The IAR archiver" FORCE) endmacro() -macro(__compiler_iar_AVR lang) +macro(__compiler_iar_xlink lang) set(CMAKE_EXECUTABLE_SUFFIX ".bin") if (${lang} STREQUAL "C" OR ${lang} STREQUAL "CXX") @@ -84,6 +93,14 @@ macro(__compiler_iar_AVR lang) string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -Oh -r -DNDEBUG") endif() + if (${lang} STREQUAL "ASM") + string(APPEND CMAKE_ASM_FLAGS_INIT " ") + string(APPEND CMAKE_ASM_FLAGS_DEBUG_INIT " -r") + string(APPEND CMAKE_ASM_FLAGS_MINSIZEREL_INIT " -DNDEBUG") + string(APPEND CMAKE_ASM_FLAGS_RELEASE_INIT " -DNDEBUG") + string(APPEND CMAKE_ASM_FLAGS_RELWITHDEBINFO_INIT " -r -DNDEBUG") + endif() + set(CMAKE_${lang}_LINK_EXECUTABLE "\"${CMAKE_IAR_LINKER}\" -S <OBJECTS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <LINK_LIBRARIES> -o <TARGET>") set(CMAKE_${lang}_CREATE_STATIC_LIBRARY "\"${CMAKE_IAR_AR}\" <TARGET> <LINK_FLAGS> <OBJECTS>") set(CMAKE_${lang}_ARCHIVE_CREATE "\"${CMAKE_IAR_AR}\" <TARGET> <LINK_FLAGS> <OBJECTS>") diff --git a/Modules/ExternalProject.cmake b/Modules/ExternalProject.cmake index 8f1e194..20b37d2 100644 --- a/Modules/ExternalProject.cmake +++ b/Modules/ExternalProject.cmake @@ -422,6 +422,10 @@ External Project Definition different behavior depending on whether the build starts from a fresh build directory or re-uses previous build contents. + If the CMake generator is the ``Green Hills MULTI`` and not overridden then + the orginal projects settings for the GHS toolset and target system + customization cache variables are propagated into the external project. + ``SOURCE_SUBDIR <dir>`` When no ``CONFIGURE_COMMAND`` option is specified, the configure step assumes the external project has a ``CMakeLists.txt`` file at the top of @@ -2850,18 +2854,6 @@ function(_ep_extract_configure_command var name) set(has_cmake_cache_default_args 1) endif() - if(has_cmake_cache_args OR has_cmake_cache_default_args) - set(_ep_cache_args_script "<TMP_DIR>/${name}-cache-$<CONFIG>.cmake") - if(has_cmake_cache_args) - _ep_command_line_to_initial_cache(script_initial_cache_force "${cmake_cache_args}" 1) - endif() - if(has_cmake_cache_default_args) - _ep_command_line_to_initial_cache(script_initial_cache_default "${cmake_cache_default_args}" 0) - endif() - _ep_write_initial_cache(${name} "${_ep_cache_args_script}" "${script_initial_cache_force}${script_initial_cache_default}") - list(APPEND cmd "-C${_ep_cache_args_script}") - endif() - get_target_property(cmake_generator ${name} _EP_CMAKE_GENERATOR) get_target_property(cmake_generator_instance ${name} _EP_CMAKE_GENERATOR_INSTANCE) get_target_property(cmake_generator_platform ${name} _EP_CMAKE_GENERATOR_PLATFORM) @@ -2882,6 +2874,16 @@ function(_ep_extract_configure_command var name) list(APPEND cmd "-G${CMAKE_EXTRA_GENERATOR} - ${CMAKE_GENERATOR}") else() list(APPEND cmd "-G${CMAKE_GENERATOR}") + if("${CMAKE_GENERATOR}" MATCHES "Green Hills MULTI") + set(has_cmake_cache_default_args 1) + set(cmake_cache_default_args ${cmake_cache_default_args} + "-DGHS_TARGET_PLATFORM:STRING=${GHS_TARGET_PLATFORM}" + "-DGHS_PRIMARY_TARGET:STRING=${GHS_PRIMARY_TARGET}" + "-DGHS_TOOLSET_ROOT:STRING=${GHS_TOOLSET_ROOT}" + "-DGHS_OS_ROOT:STRING=${GHS_OS_ROOT}" + "-DGHS_OS_DIR:STRING=${GHS_OS_DIR}" + "-DGHS_BSP_NAME:STRING=${GHS_BSP_NAME}") + endif() endif() if(cmake_generator_platform) message(FATAL_ERROR "Option CMAKE_GENERATOR_PLATFORM not allowed without CMAKE_GENERATOR.") @@ -2903,6 +2905,18 @@ function(_ep_extract_configure_command var name) endif() endif() + if(has_cmake_cache_args OR has_cmake_cache_default_args) + set(_ep_cache_args_script "<TMP_DIR>/${name}-cache-$<CONFIG>.cmake") + if(has_cmake_cache_args) + _ep_command_line_to_initial_cache(script_initial_cache_force "${cmake_cache_args}" 1) + endif() + if(has_cmake_cache_default_args) + _ep_command_line_to_initial_cache(script_initial_cache_default "${cmake_cache_default_args}" 0) + endif() + _ep_write_initial_cache(${name} "${_ep_cache_args_script}" "${script_initial_cache_force}${script_initial_cache_default}") + list(APPEND cmd "-C${_ep_cache_args_script}") + endif() + list(APPEND cmd "<SOURCE_DIR><SOURCE_SUBDIR>") endif() diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake index 53be493..7325eca 100644 --- a/Modules/FindBoost.cmake +++ b/Modules/FindBoost.cmake @@ -466,8 +466,17 @@ function(_Boost_GUESS_COMPILER_PREFIX _ret) elseif (GHSMULTI) set(_boost_COMPILER "-ghs") elseif("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC" OR "x${CMAKE_CXX_SIMULATE_ID}" STREQUAL "xMSVC") - if(MSVC_TOOLSET_VERSION GREATER_EQUAL 141) - set(_boost_COMPILER "-vc141;-vc140") + if(MSVC_TOOLSET_VERSION GREATER_EQUAL 150) + # Not yet known. + set(_boost_COMPILER "") + elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 140) + # MSVC toolset 14.x versions are forward compatible. + set(_boost_COMPILER "") + foreach(v 9 8 7 6 5 4 3 2 1 0) + if(MSVC_TOOLSET_VERSION GREATER_EQUAL 14${v}) + list(APPEND _boost_COMPILER "-vc14${v}") + endif() + endforeach() elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 80) set(_boost_COMPILER "-vc${MSVC_TOOLSET_VERSION}") elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.10) @@ -1083,9 +1092,15 @@ function(_Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS component else() set(_arch_suffix 32) endif() - if(MSVC_TOOLSET_VERSION GREATER_EQUAL 141) - list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-14.1) - list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-14.0) + if(MSVC_TOOLSET_VERSION GREATER_EQUAL 150) + # Not yet known. + elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 140) + # MSVC toolset 14.x versions are forward compatible. + foreach(v 9 8 7 6 5 4 3 2 1 0) + if(MSVC_TOOLSET_VERSION GREATER_EQUAL 14${v}) + list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-14.${v}) + endif() + endforeach() elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 80) math(EXPR _toolset_major_version "${MSVC_TOOLSET_VERSION} / 10") list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-${_toolset_major_version}.0) @@ -1762,7 +1777,7 @@ foreach(COMPONENT ${Boost_FIND_COMPONENTS}) list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}${COMPONENT_PYTHON_VERSION_MAJOR}-py${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}") list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-py${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}") # Gentoo - list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}") + list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}.${COMPONENT_PYTHON_VERSION_MINOR}") # RPMs list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}") endif() diff --git a/Modules/FindOpenGL.cmake b/Modules/FindOpenGL.cmake index 832dca2..00db033 100644 --- a/Modules/FindOpenGL.cmake +++ b/Modules/FindOpenGL.cmake @@ -205,11 +205,13 @@ else() find_library(OPENGL_glx_LIBRARY NAMES GLX PATHS ${_OPENGL_LIB_PATH} + PATH_SUFFIXES libglvnd ) find_library(OPENGL_egl_LIBRARY NAMES EGL PATHS ${_OPENGL_LIB_PATH} + PATH_SUFFIXES libglvnd ) find_library(OPENGL_glu_LIBRARY @@ -264,6 +266,7 @@ else() /usr/openwin/lib /usr/shlib ${_OPENGL_LIB_PATH} + PATH_SUFFIXES libglvnd ) endif() diff --git a/Modules/InstallRequiredSystemLibraries.cmake b/Modules/InstallRequiredSystemLibraries.cmake index 2f34a7a..0a98895 100644 --- a/Modules/InstallRequiredSystemLibraries.cmake +++ b/Modules/InstallRequiredSystemLibraries.cmake @@ -213,10 +213,13 @@ if(MSVC) elseif(MSVC_VERSION_VERSION GREATER_EQUAL 143) message(WARNING "MSVC toolset v${MSVC_VERSION_VERSION} not yet supported.") elseif(MSVC_TOOLSET_VERSION EQUAL 142) - # FIXME: VS 2019 RC 4 uses VC141 but an update will fix it to be VC142. - set(MSVC_REDIST_NAME VC141) + set(MSVC_REDIST_NAME VC142) set(_MSVC_DLL_VERSION 140) set(_MSVC_IDE_VERSION 16) + if(MSVC_VERSION EQUAL 1920) + # VS2019 named this differently prior to update 1. + set(MSVC_REDIST_NAME VC141) + endif() elseif(MSVC_TOOLSET_VERSION EQUAL 141) set(MSVC_REDIST_NAME VC141) set(_MSVC_DLL_VERSION 140) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 924d997..49f237f 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -350,8 +350,8 @@ set(SRCS cmQtAutoGenGlobalInitializer.h cmQtAutoGenInitializer.cxx cmQtAutoGenInitializer.h - cmQtAutoGeneratorMocUic.cxx - cmQtAutoGeneratorMocUic.h + cmQtAutoMocUic.cxx + cmQtAutoMocUic.h cmQtAutoRcc.cxx cmQtAutoRcc.h cmRST.cxx @@ -391,6 +391,8 @@ set(SRCS cmVariableWatch.h cmVersion.cxx cmVersion.h + cmWorkerPool.cxx + cmWorkerPool.h cmWorkingDirectory.cxx cmWorkingDirectory.h cmXMLParser.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 70c44e1..b7b278b 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 20190412) +set(CMake_VERSION_PATCH 20190417) #set(CMake_VERSION_RC 1) diff --git a/Source/CTest/cmCTestBuildAndTestHandler.cxx b/Source/CTest/cmCTestBuildAndTestHandler.cxx index a7d4455..9ad9669 100644 --- a/Source/CTest/cmCTestBuildAndTestHandler.cxx +++ b/Source/CTest/cmCTestBuildAndTestHandler.cxx @@ -126,11 +126,12 @@ public: cmSystemTools::SetStdoutCallback([&s](std::string const& m) { s += m; }); cmSystemTools::SetStderrCallback([&s](std::string const& m) { s += m; }); - this->CM.SetProgressCallback( - [&s](const std::string& msg, float /*unused*/) { + this->CM.SetProgressCallback([&s](const std::string& msg, float prog) { + if (prog < 0) { s += msg; s += "\n"; - }); + } + }); } ~cmCTestBuildAndTestCaptureRAII() diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index af409e4..8b3d9d6 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -2210,8 +2210,8 @@ struct TargetOutputNameArtifactResultGetter<ArtifactLinkerTag> // The file used to link to the target (.so, .lib, .a). if (!target->IsLinkable()) { ::reportError(context, content->GetOriginalExpression(), - "TARGET_LINKER_OUTPUT_NAME is allowed only for libraries " - "and executables with ENABLE_EXPORTS."); + "TARGET_LINKER_FILE_BASE_NAME is allowed only for " + "libraries and executables with ENABLE_EXPORTS."); return std::string(); } cmStateEnums::ArtifactType artifact = @@ -2232,7 +2232,7 @@ struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag> if (target->IsImported()) { ::reportError( context, content->GetOriginalExpression(), - "TARGET_PDB_OUTPUT_NAME not allowed for IMPORTED targets."); + "TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets."); return std::string(); } @@ -2243,7 +2243,7 @@ struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag> if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) { ::reportError( context, content->GetOriginalExpression(), - "TARGET_PDB_OUTPUT_NAME is not supported by the target linker."); + "TARGET_PDB_FILE_BASE_NAME is not supported by the target linker."); return std::string(); } @@ -2253,7 +2253,7 @@ struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag> targetType != cmStateEnums::MODULE_LIBRARY && targetType != cmStateEnums::EXECUTABLE) { ::reportError(context, content->GetOriginalExpression(), - "TARGET_PDB_OUTPUT_NAME is allowed only for " + "TARGET_PDB_FILE_BASE_NAME is allowed only for " "targets with linker created artifacts."); return std::string(); } @@ -2263,9 +2263,9 @@ struct TargetOutputNameArtifactResultGetter<ArtifactPdbTag> }; template <typename ArtifactT> -struct TargetOutputNameArtifact : public TargetArtifactBase +struct TargetFileBaseNameArtifact : public TargetArtifactBase { - TargetOutputNameArtifact() {} // NOLINT(modernize-use-equals-default) + TargetFileBaseNameArtifact() {} // NOLINT(modernize-use-equals-default) int NumExpectedParameters() const override { return 1; } @@ -2290,12 +2290,12 @@ struct TargetOutputNameArtifact : public TargetArtifactBase } }; -static const TargetOutputNameArtifact<ArtifactNameTag> targetOutputNameNode; - -static const TargetOutputNameArtifact<ArtifactLinkerTag> - targetLinkerOutputNameNode; - -static const TargetOutputNameArtifact<ArtifactPdbTag> targetPdbOutputNameNode; +static const TargetFileBaseNameArtifact<ArtifactNameTag> + targetFileBaseNameNode; +static const TargetFileBaseNameArtifact<ArtifactLinkerTag> + targetLinkerFileBaseNameNode; +static const TargetFileBaseNameArtifact<ArtifactPdbTag> + targetPdbFileBaseNameNode; class ArtifactFilePrefixTag; class ArtifactLinkerFilePrefixTag; @@ -2474,6 +2474,9 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode( { "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File }, { "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File }, { "TARGET_PDB_FILE", &targetPdbNodeGroup.File }, + { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode }, + { "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode }, + { "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode }, { "TARGET_FILE_PREFIX", &targetFilePrefixNode }, { "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode }, { "TARGET_FILE_SUFFIX", &targetFileSuffixNode }, @@ -2488,9 +2491,6 @@ const cmGeneratorExpressionNode* cmGeneratorExpressionNode::GetNode( { "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir }, { "TARGET_BUNDLE_DIR", &targetBundleDirNode }, { "TARGET_BUNDLE_CONTENT_DIR", &targetBundleContentDirNode }, - { "TARGET_OUTPUT_NAME", &targetOutputNameNode }, - { "TARGET_LINKER_OUTPUT_NAME", &targetLinkerOutputNameNode }, - { "TARGET_PDB_OUTPUT_NAME", &targetPdbOutputNameNode }, { "STREQUAL", &strEqualNode }, { "EQUAL", &equalNode }, { "IN_LIST", &inListNode }, diff --git a/Source/cmGhsMultiGpj.cxx b/Source/cmGhsMultiGpj.cxx index 8b69b51..da27971 100644 --- a/Source/cmGhsMultiGpj.cxx +++ b/Source/cmGhsMultiGpj.cxx @@ -9,7 +9,8 @@ static const char* GHS_TAG[] = { "[INTEGRITY Application]", "[Project]", "[Program]", "[Reference]", - "[Subproject]" }; + "[Subproject]", + "[Custom Target]" }; const char* GhsMultiGpj::GetGpjTag(Types gpjType) { @@ -21,6 +22,7 @@ const char* GhsMultiGpj::GetGpjTag(Types gpjType) case PROGRAM: case REFERENCE: case SUBPROJECT: + case CUSTOM_TARGET: tag = GHS_TAG[gpjType]; break; default: diff --git a/Source/cmGhsMultiGpj.h b/Source/cmGhsMultiGpj.h index 420eab1..e588150 100644 --- a/Source/cmGhsMultiGpj.h +++ b/Source/cmGhsMultiGpj.h @@ -16,7 +16,8 @@ public: PROJECT, PROGRAM, REFERENCE, - SUBPROJECT + SUBPROJECT, + CUSTOM_TARGET }; static void WriteGpjTag(Types gpjType, std::ostream& fout); diff --git a/Source/cmGhsMultiTargetGenerator.cxx b/Source/cmGhsMultiTargetGenerator.cxx index 668bcbd..b80da72 100644 --- a/Source/cmGhsMultiTargetGenerator.cxx +++ b/Source/cmGhsMultiTargetGenerator.cxx @@ -3,7 +3,7 @@ #include "cmGhsMultiTargetGenerator.h" #include "cmCustomCommand.h" -#include "cmCustomCommandLines.h" +#include "cmCustomCommandGenerator.h" #include "cmGeneratedFileStream.h" #include "cmGeneratorTarget.h" #include "cmGlobalGhsMultiGenerator.h" @@ -11,17 +11,17 @@ #include "cmLocalGenerator.h" #include "cmLocalGhsMultiGenerator.h" #include "cmMakefile.h" +#include "cmOutputConverter.h" #include "cmSourceFile.h" +#include "cmSourceFileLocation.h" #include "cmSourceGroup.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" #include "cmStateTypes.h" #include "cmSystemTools.h" #include "cmTarget.h" -#include "cmTargetDepend.h" #include <algorithm> -#include <assert.h> #include <ostream> #include <set> #include <utility> @@ -32,6 +32,11 @@ cmGhsMultiTargetGenerator::cmGhsMultiTargetGenerator(cmGeneratorTarget* target) static_cast<cmLocalGhsMultiGenerator*>(target->GetLocalGenerator())) , Makefile(target->Target->GetMakefile()) , Name(target->GetName()) +#ifdef _WIN32 + , CmdWindowsShell(true) +#else + , CmdWindowsShell(false) +#endif { // Store the configuration name that is being used if (const char* config = this->Makefile->GetDefinition("CMAKE_BUILD_TYPE")) { @@ -85,10 +90,19 @@ void cmGhsMultiTargetGenerator::Generate() return; } case cmStateEnums::UTILITY: { - std::string msg = "add_custom_target(<name> ...) not supported: "; - msg += this->Name; - cmSystemTools::Message(msg); - return; + this->TargetNameReal = this->GeneratorTarget->GetName(); + this->TagType = GhsMultiGpj::CUSTOM_TARGET; + break; + } + case cmStateEnums::GLOBAL_TARGET: { + this->TargetNameReal = this->GeneratorTarget->GetName(); + if (this->TargetNameReal == + this->GetGlobalGenerator()->GetInstallTargetName()) { + this->TagType = GhsMultiGpj::CUSTOM_TARGET; + } else { + return; + } + break; } default: return; @@ -105,29 +119,29 @@ void cmGhsMultiTargetGenerator::Generate() void cmGhsMultiTargetGenerator::GenerateTarget() { - // Open the filestream in copy-if-different mode. - std::string fname = this->LocalGenerator->GetCurrentBinaryDirectory(); - fname += "/"; - fname += this->Name; - fname += cmGlobalGhsMultiGenerator::FILE_EXTENSION; - cmGeneratedFileStream fout(fname); + // Open the target file in copy-if-different mode. + std::string fproj = this->LocalGenerator->GetCurrentBinaryDirectory(); + fproj += "/"; + fproj += this->Name; + fproj += cmGlobalGhsMultiGenerator::FILE_EXTENSION; + cmGeneratedFileStream fout(fproj); fout.SetCopyIfDifferent(true); this->GetGlobalGenerator()->WriteFileHeader(fout); GhsMultiGpj::WriteGpjTag(this->TagType, fout); - const std::string language( - this->GeneratorTarget->GetLinkerLanguage(this->ConfigName)); - - this->WriteTargetSpecifics(fout, this->ConfigName); - this->SetCompilerFlags(this->ConfigName, language); - this->WriteCompilerFlags(fout, this->ConfigName, language); - this->WriteCompilerDefinitions(fout, this->ConfigName, language); - this->WriteIncludes(fout, this->ConfigName, language); - this->WriteTargetLinkLine(fout, this->ConfigName); - this->WriteCustomCommands(fout); + if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { + const std::string language( + this->GeneratorTarget->GetLinkerLanguage(this->ConfigName)); + this->WriteTargetSpecifics(fout, this->ConfigName); + this->SetCompilerFlags(this->ConfigName, language); + this->WriteCompilerFlags(fout, this->ConfigName, language); + this->WriteCompilerDefinitions(fout, this->ConfigName, language); + this->WriteIncludes(fout, this->ConfigName, language); + this->WriteTargetLinkLine(fout, this->ConfigName); + this->WriteBuildEvents(fout); + } this->WriteSources(fout); - this->WriteReferences(fout); fout.Close(); } @@ -304,47 +318,145 @@ void cmGhsMultiTargetGenerator::WriteTargetLinkLine(std::ostream& fout, } } -void cmGhsMultiTargetGenerator::WriteCustomCommands(std::ostream& fout) +void cmGhsMultiTargetGenerator::WriteBuildEvents(std::ostream& fout) { - WriteCustomCommandsHelper(fout, this->GeneratorTarget->GetPreBuildCommands(), - cmTarget::PRE_BUILD); - WriteCustomCommandsHelper( - fout, this->GeneratorTarget->GetPostBuildCommands(), cmTarget::POST_BUILD); + this->WriteBuildEventsHelper( + fout, this->GeneratorTarget->GetPreBuildCommands(), + std::string("prebuild"), std::string("preexecShell")); + + if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { + this->WriteBuildEventsHelper( + fout, this->GeneratorTarget->GetPreLinkCommands(), + std::string("prelink"), std::string("preexecShell")); + } + + this->WriteBuildEventsHelper( + fout, this->GeneratorTarget->GetPostBuildCommands(), + std::string("postbuild"), std::string("postexecShell")); +} + +void cmGhsMultiTargetGenerator::WriteBuildEventsHelper( + std::ostream& fout, const std::vector<cmCustomCommand>& ccv, + std::string const& name, std::string const& cmd) +{ + int cmdcount = 0; + + for (cmCustomCommand const& cc : ccv) { + cmCustomCommandGenerator ccg(cc, this->ConfigName, this->LocalGenerator); + // Open the filestream for this custom command + std::string fname = this->LocalGenerator->GetCurrentBinaryDirectory(); + fname += + "/" + this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + fname += "/" + this->Name + "_" + name; + fname += std::to_string(cmdcount++); + fname += this->CmdWindowsShell ? ".bat" : ".sh"; + cmGeneratedFileStream f(fname); + f.SetCopyIfDifferent(true); + this->WriteCustomCommandsHelper(f, ccg); + f.Close(); + if (this->TagType != GhsMultiGpj::CUSTOM_TARGET) { + fout << " :" << cmd << "=\"" << fname << "\"" << std::endl; + } else { + fout << fname << std::endl; + fout << " :outputName=\"" << fname << ".rule\"" << std::endl; + } + for (auto& byp : ccg.GetByproducts()) { + fout << " :extraOutputFile=\"" << byp << "\"" << std::endl; + } + } } void cmGhsMultiTargetGenerator::WriteCustomCommandsHelper( - std::ostream& fout, std::vector<cmCustomCommand> const& commandsSet, - cmTarget::CustomCommandType const commandType) + std::ostream& fout, cmCustomCommandGenerator const& ccg) { - for (cmCustomCommand const& customCommand : commandsSet) { - cmCustomCommandLines const& commandLines = customCommand.GetCommandLines(); - for (cmCustomCommandLine const& command : commandLines) { - switch (commandType) { - case cmTarget::PRE_BUILD: - fout << " :preexecShellSafe="; - break; - case cmTarget::POST_BUILD: - fout << " :postexecShellSafe="; - break; - default: - assert("Only pre and post are supported"); + std::vector<std::string> cmdLines; + + // if the command specified a working directory use it. + std::string dir = this->LocalGenerator->GetCurrentBinaryDirectory(); + std::string currentBinDir = dir; + std::string workingDir = ccg.GetWorkingDirectory(); + if (!workingDir.empty()) { + dir = workingDir; + } + + // Line to check for error between commands. +#ifdef _WIN32 + std::string check_error = "if %errorlevel% neq 0 exit /b %errorlevel%"; +#else + std::string check_error = "if [[ $? -ne 0 ]]; then exit 1; fi"; +#endif + +#ifdef _WIN32 + cmdLines.push_back("@echo off"); +#endif + // Echo the custom command's comment text. + const char* comment = ccg.GetComment(); + if (comment && *comment) { + std::string echocmd = "echo "; + echocmd += comment; + cmdLines.push_back(std::move(echocmd)); + } + + // Switch to working directory + std::string cdCmd; +#ifdef _WIN32 + std::string cdStr = "cd /D "; +#else + std::string cdStr = "cd "; +#endif + cdCmd = cdStr + + this->LocalGenerator->ConvertToOutputFormat(dir, cmOutputConverter::SHELL); + cmdLines.push_back(std::move(cdCmd)); + + for (unsigned int c = 0; c < ccg.GetNumberOfCommands(); ++c) { + // Build the command line in a single string. + std::string cmd = ccg.GetCommand(c); + if (!cmd.empty()) { + // Use "call " before any invocations of .bat or .cmd files + // invoked as custom commands in the WindowsShell. + // + bool useCall = false; + + if (this->CmdWindowsShell) { + std::string suffix; + if (cmd.size() > 4) { + suffix = cmSystemTools::LowerCase(cmd.substr(cmd.size() - 4)); + if (suffix == ".bat" || suffix == ".cmd") { + useCall = true; + } + } } - bool firstIteration = true; - for (std::string const& commandLine : command) { - std::string subCommandE = - this->LocalGenerator->EscapeForShell(commandLine, true); - fout << (firstIteration ? "'" : " "); - // Need to double escape backslashes - cmSystemTools::ReplaceString(subCommandE, "\\", "\\\\"); - fout << subCommandE; - firstIteration = false; + cmSystemTools::ReplaceString(cmd, "/./", "/"); + // Convert the command to a relative path only if the current + // working directory will be the start-output directory. + bool had_slash = cmd.find('/') != std::string::npos; + if (workingDir.empty()) { + cmd = + this->LocalGenerator->MaybeConvertToRelativePath(currentBinDir, cmd); } - if (!command.empty()) { - fout << "'" << std::endl; + bool has_slash = cmd.find('/') != std::string::npos; + if (had_slash && !has_slash) { + // This command was specified as a path to a file in the + // current directory. Add a leading "./" so it can run + // without the current directory being in the search path. + cmd = "./" + cmd; } + cmd = this->LocalGenerator->ConvertToOutputFormat( + cmd, cmOutputConverter::SHELL); + if (useCall) { + cmd = "call " + cmd; + } + ccg.AppendArguments(c, cmd); + cmdLines.push_back(std::move(cmd)); } } + + // push back the custom commands + for (auto const& c : cmdLines) { + fout << c << std::endl; + fout << check_error << std::endl; + } } void cmGhsMultiTargetGenerator::WriteSourceProperty( @@ -385,7 +497,7 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) /* list of known groups and the order they are displayed in a project file */ const std::vector<std::string> standardGroups = { - "Header Files", "Source Files", "CMake Rules", + "CMake Rules", "Header Files", "Source Files", "Object Files", "Object Libraries", "Resources" }; @@ -403,6 +515,14 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) groupFilesList[i] = *n; i += 1; groupNames.erase(gn); + } else if (this->TagType == GhsMultiGpj::CUSTOM_TARGET && + gn == "CMake Rules") { + /* make sure that rules folder always exists in case of custom targets + * that have no custom commands except for pre or post build events. + */ + groupFilesList.resize(groupFilesList.size() + 1); + groupFilesList[i] = gn; + i += 1; } } @@ -433,7 +553,7 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) /* write files into the proper project file * -- groups go into main project file - * unless FOLDER property or variable is set. + * unless NO_SOURCE_GROUP_FILE property or variable is set. */ for (auto& sg : groupFilesList) { std::ostream* fout; @@ -472,33 +592,98 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) } else { *fout << "{comment} " << sg << std::endl; } + } else if (sg.empty()) { + *fout << "{comment} Others" << std::endl; } - /* output rule for each source file */ - for (const cmSourceFile* si : groupFiles[sg]) { - - // Convert filename to native system - // WORKAROUND: GHS MULTI 6.1.4 and 6.1.6 are known to need backslash on - // windows when opening some files from the search window. - std::string fname(si->GetFullPath()); - cmSystemTools::ConvertToOutputSlashes(fname); - *fout << fname << std::endl; - - if ("ld" != si->GetExtension() && "int" != si->GetExtension() && - "bsp" != si->GetExtension()) { - WriteObjectLangOverride(*fout, si); + if (sg != "CMake Rules") { + /* output rule for each source file */ + for (const cmSourceFile* si : groupFiles[sg]) { + bool compile = true; + // Convert filename to native system + // WORKAROUND: GHS MULTI 6.1.4 and 6.1.6 are known to need backslash on + // windows when opening some files from the search window. + std::string fname(si->GetFullPath()); + cmSystemTools::ConvertToOutputSlashes(fname); + + /* For custom targets list any associated sources, + * comment out source code to prevent it from being + * compiled when processing this target. + * Otherwise, comment out any custom command (main) dependencies that + * are listed as source files to prevent them from being considered + * part of the build. + */ + std::string comment; + if ((this->TagType == GhsMultiGpj::CUSTOM_TARGET && + !si->GetLanguage().empty()) || + si->GetCustomCommand()) { + comment = "{comment} "; + compile = false; + } + + *fout << comment << fname << std::endl; + if (compile) { + if ("ld" != si->GetExtension() && "int" != si->GetExtension() && + "bsp" != si->GetExtension()) { + WriteObjectLangOverride(*fout, si); + } + + this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I"); + this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D"); + this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", ""); + + /* to avoid clutter in the GUI only print out the objectName if it + * has been renamed */ + std::string objectName = this->GeneratorTarget->GetObjectName(si); + if (!objectName.empty() && + this->GeneratorTarget->HasExplicitObjectName(si)) { + *fout << " -o " << objectName << std::endl; + } + } } - - this->WriteSourceProperty(*fout, si, "INCLUDE_DIRECTORIES", "-I"); - this->WriteSourceProperty(*fout, si, "COMPILE_DEFINITIONS", "-D"); - this->WriteSourceProperty(*fout, si, "COMPILE_OPTIONS", ""); - - /* to avoid clutter in the gui only print out the objectName if it has - * been renamed */ - std::string objectName = this->GeneratorTarget->GetObjectName(si); - if (!objectName.empty() && - this->GeneratorTarget->HasExplicitObjectName(si)) { - *fout << " -o " << objectName << std::endl; + } else { + std::vector<cmSourceFile const*> customCommands; + if (ComputeCustomCommandOrder(customCommands)) { + std::string message = "The custom commands for target [" + + this->GeneratorTarget->GetName() + "] had a cycle.\n"; + cmSystemTools::Error(message); + } else { + /* Custom targets do not have a dependency on SOURCES files. + * Therefore the dependency list may include SOURCES files after the + * custom target. Because nothing can depend on the custom target just + * move it to the last item. + */ + for (auto sf = customCommands.begin(); sf != customCommands.end(); + ++sf) { + if (((*sf)->GetLocation()).GetName() == this->Name + ".rule") { + std::rotate(sf, sf + 1, customCommands.end()); + break; + } + } + int cmdcount = 0; + for (auto& sf : customCommands) { + const cmCustomCommand* cc = sf->GetCustomCommand(); + cmCustomCommandGenerator ccg(*cc, this->ConfigName, + this->LocalGenerator); + + // Open the filestream for this custom command + std::string fname = + this->LocalGenerator->GetCurrentBinaryDirectory(); + fname += "/" + + this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget); + fname += "/" + this->Name + "_cc"; + fname += std::to_string(cmdcount++) + "_"; + fname += (sf->GetLocation()).GetName(); + fname += this->CmdWindowsShell ? ".bat" : ".sh"; + cmGeneratedFileStream f(fname); + f.SetCopyIfDifferent(true); + this->WriteCustomCommandsHelper(f, ccg); + f.Close(); + this->WriteCustomCommandLine(*fout, fname, ccg); + } + } + if (this->TagType == GhsMultiGpj::CUSTOM_TARGET) { + this->WriteBuildEvents(*fout); } } } @@ -508,6 +693,33 @@ void cmGhsMultiTargetGenerator::WriteSources(std::ostream& fout_proj) } } +void cmGhsMultiTargetGenerator::WriteCustomCommandLine( + std::ostream& fout, std::string& fname, cmCustomCommandGenerator const& ccg) +{ + /* NOTE: Customization Files are not well documented. Testing showed + * that ":outputName=file" can only be used once per script. The + * script will only run if ":outputName=file" is missing or just run + * once if ":outputName=file" is not specified. If there are + * multiple outputs then the script needs to be listed multiple times + * for each output. Otherwise it won't rerun the script if one of + * the outputs is manually deleted. + */ + bool specifyExtra = true; + for (auto& out : ccg.GetOutputs()) { + fout << fname << std::endl; + fout << " :outputName=\"" << out << "\"" << std::endl; + if (specifyExtra) { + for (auto& byp : ccg.GetByproducts()) { + fout << " :extraOutputFile=\"" << byp << "\"" << std::endl; + } + for (auto& dep : ccg.GetDepends()) { + fout << " :depends=\"" << dep << "\"" << std::endl; + } + specifyExtra = false; + } + } +} + void cmGhsMultiTargetGenerator::WriteObjectLangOverride( std::ostream& fout, const cmSourceFile* sourceFile) { @@ -521,35 +733,6 @@ void cmGhsMultiTargetGenerator::WriteObjectLangOverride( } } -void cmGhsMultiTargetGenerator::WriteReferences(std::ostream& fout) -{ - // This only applies to INTEGRITY Applications - if (this->TagType != GhsMultiGpj::INTERGRITY_APPLICATION) { - return; - } - - // Get the targets that this one depends upon - cmTargetDependSet unordered = - this->GetGlobalGenerator()->GetTargetDirectDepends(this->GeneratorTarget); - cmGlobalGhsMultiGenerator::OrderedTargetDependSet ordered(unordered, - this->Name); - for (auto& t : ordered) { - std::string tname = t->GetName(); - std::string tpath = t->LocalGenerator->GetCurrentBinaryDirectory(); - std::string rootpath = this->LocalGenerator->GetCurrentBinaryDirectory(); - std::string outpath = - this->LocalGenerator->MaybeConvertToRelativePath(rootpath, tpath) + "/" + - tname + "REF" + cmGlobalGhsMultiGenerator::FILE_EXTENSION; - - fout << outpath; - fout << " "; - GhsMultiGpj::WriteGpjTag(GhsMultiGpj::REFERENCE, fout); - - // Tell the global generator that a refernce project needs to be created - t->Target->SetProperty("GHS_REFERENCE_PROJECT", "ON"); - } -} - bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp() { const char* p = this->GeneratorTarget->GetProperty("ghs_integrity_app"); @@ -566,3 +749,51 @@ bool cmGhsMultiTargetGenerator::DetermineIfIntegrityApp() } return false; } + +bool cmGhsMultiTargetGenerator::ComputeCustomCommandOrder( + std::vector<cmSourceFile const*>& order) +{ + std::set<cmSourceFile const*> temp; + std::set<cmSourceFile const*> perm; + + // Collect all custom commands for this target + std::vector<cmSourceFile const*> customCommands; + this->GeneratorTarget->GetCustomCommands(customCommands, this->ConfigName); + + for (cmSourceFile const* si : customCommands) { + bool r = VisitCustomCommand(temp, perm, order, si); + if (r) { + return r; + } + } + return false; +} + +bool cmGhsMultiTargetGenerator::VisitCustomCommand( + std::set<cmSourceFile const*>& temp, std::set<cmSourceFile const*>& perm, + std::vector<cmSourceFile const*>& order, cmSourceFile const* si) +{ + /* check if permanent mark is set*/ + if (perm.find(si) == perm.end()) { + /* set temporary mark; check if revisit*/ + if (temp.insert(si).second) { + for (auto& di : si->GetCustomCommand()->GetDepends()) { + cmSourceFile const* sf = this->GeneratorTarget->GetLocalGenerator() + ->GetMakefile() + ->GetSourceFileWithOutput(di); + /* if sf exists then visit */ + if (sf && this->VisitCustomCommand(temp, perm, order, sf)) { + return true; + } + } + /* mark as complete; insert into beginning of list*/ + perm.insert(si); + order.push_back(si); + return false; + } + /* revisiting item - not a DAG */ + return true; + } + /* already complete */ + return false; +} diff --git a/Source/cmGhsMultiTargetGenerator.h b/Source/cmGhsMultiTargetGenerator.h index a4e23d9..a131567 100644 --- a/Source/cmGhsMultiTargetGenerator.h +++ b/Source/cmGhsMultiTargetGenerator.h @@ -5,14 +5,14 @@ #include "cmGhsMultiGpj.h" -#include "cmTarget.h" - #include <iosfwd> #include <map> +#include <set> #include <string> #include <vector> class cmCustomCommand; +class cmCustomCommandGenerator; class cmGeneratorTarget; class cmGlobalGhsMultiGenerator; class cmLocalGhsMultiGenerator; @@ -49,15 +49,23 @@ private: void WriteIncludes(std::ostream& fout, const std::string& config, const std::string& language); void WriteTargetLinkLine(std::ostream& fout, std::string const& config); - void WriteCustomCommands(std::ostream& fout); - void WriteCustomCommandsHelper( - std::ostream& fout, std::vector<cmCustomCommand> const& commandsSet, - cmTarget::CustomCommandType commandType); + void WriteBuildEvents(std::ostream& fout); + void WriteBuildEventsHelper(std::ostream& fout, + const std::vector<cmCustomCommand>& ccv, + std::string const& name, std::string const& cmd); + void WriteCustomCommandsHelper(std::ostream& fout, + cmCustomCommandGenerator const& ccg); + void WriteCustomCommandLine(std::ostream& fout, std::string& fname, + cmCustomCommandGenerator const& ccg); + bool ComputeCustomCommandOrder(std::vector<cmSourceFile const*>& order); + bool VisitCustomCommand(std::set<cmSourceFile const*>& temp, + std::set<cmSourceFile const*>& perm, + std::vector<cmSourceFile const*>& order, + cmSourceFile const* sf); void WriteSources(std::ostream& fout_proj); void WriteSourceProperty(std::ostream& fout, const cmSourceFile* sf, std::string const& propName, std::string const& propFlag); - void WriteReferences(std::ostream& fout); static void WriteObjectLangOverride(std::ostream& fout, const cmSourceFile* sourceFile); @@ -71,7 +79,8 @@ private: std::string TargetNameReal; GhsMultiGpj::Types TagType; std::string const Name; - std::string ConfigName; /* CMAKE_BUILD_TYPE */ + std::string ConfigName; /* CMAKE_BUILD_TYPE */ + bool const CmdWindowsShell; /* custom commands run in cmd.exe or /bin/sh */ }; #endif // ! cmGhsMultiTargetGenerator_h diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx index 9f361f6..b69dea0 100644 --- a/Source/cmGlobalGhsMultiGenerator.cxx +++ b/Source/cmGlobalGhsMultiGenerator.cxx @@ -265,14 +265,68 @@ void cmGlobalGhsMultiGenerator::WriteFileHeader(std::ostream& fout) << std::endl; } -void cmGlobalGhsMultiGenerator::WriteTopLevelProject( - std::ostream& fout, cmLocalGenerator* root, - std::vector<cmLocalGenerator*>& generators) +void cmGlobalGhsMultiGenerator::WriteCustomRuleBOD(std::ostream& fout) { - WriteFileHeader(fout); + fout << "Commands {\n" + " Custom_Rule_Command {\n" + " name = \"Custom Rule Command\"\n" + " exec = \""; +#ifdef _WIN32 + fout << "cmd.exe"; +#else + fout << "/bin/sh"; +#endif + fout << "\"\n" + " options = {\"SpecialOptions\"}\n" + " }\n" + "}\n"; + + fout << "\n\n"; + fout << "FileTypes {\n" + " CmakeRule {\n" + " name = \"Custom Rule\"\n" + " action = \"&Run\"\n" + " extensions = {\""; +#ifdef _WIN32 + fout << "bat"; +#else + fout << "sh"; +#endif + fout << "\"}\n" + " grepable = false\n" + " command = \"Custom Rule Command\"\n" + " commandLine = \"$COMMAND "; +#ifdef _WIN32 + fout << "/c"; +#endif + fout << " $INPUTFILE\"\n" + " progress = \"Processing Custom Rule\"\n" + " promoteToFirstPass = true\n" + " outputType = \"None\"\n" + " color = \"#800080\"\n" + " }\n" + "}\n"; +} - this->WriteMacros(fout); - this->WriteHighLevelDirectives(fout); +void cmGlobalGhsMultiGenerator::WriteCustomTargetBOD(std::ostream& fout) +{ + fout << "FileTypes {\n" + " CmakeTarget {\n" + " name = \"Custom Target\"\n" + " action = \"&Execute\"\n" + " grepable = false\n" + " outputType = \"None\"\n" + " color = \"#800080\"\n" + " }\n" + "}\n"; +} + +void cmGlobalGhsMultiGenerator::WriteTopLevelProject(std::ostream& fout, + cmLocalGenerator* root) +{ + this->WriteFileHeader(fout); + this->WriteMacros(fout, root); + this->WriteHighLevelDirectives(root, fout); GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fout); fout << "# Top Level Project File" << std::endl; @@ -298,71 +352,152 @@ void cmGlobalGhsMultiGenerator::WriteTopLevelProject( } fout << "\"" << this->OsDir << "\"" << std::endl; } - - WriteSubProjects(fout, root, generators); } -void cmGlobalGhsMultiGenerator::WriteSubProjects( - std::ostream& fout, cmLocalGenerator* root, - std::vector<cmLocalGenerator*>& generators) +void cmGlobalGhsMultiGenerator::WriteSubProjects(std::ostream& fout, + std::string& all_target) { - // Collect all targets under this root generator and the transitive - // closure of their dependencies. - TargetDependSet projectTargets; - TargetDependSet originalTargets; - this->GetTargetSets(projectTargets, originalTargets, root, generators); - OrderedTargetDependSet orderedProjectTargets(projectTargets, ""); - - // write out all the sub-projects - std::string rootBinaryDir = root->GetCurrentBinaryDirectory(); - for (cmGeneratorTarget const* target : orderedProjectTargets) { - if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + fout << "CMakeFiles/" << all_target << " [Project]" << std::endl; + // All known targets + for (cmGeneratorTarget const* target : this->ProjectTargets) { + if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY || + target->GetType() == cmStateEnums::MODULE_LIBRARY || + target->GetType() == cmStateEnums::SHARED_LIBRARY || + (target->GetType() == cmStateEnums::GLOBAL_TARGET && + target->GetName() != GetInstallTargetName())) { continue; } + fout << "CMakeFiles/" << target->GetName() + ".tgt" + FILE_EXTENSION + << " [Project]" << std::endl; + } +} - const char* projName = target->GetProperty("GENERATOR_FILE_NAME"); - const char* projType = target->GetProperty("GENERATOR_FILE_NAME_EXT"); - if (projName && projType) { - cmLocalGenerator* lg = target->GetLocalGenerator(); - std::string dir = lg->GetCurrentBinaryDirectory(); - dir = root->MaybeConvertToRelativePath(rootBinaryDir, dir); - if (dir == ".") { - dir.clear(); - } else { - if (dir.back() != '/') { - dir += "/"; - } +void cmGlobalGhsMultiGenerator::WriteProjectLine( + std::ostream& fout, cmGeneratorTarget const* target, cmLocalGenerator* root, + std::string& rootBinaryDir) +{ + const char* projName = target->GetProperty("GENERATOR_FILE_NAME"); + const char* projType = target->GetProperty("GENERATOR_FILE_NAME_EXT"); + if (projName && projType) { + cmLocalGenerator* lg = target->GetLocalGenerator(); + std::string dir = lg->GetCurrentBinaryDirectory(); + dir = root->MaybeConvertToRelativePath(rootBinaryDir, dir); + if (dir == ".") { + dir.clear(); + } else { + if (dir.back() != '/') { + dir += "/"; } + } - if (cmSystemTools::IsOn(target->GetProperty("EXCLUDE_FROM_ALL"))) { - fout << "{comment} "; - } - std::string projFile = dir + projName + FILE_EXTENSION; - fout << projFile; - fout << " " << projType << std::endl; + std::string projFile = dir + projName + FILE_EXTENSION; + fout << projFile; + fout << " " << projType << std::endl; + } else { + /* Should never happen */ + std::string message = + "The project file for target [" + target->GetName() + "] is missing.\n"; + cmSystemTools::Error(message); + fout << "{comment} " << target->GetName() << " [missing project file]\n"; + } +} - if (cmSystemTools::IsOn(target->GetProperty("GHS_REFERENCE_PROJECT"))) { - // create reference project - std::string fname = dir; - fname += target->GetName(); - fname += "REF"; - fname += FILE_EXTENSION; +void cmGlobalGhsMultiGenerator::WriteTargets(cmLocalGenerator* root) +{ + std::string rootBinaryDir = root->GetCurrentBinaryDirectory(); + rootBinaryDir += "/CMakeFiles"; + + // All known targets + for (cmGeneratorTarget const* target : this->ProjectTargets) { + if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY || + target->GetType() == cmStateEnums::MODULE_LIBRARY || + target->GetType() == cmStateEnums::SHARED_LIBRARY || + (target->GetType() == cmStateEnums::GLOBAL_TARGET && + target->GetName() != GetInstallTargetName())) { + continue; + } - cmGeneratedFileStream fref(fname); - fref.SetCopyIfDifferent(true); + // create target build file + std::string name = target->GetName() + ".tgt" + FILE_EXTENSION; + std::string fname = rootBinaryDir + "/" + name; + cmGeneratedFileStream fbld(fname); + fbld.SetCopyIfDifferent(true); + this->WriteFileHeader(fbld); + GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld); + std::vector<cmGeneratorTarget const*> build; + if (ComputeTargetBuildOrder(target, build)) { + std::string message = "The inter-target dependency graph for target [" + + target->GetName() + "] had a cycle.\n"; + cmSystemTools::Error(message); + } else { + for (auto& tgt : build) { + WriteProjectLine(fbld, tgt, root, rootBinaryDir); + } + } + fbld.Close(); + } +} - this->WriteFileHeader(fref); - GhsMultiGpj::WriteGpjTag(GhsMultiGpj::REFERENCE, fref); - fref << " :reference=" << projFile << std::endl; +void cmGlobalGhsMultiGenerator::WriteAllTarget( + cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators, + std::string& all_target) +{ + this->ProjectTargets.clear(); + + // create target build file + all_target = root->GetProjectName() + "." + this->GetAllTargetName() + + ".tgt" + FILE_EXTENSION; + std::string fname = + root->GetCurrentBinaryDirectory() + "/CMakeFiles/" + all_target; + cmGeneratedFileStream fbld(fname); + fbld.SetCopyIfDifferent(true); + this->WriteFileHeader(fbld); + GhsMultiGpj::WriteGpjTag(GhsMultiGpj::PROJECT, fbld); - fref.Close(); + // Collect all targets under this root generator and the transitive + // closure of their dependencies. + TargetDependSet projectTargets; + TargetDependSet originalTargets; + this->GetTargetSets(projectTargets, originalTargets, root, generators); + OrderedTargetDependSet sortedProjectTargets(projectTargets, ""); + std::vector<cmGeneratorTarget const*> defaultTargets; + for (cmGeneratorTarget const* t : sortedProjectTargets) { + /* save list of all targets in sorted order */ + this->ProjectTargets.push_back(t); + } + for (cmGeneratorTarget const* t : sortedProjectTargets) { + if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) { + continue; + } + if (!cmSystemTools::IsOn(t->GetProperty("EXCLUDE_FROM_ALL"))) { + defaultTargets.push_back(t); + } + } + std::vector<cmGeneratorTarget const*> build; + if (ComputeTargetBuildOrder(defaultTargets, build)) { + std::string message = "The inter-target dependency graph for project [" + + root->GetProjectName() + "] had a cycle.\n"; + cmSystemTools::Error(message); + } else { + // determine the targets for ALL target + std::string rootBinaryDir = root->GetCurrentBinaryDirectory(); + rootBinaryDir += "/CMakeFiles"; + for (cmGeneratorTarget const* target : build) { + if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY || + target->GetType() == cmStateEnums::MODULE_LIBRARY || + target->GetType() == cmStateEnums::SHARED_LIBRARY) { + continue; } + this->WriteProjectLine(fbld, target, root, rootBinaryDir); } } + fbld.Close(); } void cmGlobalGhsMultiGenerator::Generate() { + std::string fname; + // first do the superclass method this->cmGlobalGenerator::Generate(); @@ -370,11 +505,32 @@ void cmGlobalGhsMultiGenerator::Generate() for (auto& it : this->ProjectMap) { this->OutputTopLevelProject(it.second[0], it.second); } + + // create custom rule BOD file + fname = this->GetCMakeInstance()->GetHomeOutputDirectory() + + "/CMakeFiles/custom_rule.bod"; + cmGeneratedFileStream frule(fname); + frule.SetCopyIfDifferent(true); + this->WriteFileHeader(frule); + this->WriteCustomRuleBOD(frule); + frule.Close(); + + // create custom target BOD file + fname = this->GetCMakeInstance()->GetHomeOutputDirectory() + + "/CMakeFiles/custom_target.bod"; + cmGeneratedFileStream ftarget(fname); + ftarget.SetCopyIfDifferent(true); + this->WriteFileHeader(ftarget); + this->WriteCustomTargetBOD(ftarget); + ftarget.Close(); } void cmGlobalGhsMultiGenerator::OutputTopLevelProject( cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators) { + std::string fname; + std::string all_target; + if (generators.empty()) { return; } @@ -383,18 +539,21 @@ void cmGlobalGhsMultiGenerator::OutputTopLevelProject( * with target projects. This avoid the issue where the project has * the same name as the executable target. */ - std::string fname = root->GetCurrentBinaryDirectory(); + fname = root->GetCurrentBinaryDirectory(); fname += "/"; fname += root->GetProjectName(); fname += ".top"; fname += FILE_EXTENSION; - cmGeneratedFileStream fout(fname); - fout.SetCopyIfDifferent(true); + cmGeneratedFileStream top(fname); + top.SetCopyIfDifferent(true); + this->WriteTopLevelProject(top, root); - this->WriteTopLevelProject(fout, root, generators); + this->WriteAllTarget(root, generators, all_target); + this->WriteTargets(root); - fout.Close(); + this->WriteSubProjects(top, all_target); + top.Close(); } std::vector<cmGlobalGenerator::GeneratedMakeCommand> @@ -426,6 +585,9 @@ cmGlobalGhsMultiGenerator::GenerateBuildCommand( std::vector<std::string> files; cmSystemTools::Glob(projectDir, ".*\\.top\\.gpj", files); if (!files.empty()) { + /* if multiple top-projects are found in build directory + * then prefer projectName top-project. + */ auto p = std::find(files.begin(), files.end(), proj); if (p == files.end()) { proj = files.at(0); @@ -440,20 +602,24 @@ cmGlobalGhsMultiGenerator::GenerateBuildCommand( } else { for (const auto& tname : targetNames) { if (!tname.empty()) { - if (tname.compare(tname.size() - 4, 4, ".gpj") == 0) { - makeCommand.Add(tname); - } else { - makeCommand.Add(tname + ".gpj"); - } + makeCommand.Add(tname + ".tgt.gpj"); } } } + } else { + /* transform name to default build */; + std::string all = proj; + all.replace(all.end() - 7, all.end(), + std::string(this->GetAllTargetName()) + ".tgt.gpj"); + makeCommand.Add(all); } return { makeCommand }; } -void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout) +void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout, + cmLocalGenerator* root) { + fout << "macro PROJ_NAME=" << root->GetProjectName() << std::endl; char const* ghsGpjMacros = this->GetCMakeInstance()->GetCacheDefinition("GHS_GPJ_MACROS"); if (nullptr != ghsGpjMacros) { @@ -465,13 +631,14 @@ void cmGlobalGhsMultiGenerator::WriteMacros(std::ostream& fout) } } -void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(std::ostream& fout) +void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives( + cmLocalGenerator* root, std::ostream& fout) { /* set primary target */ std::string tgt; const char* t = this->GetCMakeInstance()->GetCacheDefinition("GHS_PRIMARY_TARGET"); - if (t) { + if (t && *t != '\0') { tgt = t; this->GetCMakeInstance()->MarkCliAsUsed("GHS_PRIMARY_TARGET"); } else { @@ -486,16 +653,20 @@ void cmGlobalGhsMultiGenerator::WriteHighLevelDirectives(std::ostream& fout) } fout << "primaryTarget=" << tgt << std::endl; + fout << "customization=" << root->GetBinaryDirectory() + << "/CMakeFiles/custom_rule.bod" << std::endl; + fout << "customization=" << root->GetBinaryDirectory() + << "/CMakeFiles/custom_target.bod" << std::endl; char const* const customization = this->GetCMakeInstance()->GetCacheDefinition("GHS_CUSTOMIZATION"); if (nullptr != customization && strlen(customization) > 0) { - fout << "customization=" << trimQuotes(customization) << std::endl; + fout << "customization=" << this->TrimQuotes(customization) << std::endl; this->GetCMakeInstance()->MarkCliAsUsed("GHS_CUSTOMIZATION"); } } -std::string cmGlobalGhsMultiGenerator::trimQuotes(std::string const& str) +std::string cmGlobalGhsMultiGenerator::TrimQuotes(std::string const& str) { std::string result; result.reserve(str.size()); @@ -528,3 +699,56 @@ cmGlobalGhsMultiGenerator::OrderedTargetDependSet::OrderedTargetDependSet( { this->insert(targets.begin(), targets.end()); } + +bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder( + cmGeneratorTarget const* tgt, std::vector<cmGeneratorTarget const*>& build) +{ + std::vector<cmGeneratorTarget const*> t{ tgt }; + return ComputeTargetBuildOrder(t, build); +} + +bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder( + std::vector<cmGeneratorTarget const*>& tgt, + std::vector<cmGeneratorTarget const*>& build) +{ + std::set<cmGeneratorTarget const*> temp; + std::set<cmGeneratorTarget const*> perm; + + for (auto ti : tgt) { + bool r = VisitTarget(temp, perm, build, ti); + if (r) { + return r; + } + } + return false; +} + +bool cmGlobalGhsMultiGenerator::VisitTarget( + std::set<cmGeneratorTarget const*>& temp, + std::set<cmGeneratorTarget const*>& perm, + std::vector<cmGeneratorTarget const*>& order, cmGeneratorTarget const* ti) +{ + /* check if permanent mark is set*/ + if (perm.find(ti) == perm.end()) { + /* set temporary mark; check if revisit*/ + if (temp.insert(ti).second) { + /* sort targets lexicographically to ensure that nodes are always visited + * in the same order */ + OrderedTargetDependSet sortedTargets(this->GetTargetDirectDepends(ti), + ""); + for (auto& di : sortedTargets) { + if (this->VisitTarget(temp, perm, order, di)) { + return true; + } + } + /* mark as complete; insert into beginning of list*/ + perm.insert(ti); + order.push_back(ti); + return false; + } + /* revisiting item - not a DAG */ + return true; + } + /* already complete */ + return false; +} diff --git a/Source/cmGlobalGhsMultiGenerator.h b/Source/cmGlobalGhsMultiGenerator.h index 1aeb1dc..98358c7 100644 --- a/Source/cmGlobalGhsMultiGenerator.h +++ b/Source/cmGlobalGhsMultiGenerator.h @@ -78,23 +78,7 @@ public: // Write the common disclaimer text at the top of each build file. void WriteFileHeader(std::ostream& fout); - // Target dependency sorting - class TargetSet : public std::set<cmGeneratorTarget const*> - { - }; - class TargetCompare - { - std::string First; - - public: - TargetCompare(std::string first) - : First(std::move(first)) - { - } - bool operator()(cmGeneratorTarget const* l, - cmGeneratorTarget const* r) const; - }; - class OrderedTargetDependSet; + const char* GetInstallTargetName() const override { return "install"; } protected: void Generate() override; @@ -111,18 +95,53 @@ private: /* top-level project */ void OutputTopLevelProject(cmLocalGenerator* root, std::vector<cmLocalGenerator*>& generators); - void WriteTopLevelProject(std::ostream& fout, cmLocalGenerator* root, - std::vector<cmLocalGenerator*>& generators); - void WriteMacros(std::ostream& fout); - void WriteHighLevelDirectives(std::ostream& fout); - void WriteSubProjects(std::ostream& fout, cmLocalGenerator* root, - std::vector<cmLocalGenerator*>& generators); - - std::string trimQuotes(std::string const& str); + void WriteTopLevelProject(std::ostream& fout, cmLocalGenerator* root); + void WriteMacros(std::ostream& fout, cmLocalGenerator* root); + void WriteHighLevelDirectives(cmLocalGenerator* root, std::ostream& fout); + void WriteSubProjects(std::ostream& fout, std::string& all_target); + void WriteTargets(cmLocalGenerator* root); + void WriteProjectLine(std::ostream& fout, cmGeneratorTarget const* target, + cmLocalGenerator* root, std::string& rootBinaryDir); + void WriteCustomRuleBOD(std::ostream& fout); + void WriteCustomTargetBOD(std::ostream& fout); + void WriteAllTarget(cmLocalGenerator* root, + std::vector<cmLocalGenerator*>& generators, + std::string& all_target); + + std::string TrimQuotes(std::string const& str); std::string OsDir; static const char* DEFAULT_BUILD_PROGRAM; static const char* DEFAULT_TOOLSET_ROOT; + + bool ComputeTargetBuildOrder(cmGeneratorTarget const* tgt, + std::vector<cmGeneratorTarget const*>& build); + bool ComputeTargetBuildOrder(std::vector<cmGeneratorTarget const*>& tgt, + std::vector<cmGeneratorTarget const*>& build); + bool VisitTarget(std::set<cmGeneratorTarget const*>& temp, + std::set<cmGeneratorTarget const*>& perm, + std::vector<cmGeneratorTarget const*>& order, + cmGeneratorTarget const* ti); + + std::vector<cmGeneratorTarget const*> ProjectTargets; + + // Target sorting + class TargetSet : public std::set<cmGeneratorTarget const*> + { + }; + class TargetCompare + { + std::string First; + + public: + TargetCompare(std::string first) + : First(std::move(first)) + { + } + bool operator()(cmGeneratorTarget const* l, + cmGeneratorTarget const* r) const; + }; + class OrderedTargetDependSet; }; class cmGlobalGhsMultiGenerator::OrderedTargetDependSet diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx index f115016..6fbea82 100644 --- a/Source/cmQtAutoGenerator.cxx +++ b/Source/cmQtAutoGenerator.cxx @@ -14,12 +14,6 @@ #include "cmSystemTools.h" #include "cmake.h" -#include <algorithm> -#include <sstream> -#include <utility> - -// -- Class methods - cmQtAutoGenerator::Logger::Logger() { // Initialize logger @@ -431,232 +425,6 @@ bool cmQtAutoGenerator::FileSystem::MakeParentDirectory( return cmQtAutoGenerator::MakeParentDirectory(filename); } -int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::init(uv_loop_t* uv_loop, - ReadOnlyProcessT* process) -{ - Process_ = process; - Target_ = nullptr; - return UVPipe_.init(*uv_loop, 0, this); -} - -int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::startRead(std::string* target) -{ - Target_ = target; - return uv_read_start(uv_stream(), &PipeT::UVAlloc, &PipeT::UVData); -} - -void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::reset() -{ - Process_ = nullptr; - Target_ = nullptr; - UVPipe_.reset(); - Buffer_.clear(); - Buffer_.shrink_to_fit(); -} - -void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVAlloc(uv_handle_t* handle, - size_t suggestedSize, - uv_buf_t* buf) -{ - auto& pipe = *reinterpret_cast<PipeT*>(handle->data); - pipe.Buffer_.resize(suggestedSize); - buf->base = pipe.Buffer_.data(); - buf->len = pipe.Buffer_.size(); -} - -void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVData(uv_stream_t* stream, - ssize_t nread, - const uv_buf_t* buf) -{ - auto& pipe = *reinterpret_cast<PipeT*>(stream->data); - if (nread > 0) { - // Append data to merged output - if ((buf->base != nullptr) && (pipe.Target_ != nullptr)) { - pipe.Target_->append(buf->base, nread); - } - } else if (nread < 0) { - // EOF or error - auto* proc = pipe.Process_; - // Check it this an unusual error - if (nread != UV_EOF) { - if (!proc->Result()->error()) { - proc->Result()->ErrorMessage = - "libuv reading from pipe failed with error code "; - proc->Result()->ErrorMessage += std::to_string(nread); - } - } - // Clear libuv pipe handle and try to finish - pipe.reset(); - proc->UVTryFinish(); - } -} - -void cmQtAutoGenerator::ProcessResultT::reset() -{ - ExitStatus = 0; - TermSignal = 0; - if (!StdOut.empty()) { - StdOut.clear(); - StdOut.shrink_to_fit(); - } - if (!StdErr.empty()) { - StdErr.clear(); - StdErr.shrink_to_fit(); - } - if (!ErrorMessage.empty()) { - ErrorMessage.clear(); - ErrorMessage.shrink_to_fit(); - } -} - -void cmQtAutoGenerator::ReadOnlyProcessT::setup( - ProcessResultT* result, bool mergedOutput, - std::vector<std::string> const& command, std::string const& workingDirectory) -{ - Setup_.WorkingDirectory = workingDirectory; - Setup_.Command = command; - Setup_.Result = result; - Setup_.MergedOutput = mergedOutput; -} - -static std::string getUVError(const char* prefixString, int uvErrorCode) -{ - std::ostringstream ost; - ost << prefixString << ": " << uv_strerror(uvErrorCode); - return ost.str(); -} - -bool cmQtAutoGenerator::ReadOnlyProcessT::start( - uv_loop_t* uv_loop, std::function<void()>&& finishedCallback) -{ - if (IsStarted() || (Result() == nullptr)) { - return false; - } - - // Reset result before the start - Result()->reset(); - - // Fill command string pointers - if (!Setup().Command.empty()) { - CommandPtr_.reserve(Setup().Command.size() + 1); - for (std::string const& arg : Setup().Command) { - CommandPtr_.push_back(arg.c_str()); - } - CommandPtr_.push_back(nullptr); - } else { - Result()->ErrorMessage = "Empty command"; - } - - if (!Result()->error()) { - if (UVPipeOut_.init(uv_loop, this) != 0) { - Result()->ErrorMessage = "libuv stdout pipe initialization failed"; - } - } - if (!Result()->error()) { - if (UVPipeErr_.init(uv_loop, this) != 0) { - Result()->ErrorMessage = "libuv stderr pipe initialization failed"; - } - } - if (!Result()->error()) { - // -- Setup process stdio options - // stdin - UVOptionsStdIO_[0].flags = UV_IGNORE; - UVOptionsStdIO_[0].data.stream = nullptr; - // stdout - UVOptionsStdIO_[1].flags = - static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); - UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream(); - // stderr - UVOptionsStdIO_[2].flags = - static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); - UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream(); - - // -- Setup process options - std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0); - UVOptions_.exit_cb = &ReadOnlyProcessT::UVExit; - UVOptions_.file = CommandPtr_[0]; - UVOptions_.args = const_cast<char**>(CommandPtr_.data()); - UVOptions_.cwd = Setup_.WorkingDirectory.c_str(); - UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE; - UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size()); - UVOptions_.stdio = UVOptionsStdIO_.data(); - - // -- Spawn process - int uvErrorCode = UVProcess_.spawn(*uv_loop, UVOptions_, this); - if (uvErrorCode != 0) { - Result()->ErrorMessage = - getUVError("libuv process spawn failed ", uvErrorCode); - } - } - // -- Start reading from stdio streams - if (!Result()->error()) { - if (UVPipeOut_.startRead(&Result()->StdOut) != 0) { - Result()->ErrorMessage = "libuv start reading from stdout pipe failed"; - } - } - if (!Result()->error()) { - if (UVPipeErr_.startRead(Setup_.MergedOutput ? &Result()->StdOut - : &Result()->StdErr) != 0) { - Result()->ErrorMessage = "libuv start reading from stderr pipe failed"; - } - } - - if (!Result()->error()) { - IsStarted_ = true; - FinishedCallback_ = std::move(finishedCallback); - } else { - // Clear libuv handles and finish - UVProcess_.reset(); - UVPipeOut_.reset(); - UVPipeErr_.reset(); - CommandPtr_.clear(); - } - - return IsStarted(); -} - -void cmQtAutoGenerator::ReadOnlyProcessT::UVExit(uv_process_t* handle, - int64_t exitStatus, - int termSignal) -{ - auto& proc = *reinterpret_cast<ReadOnlyProcessT*>(handle->data); - if (proc.IsStarted() && !proc.IsFinished()) { - // Set error message on demand - proc.Result()->ExitStatus = exitStatus; - proc.Result()->TermSignal = termSignal; - if (!proc.Result()->error()) { - if (termSignal != 0) { - proc.Result()->ErrorMessage = "Process was terminated by signal "; - proc.Result()->ErrorMessage += - std::to_string(proc.Result()->TermSignal); - } else if (exitStatus != 0) { - proc.Result()->ErrorMessage = "Process failed with return value "; - proc.Result()->ErrorMessage += - std::to_string(proc.Result()->ExitStatus); - } - } - - // Reset process handle and try to finish - proc.UVProcess_.reset(); - proc.UVTryFinish(); - } -} - -void cmQtAutoGenerator::ReadOnlyProcessT::UVTryFinish() -{ - // There still might be data in the pipes after the process has finished. - // Therefore check if the process is finished AND all pipes are closed - // before signaling the worker thread to continue. - if (UVProcess_.get() == nullptr) { - if (UVPipeOut_.uv_pipe() == nullptr) { - if (UVPipeErr_.uv_pipe() == nullptr) { - IsFinished_ = true; - FinishedCallback_(); - } - } - } -} - cmQtAutoGenerator::cmQtAutoGenerator() = default; cmQtAutoGenerator::~cmQtAutoGenerator() = default; diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h index 479d357..437fa20 100644 --- a/Source/cmQtAutoGenerator.h +++ b/Source/cmQtAutoGenerator.h @@ -7,14 +7,8 @@ #include "cmFilePathChecksum.h" #include "cmQtAutoGen.h" -#include "cmUVHandlePtr.h" -#include "cm_uv.h" -#include <array> -#include <functional> #include <mutex> -#include <stddef.h> -#include <stdint.h> #include <string> #include <vector> @@ -137,102 +131,6 @@ public: cmFilePathChecksum FilePathChecksum_; }; - /// @brief Return value and output of an external process - struct ProcessResultT - { - void reset(); - bool error() const - { - return (ExitStatus != 0) || (TermSignal != 0) || !ErrorMessage.empty(); - } - - std::int64_t ExitStatus = 0; - int TermSignal = 0; - std::string StdOut; - std::string StdErr; - std::string ErrorMessage; - }; - - /// @brief External process management class - struct ReadOnlyProcessT - { - // -- Types - - /// @brief libuv pipe buffer class - class PipeT - { - public: - int init(uv_loop_t* uv_loop, ReadOnlyProcessT* process); - int startRead(std::string* target); - void reset(); - - // -- Libuv casts - uv_pipe_t* uv_pipe() { return UVPipe_.get(); } - uv_stream_t* uv_stream() - { - return reinterpret_cast<uv_stream_t*>(uv_pipe()); - } - uv_handle_t* uv_handle() - { - return reinterpret_cast<uv_handle_t*>(uv_pipe()); - } - - // -- Libuv callbacks - static void UVAlloc(uv_handle_t* handle, size_t suggestedSize, - uv_buf_t* buf); - static void UVData(uv_stream_t* stream, ssize_t nread, - const uv_buf_t* buf); - - private: - ReadOnlyProcessT* Process_ = nullptr; - std::string* Target_ = nullptr; - std::vector<char> Buffer_; - cm::uv_pipe_ptr UVPipe_; - }; - - /// @brief Process settings - struct SetupT - { - std::string WorkingDirectory; - std::vector<std::string> Command; - ProcessResultT* Result = nullptr; - bool MergedOutput = false; - }; - - // -- Const accessors - const SetupT& Setup() const { return Setup_; } - ProcessResultT* Result() const { return Setup_.Result; } - bool IsStarted() const { return IsStarted_; } - bool IsFinished() const { return IsFinished_; } - - // -- Runtime - void setup(ProcessResultT* result, bool mergedOutput, - std::vector<std::string> const& command, - std::string const& workingDirectory = std::string()); - bool start(uv_loop_t* uv_loop, std::function<void()>&& finishedCallback); - - private: - // -- Friends - friend class PipeT; - // -- Libuv callbacks - static void UVExit(uv_process_t* handle, int64_t exitStatus, - int termSignal); - void UVTryFinish(); - - // -- Setup - SetupT Setup_; - // -- Runtime - bool IsStarted_ = false; - bool IsFinished_ = false; - std::function<void()> FinishedCallback_; - std::vector<const char*> CommandPtr_; - std::array<uv_stdio_container_t, 3> UVOptionsStdIO_; - uv_process_options_t UVOptions_; - cm::uv_process_ptr UVProcess_; - PipeT UVPipeOut_; - PipeT UVPipeErr_; - }; - public: // -- Constructors cmQtAutoGenerator(); diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoMocUic.cxx index ec1a1aa..75c5d8a 100644 --- a/Source/cmQtAutoGeneratorMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -1,10 +1,10 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmQtAutoGeneratorMocUic.h" +#include "cmQtAutoMocUic.h" #include <algorithm> #include <array> -#include <cstddef> +#include <deque> #include <list> #include <memory> #include <set> @@ -24,7 +24,7 @@ // -- Class methods -std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath( +std::string cmQtAutoMocUic::BaseSettingsT::AbsoluteBuildPath( std::string const& relativePath) const { return FileSys->CollapseFullPath(relativePath, AutogenBuildDir); @@ -35,7 +35,7 @@ std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath( * appending different header extensions * @return True on success */ -bool cmQtAutoGeneratorMocUic::BaseSettingsT::FindHeader( +bool cmQtAutoMocUic::BaseSettingsT::FindHeader( std::string& header, std::string const& testBasePath) const { for (std::string const& ext : HeaderExtensions) { @@ -50,8 +50,7 @@ bool cmQtAutoGeneratorMocUic::BaseSettingsT::FindHeader( return false; } -bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped( - std::string const& fileName) const +bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const { return (!Enabled || (SkipList.find(fileName) != SkipList.end())); } @@ -60,7 +59,7 @@ bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped( * @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 cmQtAutoGeneratorMocUic::MocSettingsT::FindMacro( +std::string cmQtAutoMocUic::MocSettingsT::FindMacro( std::string const& content) const { for (KeyExpT const& filter : MacroFilters) { @@ -77,7 +76,7 @@ std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindMacro( return std::string(); } -std::string cmQtAutoGeneratorMocUic::MocSettingsT::MacrosString() const +std::string cmQtAutoMocUic::MocSettingsT::MacrosString() const { std::string res; const auto itB = MacroFilters.cbegin(); @@ -99,7 +98,7 @@ std::string cmQtAutoGeneratorMocUic::MocSettingsT::MacrosString() const return res; } -std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindIncludedFile( +std::string cmQtAutoMocUic::MocSettingsT::FindIncludedFile( std::string const& sourcePath, std::string const& includeString) const { // Search in vicinity of the source @@ -123,7 +122,7 @@ std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindIncludedFile( return std::string(); } -void cmQtAutoGeneratorMocUic::MocSettingsT::FindDependencies( +void cmQtAutoMocUic::MocSettingsT::FindDependencies( std::string const& content, std::set<std::string>& depends) const { if (!DependFilters.empty() && !content.empty()) { @@ -147,17 +146,124 @@ void cmQtAutoGeneratorMocUic::MocSettingsT::FindDependencies( } } -bool cmQtAutoGeneratorMocUic::UicSettingsT::skipped( - std::string const& fileName) const +bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const { return (!Enabled || (SkipList.find(fileName) != SkipList.end())); } -void cmQtAutoGeneratorMocUic::JobParseT::Process(WorkerT& wrk) +void cmQtAutoMocUic::JobT::LogError(GenT genType, + std::string const& message) const +{ + Gen()->AbortError(); + Gen()->Log().Error(genType, message); +} + +void cmQtAutoMocUic::JobT::LogFileError(GenT genType, + std::string const& filename, + std::string const& message) const +{ + Gen()->AbortError(); + Gen()->Log().ErrorFile(genType, filename, message); +} + +void cmQtAutoMocUic::JobT::LogCommandError( + GenT genType, std::string const& message, + std::vector<std::string> const& command, std::string const& output) const +{ + Gen()->AbortError(); + Gen()->Log().ErrorCommand(genType, message, command, output); +} + +bool cmQtAutoMocUic::JobT::RunProcess(GenT genType, + cmWorkerPool::ProcessResultT& result, + std::vector<std::string> const& command) +{ + // Log command + if (Log().Verbose()) { + std::string msg = "Running command:\n"; + msg += QuotedCommand(command); + msg += '\n'; + Log().Info(genType, msg); + } + return cmWorkerPool::JobT::RunProcess(result, command, + Gen()->Base().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; + } + if (generate) { + cmWorkerPool::ProcessResultT result; + { + // Compose command + std::vector<std::string> cmd = Gen()->Moc().PredefsCmd; + // Add includes + cmd.insert(cmd.end(), Gen()->Moc().Includes.begin(), + Gen()->Moc().Includes.end()); + // Add definitions + for (std::string const& def : Gen()->Moc().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); + } + } + + // (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); + } + } + } +} + +void cmQtAutoMocUic::JobParseT::Process() { if (AutoMoc && Header) { // Don't parse header for moc if the file is included by a source already - if (wrk.Gen().ParallelMocIncluded(FileName)) { + if (Gen()->ParallelMocIncluded(FileName)) { AutoMoc = false; } } @@ -165,35 +271,32 @@ void cmQtAutoGeneratorMocUic::JobParseT::Process(WorkerT& wrk) if (AutoMoc || AutoUic) { std::string error; MetaT meta; - if (wrk.FileSys().FileRead(meta.Content, FileName, &error)) { + if (FileSys().FileRead(meta.Content, FileName, &error)) { if (!meta.Content.empty()) { - meta.FileDir = wrk.FileSys().SubDirPrefix(FileName); - meta.FileBase = - wrk.FileSys().GetFilenameWithoutLastExtension(FileName); + meta.FileDir = FileSys().SubDirPrefix(FileName); + meta.FileBase = FileSys().GetFilenameWithoutLastExtension(FileName); bool success = true; if (AutoMoc) { if (Header) { - success = ParseMocHeader(wrk, meta); + success = ParseMocHeader(meta); } else { - success = ParseMocSource(wrk, meta); + success = ParseMocSource(meta); } } if (AutoUic && success) { - ParseUic(wrk, meta); + ParseUic(meta); } } else { - wrk.LogFileWarning(GenT::GEN, FileName, "The source file is empty"); + Log().WarningFile(GenT::GEN, FileName, "The source file is empty"); } } else { - wrk.LogFileError(GenT::GEN, FileName, - "Could not read the file: " + error); + LogFileError(GenT::GEN, FileName, "Could not read the file: " + error); } } } -bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, - MetaT const& meta) +bool cmQtAutoMocUic::JobParseT::ParseMocSource(MetaT const& meta) { struct JobPre { @@ -211,7 +314,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, }; // Check if this source file contains a relevant macro - std::string const ownMacro = wrk.Moc().FindMacro(meta.Content); + std::string const ownMacro = Gen()->Moc().FindMacro(meta.Content); // Extract moc includes from file std::deque<MocInclude> mocIncsUsc; @@ -220,11 +323,11 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, if (meta.Content.find("moc") != std::string::npos) { const char* contentChars = meta.Content.c_str(); cmsys::RegularExpressionMatch match; - while (wrk.Moc().RegExpInclude.find(contentChars, match)) { + while (Gen()->Moc().RegExpInclude.find(contentChars, match)) { std::string incString = match.match(2); - std::string incDir(wrk.FileSys().SubDirPrefix(incString)); + std::string incDir(FileSys().SubDirPrefix(incString)); std::string incBase = - wrk.FileSys().GetFilenameWithoutLastExtension(incString); + FileSys().GetFilenameWithoutLastExtension(incString); if (cmHasLiteralPrefix(incBase, "moc_")) { // moc_<BASE>.cxx // Remove the moc_ part from the base name @@ -253,10 +356,10 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, // Process moc_<BASE>.cxx includes for (const MocInclude& mocInc : mocIncsUsc) { std::string const header = - MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base); + MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base); if (!header.empty()) { // Check if header is skipped - if (wrk.Moc().skipped(header)) { + if (Gen()->Moc().skipped(header)) { continue; } // Register moc job @@ -271,9 +374,9 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, std::string emsg = "The file includes the moc file "; emsg += Quoted(mocInc.Inc); emsg += ", but the header "; - emsg += Quoted(MocStringHeaders(wrk, mocInc.Base)); + emsg += Quoted(MocStringHeaders(mocInc.Base)); emsg += " could not be found."; - wrk.LogFileError(GenT::MOC, FileName, emsg); + LogFileError(GenT::MOC, FileName, emsg); } return false; } @@ -282,7 +385,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, // Process <BASE>.moc includes for (const MocInclude& mocInc : mocIncsDot) { const bool ownMoc = (mocInc.Base == meta.FileBase); - if (wrk.Moc().RelaxedMode) { + if (Gen()->Moc().RelaxedMode) { // Relaxed mode if (!ownMacro.empty() && ownMoc) { // Add self @@ -292,10 +395,10 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, // In relaxed mode try to find a header instead but issue a warning. // This is for KDE4 compatibility std::string const header = - MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base); + MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base); if (!header.empty()) { // Check if header is skipped - if (wrk.Moc().skipped(header)) { + if (Gen()->Moc().skipped(header)) { continue; } // Register moc job @@ -305,14 +408,14 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, std::string emsg = "The file includes the moc file "; emsg += Quoted(mocInc.Inc); emsg += ", but does not contain a "; - emsg += wrk.Moc().MacrosString(); + 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"; - wrk.LogFileWarning(GenT::MOC, FileName, emsg); + Log().WarningFile(GenT::MOC, FileName, emsg); } else { std::string emsg = "The file includes the moc file "; emsg += Quoted(mocInc.Inc); @@ -324,7 +427,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, emsg += Quoted("moc_" + mocInc.Base + ".cpp"); emsg += " for compatibility with strict mode.\n" "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - wrk.LogFileWarning(GenT::MOC, FileName, emsg); + Log().WarningFile(GenT::MOC, FileName, emsg); } } } else { @@ -334,9 +437,9 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, emsg += ", which seems to be the moc file from a different " "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a " "matching header "; - emsg += Quoted(MocStringHeaders(wrk, mocInc.Base)); + emsg += Quoted(MocStringHeaders(mocInc.Base)); emsg += " could not be found."; - wrk.LogFileError(GenT::MOC, FileName, emsg); + LogFileError(GenT::MOC, FileName, emsg); } return false; } @@ -352,9 +455,9 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, std::string emsg = "The file includes the moc file "; emsg += Quoted(mocInc.Inc); emsg += ", but does not contain a "; - emsg += wrk.Moc().MacrosString(); + emsg += Gen()->Moc().MacrosString(); emsg += " macro."; - wrk.LogFileWarning(GenT::MOC, FileName, emsg); + Log().WarningFile(GenT::MOC, FileName, emsg); } } else { // Don't allow <BASE>.moc include other than self in strict mode @@ -365,7 +468,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, "source file.\nThis is not supported. Include "; emsg += Quoted(meta.FileBase + ".moc"); emsg += " to run moc on this source file."; - wrk.LogFileError(GenT::MOC, FileName, emsg); + LogFileError(GenT::MOC, FileName, emsg); } return false; } @@ -379,7 +482,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, // 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 (wrk.Moc().RelaxedMode && ownMocUscIncluded) { + if (Gen()->Moc().RelaxedMode && ownMocUscIncluded) { JobPre uscJobPre; // Remove underscore job request { @@ -408,7 +511,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, emsg += Quoted(meta.FileBase + ".moc"); emsg += " for compatibility with strict mode.\n" "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; - wrk.LogFileWarning(GenT::MOC, FileName, emsg); + Log().WarningFile(GenT::MOC, FileName, emsg); } // Add own source job jobs.emplace_back( @@ -423,7 +526,7 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, emsg += "!\nConsider to\n - add #include \""; emsg += meta.FileBase; emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; - wrk.LogFileError(GenT::MOC, FileName, emsg); + LogFileError(GenT::MOC, FileName, emsg); } return false; } @@ -431,76 +534,80 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, // Convert pre jobs to actual jobs for (JobPre& jobPre : jobs) { - JobHandleT jobHandle = cm::make_unique<JobMocT>( + cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>( std::move(jobPre.SourceFile), FileName, std::move(jobPre.IncludeString)); if (jobPre.self) { // Read dependencies from this source - static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content); + JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle); + Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends); + jobMoc.DependsValid = true; } - if (!wrk.Gen().ParallelJobPushMoc(jobHandle)) { + if (!Gen()->ParallelJobPushMoc(std::move(jobHandle))) { return false; } } return true; } -bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocHeader(WorkerT& wrk, - MetaT const& meta) +bool cmQtAutoMocUic::JobParseT::ParseMocHeader(MetaT const& meta) { bool success = true; - std::string const macroName = wrk.Moc().FindMacro(meta.Content); + std::string const macroName = Gen()->Moc().FindMacro(meta.Content); if (!macroName.empty()) { - JobHandleT jobHandle = cm::make_unique<JobMocT>( + cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>( std::string(FileName), std::string(), std::string()); // Read dependencies from this source - static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content); - success = wrk.Gen().ParallelJobPushMoc(jobHandle); + { + JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle); + Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends); + jobMoc.DependsValid = true; + } + success = Gen()->ParallelJobPushMoc(std::move(jobHandle)); } return success; } -std::string cmQtAutoGeneratorMocUic::JobParseT::MocStringHeaders( - WorkerT& wrk, std::string const& fileBase) const +std::string cmQtAutoMocUic::JobParseT::MocStringHeaders( + std::string const& fileBase) const { std::string res = fileBase; res += ".{"; - res += cmJoin(wrk.Base().HeaderExtensions, ","); + res += cmJoin(Gen()->Base().HeaderExtensions, ","); res += "}"; return res; } -std::string cmQtAutoGeneratorMocUic::JobParseT::MocFindIncludedHeader( - WorkerT& wrk, std::string const& includerDir, std::string const& includeBase) +std::string cmQtAutoMocUic::JobParseT::MocFindIncludedHeader( + std::string const& includerDir, std::string const& includeBase) { std::string header; // Search in vicinity of the source - if (!wrk.Base().FindHeader(header, includerDir + includeBase)) { + if (!Gen()->Base().FindHeader(header, includerDir + includeBase)) { // Search in include directories - for (std::string const& path : wrk.Moc().IncludePaths) { + for (std::string const& path : Gen()->Moc().IncludePaths) { std::string fullPath = path; fullPath.push_back('/'); fullPath += includeBase; - if (wrk.Base().FindHeader(header, fullPath)) { + if (Gen()->Base().FindHeader(header, fullPath)) { break; } } } // Sanitize if (!header.empty()) { - header = wrk.FileSys().GetRealPath(header); + header = FileSys().GetRealPath(header); } return header; } -bool cmQtAutoGeneratorMocUic::JobParseT::ParseUic(WorkerT& wrk, - MetaT const& meta) +bool cmQtAutoMocUic::JobParseT::ParseUic(MetaT const& meta) { bool success = true; if (meta.Content.find("ui_") != std::string::npos) { const char* contentChars = meta.Content.c_str(); cmsys::RegularExpressionMatch match; - while (wrk.Uic().RegExpInclude.find(contentChars, match)) { - if (!ParseUicInclude(wrk, meta, match.match(2))) { + while (Gen()->Uic().RegExpInclude.find(contentChars, match)) { + if (!ParseUicInclude(meta, match.match(2))) { success = false; break; } @@ -510,16 +617,16 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseUic(WorkerT& wrk, return success; } -bool cmQtAutoGeneratorMocUic::JobParseT::ParseUicInclude( - WorkerT& wrk, MetaT const& meta, std::string&& includeString) +bool cmQtAutoMocUic::JobParseT::ParseUicInclude(MetaT const& meta, + std::string&& includeString) { bool success = false; - std::string uiInputFile = UicFindIncludedFile(wrk, meta, includeString); + std::string uiInputFile = UicFindIncludedFile(meta, includeString); if (!uiInputFile.empty()) { - if (!wrk.Uic().skipped(uiInputFile)) { - JobHandleT jobHandle = cm::make_unique<JobUicT>( + if (!Gen()->Uic().skipped(uiInputFile)) { + cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobUicT>( std::move(uiInputFile), FileName, std::move(includeString)); - success = wrk.Gen().ParallelJobPushUic(jobHandle); + success = Gen()->ParallelJobPushUic(std::move(jobHandle)); } else { // A skipped file is successful success = true; @@ -528,17 +635,17 @@ bool cmQtAutoGeneratorMocUic::JobParseT::ParseUicInclude( return success; } -std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile( - WorkerT& wrk, MetaT const& meta, std::string const& includeString) +std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile( + MetaT const& meta, std::string const& includeString) { std::string res; std::string searchFile = - wrk.FileSys().GetFilenameWithoutLastExtension(includeString).substr(3); + FileSys().GetFilenameWithoutLastExtension(includeString).substr(3); searchFile += ".ui"; // Collect search paths list std::deque<std::string> testFiles; { - std::string const searchPath = wrk.FileSys().SubDirPrefix(includeString); + std::string const searchPath = FileSys().SubDirPrefix(includeString); std::string searchFileFull; if (!searchPath.empty()) { @@ -554,12 +661,12 @@ std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile( } } // AUTOUIC search paths - if (!wrk.Uic().SearchPaths.empty()) { - for (std::string const& sPath : wrk.Uic().SearchPaths) { + if (!Gen()->Uic().SearchPaths.empty()) { + for (std::string const& sPath : Gen()->Uic().SearchPaths) { testFiles.push_back((sPath + "/").append(searchFile)); } if (!searchPath.empty()) { - for (std::string const& sPath : wrk.Uic().SearchPaths) { + for (std::string const& sPath : Gen()->Uic().SearchPaths) { testFiles.push_back((sPath + "/").append(searchFileFull)); } } @@ -568,8 +675,8 @@ std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile( // Search for the .ui file! for (std::string const& testFile : testFiles) { - if (wrk.FileSys().FileExists(testFile)) { - res = wrk.FileSys().GetRealPath(testFile); + if (FileSys().FileExists(testFile)) { + res = FileSys().GetRealPath(testFile); break; } } @@ -584,162 +691,140 @@ std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile( emsg += Quoted(testFile); emsg += "\n"; } - wrk.LogFileError(GenT::UIC, FileName, emsg); + LogFileError(GenT::UIC, FileName, emsg); } return res; } -void cmQtAutoGeneratorMocUic::JobMocPredefsT::Process(WorkerT& wrk) +void cmQtAutoMocUic::JobPostParseT::Process() { - // (Re)generate moc_predefs.h on demand - bool generate(false); - bool fileExists(wrk.FileSys().FileExists(wrk.Moc().PredefsFileAbs)); - if (!fileExists) { - if (wrk.Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(wrk.Moc().PredefsFileRel); - reason += " because it doesn't exist"; - wrk.LogInfo(GenT::MOC, reason); - } - generate = true; - } else if (wrk.Moc().SettingsChanged) { - if (wrk.Log().Verbose()) { - std::string reason = "Generating "; - reason += Quoted(wrk.Moc().PredefsFileRel); - reason += " because the settings changed."; - wrk.LogInfo(GenT::MOC, reason); - } - generate = true; + if (Gen()->Moc().Enabled) { + // Add mocs compilations fence job + Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>(); } - if (generate) { - ProcessResultT result; - { - // Compose command - std::vector<std::string> cmd = wrk.Moc().PredefsCmd; - // Add includes - cmd.insert(cmd.end(), wrk.Moc().Includes.begin(), - wrk.Moc().Includes.end()); - // Add definitions - for (std::string const& def : wrk.Moc().Definitions) { - cmd.push_back("-D" + def); - } - // Execute command - if (!wrk.RunProcess(GenT::MOC, result, cmd)) { - std::string emsg = "The content generation command for "; - emsg += Quoted(wrk.Moc().PredefsFileRel); - emsg += " failed.\n"; - emsg += result.ErrorMessage; - wrk.LogCommandError(GenT::MOC, emsg, cmd, result.StdOut); - } + // Add finish job + Gen()->WorkerPool().EmplaceJob<JobFinishT>(); +} + +void cmQtAutoMocUic::JobMocsCompilationT::Process() +{ + // 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'; } + } - // (Re)write predefs file only on demand - if (!result.error()) { - if (!fileExists || - wrk.FileSys().FileDiffers(wrk.Moc().PredefsFileAbs, result.StdOut)) { - std::string error; - if (wrk.FileSys().FileWrite(wrk.Moc().PredefsFileAbs, result.StdOut, - &error)) { - // Success - } else { - std::string emsg = "Writing "; - emsg += Quoted(wrk.Moc().PredefsFileRel); - emsg += " failed. "; - emsg += error; - wrk.LogFileError(GenT::MOC, wrk.Moc().PredefsFileAbs, emsg); - } - } else { - // Touch to update the time stamp - if (wrk.Log().Verbose()) { - std::string msg = "Touching "; - msg += Quoted(wrk.Moc().PredefsFileRel); - msg += "."; - wrk.LogInfo(GenT::MOC, msg); - } - wrk.FileSys().Touch(wrk.Moc().PredefsFileAbs); - } + 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."); + } + } else if (Gen()->MocAutoFileUpdated()) { + // Only touch mocs compilation file + if (Log().Verbose()) { + Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs); } + FileSys().Touch(compAbs); } } -void cmQtAutoGeneratorMocUic::JobMocT::FindDependencies( - WorkerT& wrk, std::string const& content) +void cmQtAutoMocUic::JobMocT::FindDependencies(std::string const& content) { - wrk.Moc().FindDependencies(content, Depends); + Gen()->Moc().FindDependencies(content, Depends); DependsValid = true; } -void cmQtAutoGeneratorMocUic::JobMocT::Process(WorkerT& wrk) +void cmQtAutoMocUic::JobMocT::Process() { // Compute build file name if (!IncludeString.empty()) { - BuildFile = wrk.Base().AutogenIncludeDir; + BuildFile = Gen()->Base().AutogenIncludeDir; BuildFile += '/'; BuildFile += IncludeString; } else { // Relative build path - std::string relPath = wrk.FileSys().GetFilePathChecksum(SourceFile); + std::string relPath = FileSys().GetFilePathChecksum(SourceFile); relPath += "/moc_"; - relPath += wrk.FileSys().GetFilenameWithoutLastExtension(SourceFile); + relPath += FileSys().GetFilenameWithoutLastExtension(SourceFile); // Register relative file path with duplication check - relPath = wrk.Gen().ParallelMocAutoRegister(relPath); + relPath = Gen()->ParallelMocAutoRegister(relPath); // Absolute build path - if (wrk.Base().MultiConfig) { - BuildFile = wrk.Base().AutogenIncludeDir; + if (Gen()->Base().MultiConfig) { + BuildFile = Gen()->Base().AutogenIncludeDir; BuildFile += '/'; BuildFile += relPath; } else { - BuildFile = wrk.Base().AbsoluteBuildPath(relPath); + BuildFile = Gen()->Base().AbsoluteBuildPath(relPath); } } - if (UpdateRequired(wrk)) { - GenerateMoc(wrk); + if (UpdateRequired()) { + GenerateMoc(); } } -bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) +bool cmQtAutoMocUic::JobMocT::UpdateRequired() { - bool const verbose = wrk.Gen().Log().Verbose(); + bool const verbose = Log().Verbose(); // Test if the build file exists - if (!wrk.FileSys().FileExists(BuildFile)) { + 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"; - wrk.LogInfo(GenT::MOC, reason); + Log().Info(GenT::MOC, reason); } return true; } // Test if any setting changed - if (wrk.Moc().SettingsChanged) { + if (Gen()->Moc().SettingsChanged) { if (verbose) { std::string reason = "Generating "; reason += Quoted(BuildFile); reason += " from "; reason += Quoted(SourceFile); reason += " because the MOC settings changed"; - wrk.LogInfo(GenT::MOC, reason); + Log().Info(GenT::MOC, reason); } return true; } // Test if the moc_predefs file is newer - if (!wrk.Moc().PredefsFileAbs.empty()) { + if (!Gen()->Moc().PredefsFileAbs.empty()) { bool isOlder = false; { std::string error; - isOlder = wrk.FileSys().FileIsOlderThan( - BuildFile, wrk.Moc().PredefsFileAbs, &error); + isOlder = FileSys().FileIsOlderThan(BuildFile, + Gen()->Moc().PredefsFileAbs, &error); if (!isOlder && !error.empty()) { - wrk.LogError(GenT::MOC, error); + LogError(GenT::MOC, error); return false; } } @@ -748,8 +833,8 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) std::string reason = "Generating "; reason += Quoted(BuildFile); reason += " because it's older than: "; - reason += Quoted(wrk.Moc().PredefsFileAbs); - wrk.LogInfo(GenT::MOC, reason); + reason += Quoted(Gen()->Moc().PredefsFileAbs); + Log().Info(GenT::MOC, reason); } return true; } @@ -760,9 +845,9 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) bool isOlder = false; { std::string error; - isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); + isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); if (!isOlder && !error.empty()) { - wrk.LogError(GenT::MOC, error); + LogError(GenT::MOC, error); return false; } } @@ -772,7 +857,7 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) reason += Quoted(BuildFile); reason += " because it's older than its source file "; reason += Quoted(SourceFile); - wrk.LogInfo(GenT::MOC, reason); + Log().Info(GenT::MOC, reason); } return true; } @@ -785,7 +870,7 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) std::string content; { std::string error; - if (!wrk.FileSys().FileRead(content, SourceFile, &error)) { + if (!FileSys().FileRead(content, SourceFile, &error)) { std::string emsg = "Could not read file\n "; emsg += Quoted(SourceFile); emsg += "\nrequired by moc include "; @@ -794,20 +879,20 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) emsg += Quoted(IncluderFile); emsg += ".\n"; emsg += error; - wrk.LogError(GenT::MOC, emsg); + LogError(GenT::MOC, emsg); return false; } } - FindDependencies(wrk, content); + FindDependencies(content); } // Check dependency timestamps std::string error; - std::string sourceDir = wrk.FileSys().SubDirPrefix(SourceFile); + std::string sourceDir = FileSys().SubDirPrefix(SourceFile); for (std::string const& depFileRel : Depends) { std::string depFileAbs = - wrk.Moc().FindIncludedFile(sourceDir, depFileRel); + Gen()->Moc().FindIncludedFile(sourceDir, depFileRel); if (!depFileAbs.empty()) { - if (wrk.FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) { + if (FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) { if (verbose) { std::string reason = "Generating "; reason += Quoted(BuildFile); @@ -815,18 +900,18 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) reason += Quoted(SourceFile); reason += " because it is older than it's dependency file "; reason += Quoted(depFileAbs); - wrk.LogInfo(GenT::MOC, reason); + Log().Info(GenT::MOC, reason); } return true; } if (!error.empty()) { - wrk.LogError(GenT::MOC, error); + LogError(GenT::MOC, error); return false; } } else { std::string message = "Could not find dependency file "; message += Quoted(depFileRel); - wrk.LogFileWarning(GenT::MOC, SourceFile, message); + Log().WarningFile(GenT::MOC, SourceFile, message); } } } @@ -834,41 +919,40 @@ bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) return false; } -void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk) +void cmQtAutoMocUic::JobMocT::GenerateMoc() { // Make sure the parent directory exists - if (!wrk.FileSys().MakeParentDirectory(BuildFile)) { - wrk.LogFileError(GenT::MOC, BuildFile, - "Could not create parent directory."); + if (!FileSys().MakeParentDirectory(BuildFile)) { + LogFileError(GenT::MOC, BuildFile, "Could not create parent directory."); return; } { // Compose moc command std::vector<std::string> cmd; - cmd.push_back(wrk.Moc().Executable); + cmd.push_back(Gen()->Moc().Executable); // Add options - cmd.insert(cmd.end(), wrk.Moc().AllOptions.begin(), - wrk.Moc().AllOptions.end()); + cmd.insert(cmd.end(), Gen()->Moc().AllOptions.begin(), + Gen()->Moc().AllOptions.end()); // Add predefs include - if (!wrk.Moc().PredefsFileAbs.empty()) { + if (!Gen()->Moc().PredefsFileAbs.empty()) { cmd.emplace_back("--include"); - cmd.push_back(wrk.Moc().PredefsFileAbs); + cmd.push_back(Gen()->Moc().PredefsFileAbs); } cmd.emplace_back("-o"); cmd.push_back(BuildFile); cmd.push_back(SourceFile); // Execute moc command - ProcessResultT result; - if (wrk.RunProcess(GenT::MOC, result, cmd)) { + cmWorkerPool::ProcessResultT result; + if (RunProcess(GenT::MOC, result, cmd)) { // Moc command success // Print moc output if (!result.StdOut.empty()) { - wrk.LogInfo(GenT::MOC, result.StdOut); + Log().Info(GenT::MOC, result.StdOut); } // Notify the generator that a not included file changed (on demand) if (IncludeString.empty()) { - wrk.Gen().ParallelMocAutoUpdated(); + Gen()->ParallelMocAutoUpdated(); } } else { // Moc command failed @@ -879,51 +963,51 @@ void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk) emsg += Quoted(BuildFile); emsg += ".\n"; emsg += result.ErrorMessage; - wrk.LogCommandError(GenT::MOC, emsg, cmd, result.StdOut); + LogCommandError(GenT::MOC, emsg, cmd, result.StdOut); } - wrk.FileSys().FileRemove(BuildFile); + FileSys().FileRemove(BuildFile); } } } -void cmQtAutoGeneratorMocUic::JobUicT::Process(WorkerT& wrk) +void cmQtAutoMocUic::JobUicT::Process() { // Compute build file name - BuildFile = wrk.Base().AutogenIncludeDir; + BuildFile = Gen()->Base().AutogenIncludeDir; BuildFile += '/'; BuildFile += IncludeString; - if (UpdateRequired(wrk)) { - GenerateUic(wrk); + if (UpdateRequired()) { + GenerateUic(); } } -bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk) +bool cmQtAutoMocUic::JobUicT::UpdateRequired() { - bool const verbose = wrk.Gen().Log().Verbose(); + bool const verbose = Log().Verbose(); // Test if the build file exists - if (!wrk.FileSys().FileExists(BuildFile)) { + 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"; - wrk.LogInfo(GenT::UIC, reason); + Log().Info(GenT::UIC, reason); } return true; } // Test if the uic settings changed - if (wrk.Uic().SettingsChanged) { + if (Gen()->Uic().SettingsChanged) { if (verbose) { std::string reason = "Generating "; reason += Quoted(BuildFile); reason += " from "; reason += Quoted(SourceFile); reason += " because the UIC settings changed"; - wrk.LogInfo(GenT::UIC, reason); + Log().Info(GenT::UIC, reason); } return true; } @@ -933,9 +1017,9 @@ bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk) bool isOlder = false; { std::string error; - isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); + isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); if (!isOlder && !error.empty()) { - wrk.LogError(GenT::UIC, error); + LogError(GenT::UIC, error); return false; } } @@ -945,7 +1029,7 @@ bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk) reason += Quoted(BuildFile); reason += " because it's older than its source file "; reason += Quoted(SourceFile); - wrk.LogInfo(GenT::UIC, reason); + Log().Info(GenT::UIC, reason); } return true; } @@ -954,37 +1038,36 @@ bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk) return false; } -void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk) +void cmQtAutoMocUic::JobUicT::GenerateUic() { // Make sure the parent directory exists - if (!wrk.FileSys().MakeParentDirectory(BuildFile)) { - wrk.LogFileError(GenT::UIC, BuildFile, - "Could not create parent directory."); + if (!FileSys().MakeParentDirectory(BuildFile)) { + LogFileError(GenT::UIC, BuildFile, "Could not create parent directory."); return; } { // Compose uic command std::vector<std::string> cmd; - cmd.push_back(wrk.Uic().Executable); + cmd.push_back(Gen()->Uic().Executable); { - std::vector<std::string> allOpts = wrk.Uic().TargetOptions; - auto optionIt = wrk.Uic().Options.find(SourceFile); - if (optionIt != wrk.Uic().Options.end()) { + 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, - (wrk.Base().QtVersionMajor == 5)); + (Gen()->Base().QtVersionMajor == 5)); } cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); } cmd.emplace_back("-o"); - cmd.push_back(BuildFile); - cmd.push_back(SourceFile); + cmd.emplace_back(BuildFile); + cmd.emplace_back(SourceFile); - ProcessResultT result; - if (wrk.RunProcess(GenT::UIC, result, cmd)) { + cmWorkerPool::ProcessResultT result; + if (RunProcess(GenT::UIC, result, cmd)) { // Uic command success // Print uic output if (!result.StdOut.empty()) { - wrk.LogInfo(GenT::UIC, result.StdOut); + Log().Info(GenT::UIC, result.StdOut); } } else { // Uic command failed @@ -997,147 +1080,19 @@ void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk) emsg += Quoted(IncluderFile); emsg += ".\n"; emsg += result.ErrorMessage; - wrk.LogCommandError(GenT::UIC, emsg, cmd, result.StdOut); + LogCommandError(GenT::UIC, emsg, cmd, result.StdOut); } - wrk.FileSys().FileRemove(BuildFile); + FileSys().FileRemove(BuildFile); } } } -cmQtAutoGeneratorMocUic::WorkerT::WorkerT(cmQtAutoGeneratorMocUic* gen, - uv_loop_t* uvLoop) - : Gen_(gen) -{ - // Initialize uv asynchronous callback for process starting - ProcessRequest_.init(*uvLoop, &WorkerT::UVProcessStart, this); - // Start thread - Thread_ = std::thread(&WorkerT::Loop, this); -} - -cmQtAutoGeneratorMocUic::WorkerT::~WorkerT() -{ - // Join thread - if (Thread_.joinable()) { - Thread_.join(); - } -} - -void cmQtAutoGeneratorMocUic::WorkerT::LogInfo( - GenT genType, std::string const& message) const +void cmQtAutoMocUic::JobFinishT::Process() { - Log().Info(genType, message); + Gen()->AbortSuccess(); } -void cmQtAutoGeneratorMocUic::WorkerT::LogWarning( - GenT genType, std::string const& message) const -{ - Log().Warning(genType, message); -} - -void cmQtAutoGeneratorMocUic::WorkerT::LogFileWarning( - GenT genType, std::string const& filename, std::string const& message) const -{ - Log().WarningFile(genType, filename, message); -} - -void cmQtAutoGeneratorMocUic::WorkerT::LogError( - GenT genType, std::string const& message) const -{ - Gen().ParallelRegisterJobError(); - Log().Error(genType, message); -} - -void cmQtAutoGeneratorMocUic::WorkerT::LogFileError( - GenT genType, std::string const& filename, std::string const& message) const -{ - Gen().ParallelRegisterJobError(); - Log().ErrorFile(genType, filename, message); -} - -void cmQtAutoGeneratorMocUic::WorkerT::LogCommandError( - GenT genType, std::string const& message, - std::vector<std::string> const& command, std::string const& output) const -{ - Gen().ParallelRegisterJobError(); - Log().ErrorCommand(genType, message, command, output); -} - -bool cmQtAutoGeneratorMocUic::WorkerT::RunProcess( - GenT genType, ProcessResultT& result, - std::vector<std::string> const& command) -{ - if (command.empty()) { - return false; - } - - // Create process instance - { - std::lock_guard<std::mutex> lock(ProcessMutex_); - Process_ = cm::make_unique<ReadOnlyProcessT>(); - Process_->setup(&result, true, command, Gen().Base().AutogenBuildDir); - } - - // Send asynchronous process start request to libuv loop - ProcessRequest_.send(); - - // Log command - if (this->Log().Verbose()) { - std::string msg = "Running command:\n"; - msg += QuotedCommand(command); - msg += '\n'; - this->LogInfo(genType, msg); - } - - // Wait until the process has been finished and destroyed - { - std::unique_lock<std::mutex> ulock(ProcessMutex_); - while (Process_) { - ProcessCondition_.wait(ulock); - } - } - return !result.error(); -} - -void cmQtAutoGeneratorMocUic::WorkerT::Loop() -{ - while (true) { - Gen().WorkerSwapJob(JobHandle_); - if (JobHandle_) { - JobHandle_->Process(*this); - } else { - break; - } - } -} - -void cmQtAutoGeneratorMocUic::WorkerT::UVProcessStart(uv_async_t* handle) -{ - auto& wrk = *reinterpret_cast<WorkerT*>(handle->data); - { - std::lock_guard<std::mutex> lock(wrk.ProcessMutex_); - if (wrk.Process_ && !wrk.Process_->IsStarted()) { - wrk.Process_->start(handle->loop, [&wrk] { wrk.UVProcessFinished(); }); - } - } - - if (!wrk.Process_->IsStarted()) { - wrk.UVProcessFinished(); - } -} - -void cmQtAutoGeneratorMocUic::WorkerT::UVProcessFinished() -{ - { - std::lock_guard<std::mutex> lock(ProcessMutex_); - if (Process_ && (Process_->IsFinished() || !Process_->IsStarted())) { - Process_.reset(); - } - } - // Notify idling thread - ProcessCondition_.notify_one(); -} - -cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic() +cmQtAutoMocUic::cmQtAutoMocUic() : Base_(&FileSys()) , Moc_(&FileSys()) { @@ -1147,26 +1102,11 @@ cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic() "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+" "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); - - // Initialize libuv loop - uv_disable_stdio_inheritance(); -#ifdef CMAKE_UV_SIGNAL_HACK - UVHackRAII_ = cm::make_unique<cmUVSignalHackRAII>(); -#endif - UVLoop_ = cm::make_unique<uv_loop_t>(); - uv_loop_init(UVLoop()); - - // Initialize libuv asynchronous iteration request - UVRequest().init(*UVLoop(), &cmQtAutoGeneratorMocUic::UVPollStage, this); } -cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic() -{ - // Close libuv loop - uv_loop_close(UVLoop()); -} +cmQtAutoMocUic::~cmQtAutoMocUic() = default; -bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) +bool cmQtAutoMocUic::Init(cmMakefile* makefile) { // -- Meta Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); @@ -1364,7 +1304,7 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); // Install moc predefs job if (!Moc().PredefsCmd.empty()) { - JobQueues_.MocPredefs.emplace_back(cm::make_unique<JobMocPredefsT>()); + WorkerPool().EmplaceJob<JobMocPredefsT>(); } } @@ -1400,46 +1340,48 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) } // - Headers and sources + // Add sources { - auto addHeader = [this](std::string&& hdr, bool moc, bool uic) { - this->JobQueues_.Headers.emplace_back( - cm::make_unique<JobParseT>(std::move(hdr), moc, uic, true)); - }; auto addSource = [this](std::string&& src, bool moc, bool uic) { - this->JobQueues_.Sources.emplace_back( - cm::make_unique<JobParseT>(std::move(src), moc, uic, false)); + WorkerPool().EmplaceJob<JobParseT>(std::move(src), moc, uic, false); }; - - // Add headers - for (std::string& hdr : InfoGetList("AM_HEADERS")) { - addHeader(std::move(hdr), true, true); + for (std::string& src : InfoGetList("AM_SOURCES")) { + addSource(std::move(src), true, true); } if (Moc().Enabled) { - for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) { - addHeader(std::move(hdr), true, false); + for (std::string& src : InfoGetList("AM_MOC_SOURCES")) { + addSource(std::move(src), true, false); } } if (Uic().Enabled) { - for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) { - addHeader(std::move(hdr), false, true); + for (std::string& src : InfoGetList("AM_UIC_SOURCES")) { + addSource(std::move(src), false, true); } } - - // Add sources - for (std::string& src : InfoGetList("AM_SOURCES")) { - addSource(std::move(src), true, true); + } + // 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); + }; + for (std::string& hdr : InfoGetList("AM_HEADERS")) { + addHeader(std::move(hdr), true, true); } if (Moc().Enabled) { - for (std::string& src : InfoGetList("AM_MOC_SOURCES")) { - addSource(std::move(src), true, false); + for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) { + addHeader(std::move(hdr), true, false); } } if (Uic().Enabled) { - for (std::string& src : InfoGetList("AM_UIC_SOURCES")) { - addSource(std::move(src), false, true); + for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) { + addHeader(std::move(hdr), false, true); } } } + // Addpost parse fence job + WorkerPool().EmplaceJob<JobPostParseT>(); // Init derived information // ------------------------ @@ -1534,98 +1476,25 @@ bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) return true; } -bool cmQtAutoGeneratorMocUic::Process() +bool cmQtAutoMocUic::Process() { - // Run libuv event loop - UVRequest().send(); - if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) { - if (JobError_) { - return false; - } - } else { + SettingsFileRead(); + if (!CreateDirectories()) { return false; } - return true; -} -void cmQtAutoGeneratorMocUic::UVPollStage(uv_async_t* handle) -{ - reinterpret_cast<cmQtAutoGeneratorMocUic*>(handle->data)->PollStage(); -} - -void cmQtAutoGeneratorMocUic::PollStage() -{ - switch (Stage_) { - case StageT::SETTINGS_READ: - SettingsFileRead(); - SetStage(StageT::CREATE_DIRECTORIES); - break; - case StageT::CREATE_DIRECTORIES: - CreateDirectories(); - SetStage(StageT::PARSE_SOURCES); - break; - case StageT::PARSE_SOURCES: - if (ThreadsStartJobs(JobQueues_.Sources)) { - SetStage(StageT::PARSE_HEADERS); - } - break; - case StageT::PARSE_HEADERS: - if (ThreadsStartJobs(JobQueues_.Headers)) { - SetStage(StageT::MOC_PREDEFS); - } - break; - case StageT::MOC_PREDEFS: - if (ThreadsStartJobs(JobQueues_.MocPredefs)) { - SetStage(StageT::MOC_PROCESS); - } - break; - case StageT::MOC_PROCESS: - if (ThreadsStartJobs(JobQueues_.Moc)) { - SetStage(StageT::MOCS_COMPILATION); - } - break; - case StageT::MOCS_COMPILATION: - if (ThreadsJobsDone()) { - MocGenerateCompilation(); - SetStage(StageT::UIC_PROCESS); - } - break; - case StageT::UIC_PROCESS: - if (ThreadsStartJobs(JobQueues_.Uic)) { - SetStage(StageT::SETTINGS_WRITE); - } - break; - case StageT::SETTINGS_WRITE: - SettingsFileWrite(); - SetStage(StageT::FINISH); - break; - case StageT::FINISH: - if (ThreadsJobsDone()) { - // Clear all libuv handles - ThreadsStop(); - UVRequest().reset(); - // Set highest END stage manually - Stage_ = StageT::END; - } - break; - case StageT::END: - break; + if (!WorkerPool_.Process(Base().NumThreads, this)) { + return false; } -} -void cmQtAutoGeneratorMocUic::SetStage(StageT stage) -{ if (JobError_) { - stage = StageT::FINISH; - } - // Only allow to increase the stage - if (Stage_ < stage) { - Stage_ = stage; - UVRequest().send(); + return false; } + + return SettingsFileWrite(); } -void cmQtAutoGeneratorMocUic::SettingsFileRead() +void cmQtAutoMocUic::SettingsFileRead() { // Compose current settings strings { @@ -1691,11 +1560,10 @@ void cmQtAutoGeneratorMocUic::SettingsFileRead() } } -void cmQtAutoGeneratorMocUic::SettingsFileWrite() +bool cmQtAutoMocUic::SettingsFileWrite() { - std::lock_guard<std::mutex> jobsLock(JobsMutex_); // Only write if any setting changed - if (!JobError_ && (Moc().SettingsChanged || Uic().SettingsChanged)) { + if (Moc().SettingsChanged || Uic().SettingsChanged) { if (Log().Verbose()) { Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_)); } @@ -1721,255 +1589,142 @@ void cmQtAutoGeneratorMocUic::SettingsFileWrite() "Settings file writing failed. " + error); // Remove old settings file to trigger a full rebuild on the next run FileSys().FileRemove(SettingsFile_); - RegisterJobError(); + return false; } } + return true; } -void cmQtAutoGeneratorMocUic::CreateDirectories() +bool cmQtAutoMocUic::CreateDirectories() { // Create AUTOGEN include directory if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) { Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir, "Could not create directory."); - RegisterJobError(); + return false; } + return true; } -bool cmQtAutoGeneratorMocUic::ThreadsStartJobs(JobQueueT& queue) +// Private method that requires cmQtAutoMocUic::JobsMutex_ to be +// locked +void cmQtAutoMocUic::Abort(bool error) { - bool done = false; - std::size_t queueSize = queue.size(); - - // Change the active queue - { - std::lock_guard<std::mutex> jobsLock(JobsMutex_); - // Check if there are still unfinished jobs from the previous queue - if (JobsRemain_ == 0) { - if (!JobThreadsAbort_) { - JobQueue_.swap(queue); - JobsRemain_ = queueSize; - } else { - // Abort requested - queue.clear(); - queueSize = 0; - } - done = true; - } + if (error) { + JobError_.store(true); } + WorkerPool_.Abort(); +} - if (done && (queueSize != 0)) { - // Start new threads on demand - if (Workers_.empty()) { - Workers_.resize(Base().NumThreads); - for (auto& item : Workers_) { - item = cm::make_unique<WorkerT>(this, UVLoop()); +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; } - } else { - // Notify threads - if (queueSize == 1) { - JobsConditionRead_.notify_one(); - } else { - JobsConditionRead_.notify_all(); + { + // 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; } } - } - return done; -} - -void cmQtAutoGeneratorMocUic::ThreadsStop() -{ - if (!Workers_.empty()) { - // Clear all jobs - { - std::lock_guard<std::mutex> jobsLock(JobsMutex_); - JobThreadsAbort_ = true; - JobsRemain_ -= JobQueue_.size(); - JobQueue_.clear(); - - JobQueues_.Sources.clear(); - JobQueues_.Headers.clear(); - JobQueues_.MocPredefs.clear(); - JobQueues_.Moc.clear(); - JobQueues_.Uic.clear(); - } - // Wake threads - JobsConditionRead_.notify_all(); - // Join and clear threads - Workers_.clear(); + // 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 cmQtAutoGeneratorMocUic::ThreadsJobsDone() -{ - std::lock_guard<std::mutex> jobsLock(JobsMutex_); - return (JobsRemain_ == 0); -} - -void cmQtAutoGeneratorMocUic::WorkerSwapJob(JobHandleT& jobHandle) +bool cmQtAutoMocUic::ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle) { - bool const jobProcessed(jobHandle); - if (jobProcessed) { - jobHandle.reset(); - } + const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle)); { - std::unique_lock<std::mutex> jobsLock(JobsMutex_); - // Reduce the remaining job count and notify the libuv loop - // when all jobs are done - if (jobProcessed) { - --JobsRemain_; - if (JobsRemain_ == 0) { - UVRequest().send(); + 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; } - } - // Wait for new jobs - while (!JobThreadsAbort_ && JobQueue_.empty()) { - JobsConditionRead_.wait(jobsLock); - } - // Try to pick up a new job handle - if (!JobThreadsAbort_ && !JobQueue_.empty()) { - jobHandle = std::move(JobQueue_.front()); - JobQueue_.pop_front(); - } - } -} - -void cmQtAutoGeneratorMocUic::ParallelRegisterJobError() -{ - std::lock_guard<std::mutex> jobsLock(JobsMutex_); - RegisterJobError(); -} - -// Private method that requires cmQtAutoGeneratorMocUic::JobsMutex_ to be -// locked -void cmQtAutoGeneratorMocUic::RegisterJobError() -{ - JobError_ = true; - if (!JobThreadsAbort_) { - JobThreadsAbort_ = true; - // Clear remaining jobs - if (JobsRemain_ != 0) { - JobsRemain_ -= JobQueue_.size(); - JobQueue_.clear(); - } - } -} - -bool cmQtAutoGeneratorMocUic::ParallelJobPushMoc(JobHandleT& jobHandle) -{ - std::lock_guard<std::mutex> jobsLock(JobsMutex_); - if (!JobThreadsAbort_) { - bool pushJobHandle = true; - // Do additional tests if this is an included moc job - const JobMocT& mocJob(static_cast<JobMocT&>(*jobHandle)); - if (!mocJob.IncludeString.empty()) { - // Register included moc file and look for collisions - MocIncludedFiles_.emplace(mocJob.SourceFile); - if (!MocIncludedStrings_.emplace(mocJob.IncludeString).second) { - // Another source file includes the same moc file! - for (const JobHandleT& otherHandle : JobQueues_.Moc) { - const JobMocT& otherJob(static_cast<JobMocT&>(*otherHandle)); - if (otherJob.IncludeString == mocJob.IncludeString) { - // Check if the same moc file would be generated from different - // source files which is an error. - if (otherJob.SourceFile != mocJob.SourceFile) { - // Include string collision - std::string error = "The two source files\n "; - error += Quoted(mocJob.IncluderFile); - error += " and\n "; - error += Quoted(otherJob.IncluderFile); - 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(otherJob.SourceFile); - 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); - RegisterJobError(); - } - // Do not push this job in since the included moc file already - // gets generated by an other job. - pushJobHandle = false; - break; - } - } + { + // 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; } } - // Push job on demand - if (pushJobHandle) { - JobQueues_.Moc.emplace_back(std::move(jobHandle)); - } - } - return !JobError_; -} -bool cmQtAutoGeneratorMocUic::ParallelJobPushUic(JobHandleT& jobHandle) -{ - std::lock_guard<std::mutex> jobsLock(JobsMutex_); - if (!JobThreadsAbort_) { - bool pushJobHandle = true; - // Look for include collisions. - const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle)); - for (const JobHandleT& otherHandle : JobQueues_.Uic) { - const JobUicT& otherJob(static_cast<JobUicT&>(*otherHandle)); - if (otherJob.IncludeString == uicJob.IncludeString) { - // Check if the same uic file would be generated from different - // source files which would be an error. - if (otherJob.SourceFile != uicJob.SourceFile) { - // Include string collision - std::string error = "The two source files\n "; - error += Quoted(uicJob.IncluderFile); - error += " and\n "; - error += Quoted(otherJob.IncluderFile); - 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(otherJob.SourceFile); - 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); - RegisterJobError(); - } - // Do not push this job in since the uic file already - // gets generated by an other job. - pushJobHandle = false; - break; - } - } - if (pushJobHandle) { - JobQueues_.Uic.emplace_back(std::move(jobHandle)); - } + // 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 !JobError_; + return WorkerPool_.PushJob(std::move(jobHandle)); } -bool cmQtAutoGeneratorMocUic::ParallelMocIncluded( - std::string const& sourceFile) +bool cmQtAutoMocUic::ParallelMocIncluded(std::string const& sourceFile) { - std::lock_guard<std::mutex> mocLock(JobsMutex_); + std::lock_guard<std::mutex> guard(MocMetaMutex_); return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end()); } -std::string cmQtAutoGeneratorMocUic::ParallelMocAutoRegister( +std::string cmQtAutoMocUic::ParallelMocAutoRegister( std::string const& baseName) { std::string res; { - std::lock_guard<std::mutex> mocLock(JobsMutex_); + std::lock_guard<std::mutex> mocLock(MocMetaMutex_); res = baseName; res += ".cpp"; if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) { @@ -1990,63 +1745,3 @@ std::string cmQtAutoGeneratorMocUic::ParallelMocAutoRegister( } return res; } - -void cmQtAutoGeneratorMocUic::ParallelMocAutoUpdated() -{ - std::lock_guard<std::mutex> mocLock(JobsMutex_); - MocAutoFileUpdated_ = true; -} - -void cmQtAutoGeneratorMocUic::MocGenerateCompilation() -{ - std::lock_guard<std::mutex> mocLock(JobsMutex_); - if (!JobError_ && Moc().Enabled) { - // Write mocs compilation build file - { - // Compose mocs compilation file content - std::string content = - "// This file is autogenerated. Changes will be overwritten.\n"; - if (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 = Base().MultiConfig ? '<' : '"'; - char const send = Base().MultiConfig ? '>' : '"'; - for (std::string const& mocfile : MocAutoFiles_) { - content += "#include "; - content += sbeg; - content += mocfile; - content += send; - content += '\n'; - } - } - - std::string const& compAbs = Moc().CompFileAbs; - if (FileSys().FileDiffers(compAbs, content)) { - // Actually write mocs compilation file - if (Log().Verbose()) { - Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs); - } - std::string error; - if (!FileSys().FileWrite(compAbs, content, &error)) { - Log().ErrorFile(GenT::MOC, compAbs, - "mocs compilation file writing failed. " + error); - RegisterJobError(); - return; - } - } else if (MocAutoFileUpdated_) { - // Only touch mocs compilation file - if (Log().Verbose()) { - Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs); - } - FileSys().Touch(compAbs); - } - } - // Write mocs compilation wrapper file - if (Base().MultiConfig) { - } - } -} diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoMocUic.h index 27d73a7..3902abb 100644 --- a/Source/cmQtAutoGeneratorMocUic.h +++ b/Source/cmQtAutoMocUic.h @@ -1,26 +1,22 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGeneratorMocUic_h -#define cmQtAutoGeneratorMocUic_h +#ifndef cmQtAutoMocUic_h +#define cmQtAutoMocUic_h #include "cmConfigure.h" // IWYU pragma: keep #include "cmQtAutoGen.h" #include "cmQtAutoGenerator.h" -#include "cmUVHandlePtr.h" -#include "cmUVSignalHackRAII.h" // IWYU pragma: keep -#include "cm_uv.h" +#include "cmWorkerPool.h" #include "cmsys/RegularExpression.hxx" -#include <condition_variable> -#include <cstddef> -#include <deque> +#include <array> +#include <atomic> #include <map> #include <memory> // IWYU pragma: keep #include <mutex> #include <set> #include <string> -#include <thread> #include <unordered_set> #include <utility> #include <vector> @@ -28,18 +24,18 @@ class cmMakefile; // @brief AUTOMOC and AUTOUIC generator -class cmQtAutoGeneratorMocUic : public cmQtAutoGenerator +class cmQtAutoMocUic : public cmQtAutoGenerator { public: - cmQtAutoGeneratorMocUic(); - ~cmQtAutoGeneratorMocUic() override; + cmQtAutoMocUic(); + ~cmQtAutoMocUic() override; - cmQtAutoGeneratorMocUic(cmQtAutoGeneratorMocUic const&) = delete; - cmQtAutoGeneratorMocUic& operator=(cmQtAutoGeneratorMocUic const&) = delete; + cmQtAutoMocUic(cmQtAutoMocUic const&) = delete; + cmQtAutoMocUic& operator=(cmQtAutoMocUic const&) = delete; public: // -- Types - class WorkerT; + typedef std::multimap<std::string, std::array<std::string, 2>> IncludesMap; /// @brief Search key plus regular expression pair /// @@ -173,31 +169,71 @@ public: cmsys::RegularExpression RegExpInclude; }; - /// @brief Abstract job class for threaded processing + /// @brief Abstract job class for concurrent job processing /// - class JobT + class JobT : public cmWorkerPool::JobT { - public: - JobT() = default; - virtual ~JobT() = default; + protected: + /** + * @brief Protected default constructor + */ + JobT(bool fence = false) + : cmWorkerPool::JobT(fence) + { + } + + //! Get the generator. Only valid during Process() call! + cmQtAutoMocUic* Gen() const + { + 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(); } + + // -- Error logging with automatic abort + void LogError(GenT genType, std::string const& message) const; + void LogFileError(GenT genType, std::string const& filename, + std::string const& message) const; + void LogCommandError(GenT genType, std::string const& message, + std::vector<std::string> const& command, + std::string const& output) const; - JobT(JobT const&) = delete; - JobT& operator=(JobT const&) = delete; + /** + * @brief Run an external process. Use only during Process() call! + */ + bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result, + std::vector<std::string> const& command); + }; - // -- Abstract processing interface - virtual void Process(WorkerT& wrk) = 0; + /// @brief Fence job utility class + /// + class JobFenceT : public JobT + { + public: + JobFenceT() + : JobT(true) + { + } + void Process() override{}; }; - // Job management types - typedef std::unique_ptr<JobT> JobHandleT; - typedef std::deque<JobHandleT> JobQueueT; + /// @brief Generate moc_predefs.h + /// + class JobMocPredefsT : public JobT + { + private: + void Process() override; + }; - /// @brief Parse source job + /// @brief Parses a source file /// class JobParseT : public JobT { public: - JobParseT(std::string&& fileName, bool moc, bool uic, bool header = false) + JobParseT(std::string fileName, bool moc, bool uic, bool header = false) : FileName(std::move(fileName)) , AutoMoc(moc) , AutoUic(uic) @@ -213,18 +249,15 @@ public: std::string FileBase; }; - void Process(WorkerT& wrk) override; - bool ParseMocSource(WorkerT& wrk, MetaT const& meta); - bool ParseMocHeader(WorkerT& wrk, MetaT const& meta); - std::string MocStringHeaders(WorkerT& wrk, - std::string const& fileBase) const; - std::string MocFindIncludedHeader(WorkerT& wrk, - std::string const& includerDir, + 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(WorkerT& wrk, MetaT const& meta); - bool ParseUicInclude(WorkerT& wrk, MetaT const& meta, - std::string&& includeString); - std::string UicFindIncludedFile(WorkerT& wrk, MetaT const& meta, + bool ParseUic(MetaT const& meta); + bool ParseUicInclude(MetaT const& meta, std::string&& includeString); + std::string UicFindIncludedFile(MetaT const& meta, std::string const& includeString); private: @@ -234,12 +267,20 @@ public: bool Header = false; }; - /// @brief Generate moc_predefs + /// @brief Generates additional jobs after all files have been parsed /// - class JobMocPredefsT : public JobT + class JobPostParseT : public JobFenceT { private: - void Process(WorkerT& wrk) override; + void Process() override; + }; + + /// @brief Generate mocs_compilation.cpp + /// + class JobMocsCompilationT : public JobFenceT + { + private: + void Process() override; }; /// @brief Moc a file job @@ -247,20 +288,20 @@ public: class JobMocT : public JobT { public: - JobMocT(std::string&& sourceFile, std::string includerFile, - std::string&& includeString) + JobMocT(std::string sourceFile, std::string includerFile, + std::string includeString) : SourceFile(std::move(sourceFile)) , IncluderFile(std::move(includerFile)) , IncludeString(std::move(includeString)) { } - void FindDependencies(WorkerT& wrk, std::string const& content); + void FindDependencies(std::string const& content); private: - void Process(WorkerT& wrk) override; - bool UpdateRequired(WorkerT& wrk); - void GenerateMoc(WorkerT& wrk); + void Process() override; + bool UpdateRequired(); + void GenerateMoc(); public: std::string SourceFile; @@ -276,8 +317,8 @@ public: class JobUicT : public JobT { public: - JobUicT(std::string&& sourceFile, std::string includerFile, - std::string&& includeString) + JobUicT(std::string sourceFile, std::string includerFile, + std::string includeString) : SourceFile(std::move(sourceFile)) , IncluderFile(std::move(includerFile)) , IncludeString(std::move(includeString)) @@ -285,9 +326,9 @@ public: } private: - void Process(WorkerT& wrk) override; - bool UpdateRequired(WorkerT& wrk); - void GenerateUic(WorkerT& wrk); + void Process() override; + bool UpdateRequired(); + void GenerateUic(); public: std::string SourceFile; @@ -296,80 +337,12 @@ public: std::string BuildFile; }; - /// @brief Worker Thread + /// @brief The last job /// - class WorkerT + class JobFinishT : public JobFenceT { - public: - WorkerT(cmQtAutoGeneratorMocUic* gen, uv_loop_t* uvLoop); - ~WorkerT(); - - WorkerT(WorkerT const&) = delete; - WorkerT& operator=(WorkerT const&) = delete; - - // -- Const accessors - cmQtAutoGeneratorMocUic& Gen() const { return *Gen_; } - Logger& Log() const { return Gen_->Log(); } - FileSystem& FileSys() const { return Gen_->FileSys(); } - const BaseSettingsT& Base() const { return Gen_->Base(); } - const MocSettingsT& Moc() const { return Gen_->Moc(); } - const UicSettingsT& Uic() const { return Gen_->Uic(); } - - // -- Log info - void LogInfo(GenT genType, std::string const& message) const; - // -- Log warning - void LogWarning(GenT genType, std::string const& message) const; - void LogFileWarning(GenT genType, std::string const& filename, - std::string const& message) const; - // -- Log error - void LogError(GenT genType, std::string const& message) const; - void LogFileError(GenT genType, std::string const& filename, - std::string const& message) const; - void LogCommandError(GenT genType, std::string const& message, - std::vector<std::string> const& command, - std::string const& output) const; - - // -- External processes - /// @brief Verbose logging version - bool RunProcess(GenT genType, ProcessResultT& result, - std::vector<std::string> const& command); - private: - /// @brief Thread main loop - void Loop(); - - // -- Libuv callbacks - static void UVProcessStart(uv_async_t* handle); - void UVProcessFinished(); - - private: - // -- Generator - cmQtAutoGeneratorMocUic* Gen_; - // -- Job handle - JobHandleT JobHandle_; - // -- Process management - std::mutex ProcessMutex_; - cm::uv_async_ptr ProcessRequest_; - std::condition_variable ProcessCondition_; - std::unique_ptr<ReadOnlyProcessT> Process_; - // -- System thread - std::thread Thread_; - }; - - /// @brief Processing stage - enum class StageT - { - SETTINGS_READ, - CREATE_DIRECTORIES, - PARSE_SOURCES, - PARSE_HEADERS, - MOC_PREDEFS, - MOC_PROCESS, - MOCS_COMPILATION, - UIC_PROCESS, - SETTINGS_WRITE, - FINISH, - END + void Process() override; }; // -- Const settings interface @@ -377,41 +350,39 @@ public: const MocSettingsT& Moc() const { return this->Moc_; } const UicSettingsT& Uic() const { return this->Uic_; } - // -- Worker thread interface - void WorkerSwapJob(JobHandleT& jobHandle); // -- Parallel job processing interface - void ParallelRegisterJobError(); - bool ParallelJobPushMoc(JobHandleT& jobHandle); - bool ParallelJobPushUic(JobHandleT& jobHandle); - bool ParallelMocIncluded(std::string const& sourceFile); + 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); - void ParallelMocAutoUpdated(); + bool ParallelMocIncluded(std::string const& sourceFile); + std::set<std::string> const& MocAutoFiles() const + { + return this->MocAutoFiles_; + } private: // -- Utility accessors Logger& Log() { return Logger_; } FileSystem& FileSys() { return FileSys_; } - // -- libuv loop accessors - uv_loop_t* UVLoop() { return UVLoop_.get(); } - cm::uv_async_ptr& UVRequest() { return UVRequest_; } // -- Abstract processing interface bool Init(cmMakefile* makefile) override; bool Process() override; - // -- Process stage - static void UVPollStage(uv_async_t* handle); - void PollStage(); - void SetStage(StageT stage); // -- Settings file void SettingsFileRead(); - void SettingsFileWrite(); + bool SettingsFileWrite(); // -- Thread processing - bool ThreadsStartJobs(JobQueueT& queue); - bool ThreadsJobsDone(); - void ThreadsStop(); - void RegisterJobError(); + void Abort(bool error); // -- Generation - void CreateDirectories(); - void MocGenerateCompilation(); + bool CreateDirectories(); private: // -- Utility @@ -421,39 +392,22 @@ private: BaseSettingsT Base_; MocSettingsT Moc_; UicSettingsT Uic_; - // -- libuv loop -#ifdef CMAKE_UV_SIGNAL_HACK - std::unique_ptr<cmUVSignalHackRAII> UVHackRAII_; -#endif - std::unique_ptr<uv_loop_t> UVLoop_; - cm::uv_async_ptr UVRequest_; - StageT Stage_ = StageT::SETTINGS_READ; - // -- Job queues - std::mutex JobsMutex_; - struct - { - JobQueueT Sources; - JobQueueT Headers; - JobQueueT MocPredefs; - JobQueueT Moc; - JobQueueT Uic; - } JobQueues_; - JobQueueT JobQueue_; - std::size_t volatile JobsRemain_ = 0; - bool volatile JobError_ = false; - bool volatile JobThreadsAbort_ = false; - std::condition_variable JobsConditionRead_; // -- Moc meta - std::set<std::string> MocIncludedStrings_; + std::mutex MocMetaMutex_; std::set<std::string> MocIncludedFiles_; + IncludesMap MocIncludes_; std::set<std::string> MocAutoFiles_; - bool volatile MocAutoFileUpdated_ = false; + std::atomic<bool> MocAutoFileUpdated_ = ATOMIC_VAR_INIT(false); + // -- Uic meta + std::mutex UicMetaMutex_; + IncludesMap UicIncludes_; // -- Settings file std::string SettingsFile_; std::string SettingsStringMoc_; std::string SettingsStringUic_; - // -- Threads and loops - std::vector<std::unique_ptr<WorkerT>> Workers_; + // -- Thread pool and job queue + std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false); + cmWorkerPool WorkerPool_; }; #endif diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx index 252d985..998f904 100644 --- a/Source/cmStringCommand.cxx +++ b/Source/cmStringCommand.cxx @@ -1,9 +1,13 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ +#define _SCL_SECURE_NO_WARNINGS + #include "cmStringCommand.h" #include "cmsys/RegularExpression.hxx" +#include <algorithm> #include <ctype.h> +#include <iterator> #include <memory> // IWYU pragma: keep #include <sstream> #include <stdio.h> @@ -13,6 +17,7 @@ #include "cmCryptoHash.h" #include "cmGeneratorExpression.h" #include "cmMakefile.h" +#include "cmMessageType.h" #include "cmRange.h" #include "cmStringReplaceHelper.h" #include "cmSystemTools.h" @@ -79,6 +84,9 @@ bool cmStringCommand::InitialPass(std::vector<std::string> const& args, if (subCommand == "STRIP") { return this->HandleStripCommand(args); } + if (subCommand == "REPEAT") { + return this->HandleRepeatCommand(args); + } if (subCommand == "RANDOM") { return this->HandleRandomCommand(args); } @@ -709,6 +717,59 @@ bool cmStringCommand::HandleStripCommand(std::vector<std::string> const& args) return true; } +bool cmStringCommand::HandleRepeatCommand(std::vector<std::string> const& args) +{ + // `string(REPEAT "<str>" <times> OUTPUT_VARIABLE)` + enum ArgPos : std::size_t + { + SUB_COMMAND, + VALUE, + TIMES, + OUTPUT_VARIABLE, + TOTAL_ARGS + }; + + if (args.size() != ArgPos::TOTAL_ARGS) { + this->Makefile->IssueMessage( + MessageType::FATAL_ERROR, + "sub-command REPEAT requires three arguments."); + return true; + } + + unsigned long times; + if (!cmSystemTools::StringToULong(args[ArgPos::TIMES].c_str(), ×)) { + this->Makefile->IssueMessage(MessageType::FATAL_ERROR, + "repeat count is not a positive number."); + return true; + } + + const auto& stringValue = args[ArgPos::VALUE]; + const auto& variableName = args[ArgPos::OUTPUT_VARIABLE]; + const auto inStringLength = stringValue.size(); + + std::string result; + switch (inStringLength) { + case 0u: + // Nothing to do for zero length input strings + break; + case 1u: + // NOTE If the string to repeat consists of the only character, + // use the appropriate constructor. + result = std::string(times, stringValue[0]); + break; + default: + result = std::string(inStringLength * times, char{}); + for (auto i = 0u; i < times; ++i) { + std::copy(cm::cbegin(stringValue), cm::cend(stringValue), + &result[i * inStringLength]); + } + break; + } + + this->Makefile->AddDefinition(variableName, result.c_str()); + return true; +} + bool cmStringCommand::HandleRandomCommand(std::vector<std::string> const& args) { if (args.size() < 2 || args.size() == 3 || args.size() == 5) { diff --git a/Source/cmStringCommand.h b/Source/cmStringCommand.h index cbff73e..acde605 100644 --- a/Source/cmStringCommand.h +++ b/Source/cmStringCommand.h @@ -51,6 +51,7 @@ protected: bool HandleConcatCommand(std::vector<std::string> const& args); bool HandleJoinCommand(std::vector<std::string> const& args); bool HandleStripCommand(std::vector<std::string> const& args); + bool HandleRepeatCommand(std::vector<std::string> const& args); bool HandleRandomCommand(std::vector<std::string> const& args); bool HandleFindCommand(std::vector<std::string> const& args); bool HandleTimestampCommand(std::vector<std::string> const& args); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 5195957..c60706d 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -663,6 +663,7 @@ void cmVisualStudio10TargetGenerator::Generate() this->WriteCustomCommands(e0); this->WriteAllSources(e0); this->WriteDotNetReferences(e0); + this->WriteImports(e0); this->WriteEmbeddedResourceGroup(e0); this->WriteXamlFilesGroup(e0); this->WriteWinRTReferences(e0); @@ -811,6 +812,24 @@ void cmVisualStudio10TargetGenerator::WriteDotNetReference( this->WriteDotNetReferenceCustomTags(e2, ref); } +void cmVisualStudio10TargetGenerator::WriteImports(Elem& e0) +{ + const char* imports = + this->GeneratorTarget->Target->GetProperty("VS_PROJECT_IMPORT"); + if (imports) { + std::vector<std::string> argsSplit; + cmSystemTools::ExpandListArgument(std::string(imports), argsSplit, false); + for (auto& path : argsSplit) { + if (!cmsys::SystemTools::FileIsFullPath(path)) { + path = this->Makefile->GetCurrentSourceDirectory() + "/" + path; + } + ConvertToWindowsSlash(path); + Elem e1(e0, "Import"); + e1.Attribute("Project", path); + } + } +} + void cmVisualStudio10TargetGenerator::WriteDotNetReferenceCustomTags( Elem& e2, std::string const& ref) { @@ -1430,10 +1449,10 @@ std::string cmVisualStudio10TargetGenerator::ConvertPath( static void ConvertToWindowsSlash(std::string& s) { // first convert all of the slashes - std::string::size_type pos = 0; - while ((pos = s.find('/', pos)) != std::string::npos) { - s[pos] = '\\'; - pos++; + for (auto& ch : s) { + if (ch == '/') { + ch = '\\'; + } } } @@ -1449,7 +1468,7 @@ void cmVisualStudio10TargetGenerator::WriteGroups() std::vector<cmGeneratorTarget::AllConfigSource> const& sources = this->GeneratorTarget->GetAllConfigSources(); - std::set<cmSourceGroup*> groupsUsed; + std::set<cmSourceGroup const*> groupsUsed; for (cmGeneratorTarget::AllConfigSource const& si : sources) { std::string const& source = si.Source->GetFullPath(); cmSourceGroup* sourceGroup = @@ -1534,13 +1553,13 @@ void cmVisualStudio10TargetGenerator::WriteGroups() { Elem e1(e0, "ItemGroup"); e1.SetHasElements(); - std::vector<cmSourceGroup*> groupsVec(groupsUsed.begin(), - groupsUsed.end()); + std::vector<cmSourceGroup const*> groupsVec(groupsUsed.begin(), + groupsUsed.end()); std::sort(groupsVec.begin(), groupsVec.end(), - [](cmSourceGroup* l, cmSourceGroup* r) { + [](cmSourceGroup const* l, cmSourceGroup const* r) { return l->GetFullName() < r->GetFullName(); }); - for (cmSourceGroup* sg : groupsVec) { + for (cmSourceGroup const* sg : groupsVec) { std::string const& name = sg->GetFullName(); if (!name.empty()) { std::string guidName = "SG_Filter_" + name; @@ -1572,7 +1591,7 @@ void cmVisualStudio10TargetGenerator::WriteGroups() // Add to groupsUsed empty source groups that have non-empty children. void cmVisualStudio10TargetGenerator::AddMissingSourceGroups( - std::set<cmSourceGroup*>& groupsUsed, + std::set<cmSourceGroup const*>& groupsUsed, const std::vector<cmSourceGroup>& allGroups) { for (cmSourceGroup const& current : allGroups) { @@ -1583,17 +1602,15 @@ void cmVisualStudio10TargetGenerator::AddMissingSourceGroups( this->AddMissingSourceGroups(groupsUsed, children); - cmSourceGroup* current_ptr = const_cast<cmSourceGroup*>(¤t); - if (groupsUsed.find(current_ptr) != groupsUsed.end()) { + if (groupsUsed.count(¤t) > 0) { continue; // group has already been added to set } // check if it least one of the group's descendants is not empty // (at least one child must already have been added) - std::vector<cmSourceGroup>::const_iterator child_it = children.begin(); + auto child_it = children.begin(); while (child_it != children.end()) { - cmSourceGroup* child_ptr = const_cast<cmSourceGroup*>(&(*child_it)); - if (groupsUsed.find(child_ptr) != groupsUsed.end()) { + if (groupsUsed.count(&(*child_it)) > 0) { break; // found a child that was already added => add current group too } child_it++; @@ -1603,7 +1620,7 @@ void cmVisualStudio10TargetGenerator::AddMissingSourceGroups( continue; // no descendants have source files => ignore this group } - groupsUsed.insert(current_ptr); + groupsUsed.insert(¤t); } } @@ -2530,10 +2547,9 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( } else { std::set<std::string> languages; this->GeneratorTarget->GetLanguages(languages, configName); - for (const char* const* l = cm::cbegin(clLangs); l != cm::cend(clLangs); - ++l) { - if (languages.find(*l) != languages.end()) { - langForClCompile = *l; + for (const char* l : clLangs) { + if (languages.count(l)) { + langForClCompile = l; break; } } @@ -4058,10 +4074,7 @@ bool cmVisualStudio10TargetGenerator::IsResxHeader( { std::set<std::string> expectedResxHeaders; this->GeneratorTarget->GetExpectedResxHeaders(expectedResxHeaders, ""); - - std::set<std::string>::const_iterator it = - expectedResxHeaders.find(headerFile); - return it != expectedResxHeaders.end(); + return expectedResxHeaders.count(headerFile) > 0; } bool cmVisualStudio10TargetGenerator::IsXamlHeader( @@ -4069,10 +4082,7 @@ bool cmVisualStudio10TargetGenerator::IsXamlHeader( { std::set<std::string> expectedXamlHeaders; this->GeneratorTarget->GetExpectedXamlHeaders(expectedXamlHeaders, ""); - - std::set<std::string>::const_iterator it = - expectedXamlHeaders.find(headerFile); - return it != expectedXamlHeaders.end(); + return expectedXamlHeaders.count(headerFile) > 0; } bool cmVisualStudio10TargetGenerator::IsXamlSource( @@ -4080,10 +4090,7 @@ bool cmVisualStudio10TargetGenerator::IsXamlSource( { std::set<std::string> expectedXamlSources; this->GeneratorTarget->GetExpectedXamlSources(expectedXamlSources, ""); - - std::set<std::string>::const_iterator it = - expectedXamlSources.find(sourceFile); - return it != expectedXamlSources.end(); + return expectedXamlSources.count(sourceFile) > 0; } void cmVisualStudio10TargetGenerator::WriteApplicationTypeSettings(Elem& e1) @@ -4654,10 +4661,8 @@ void cmVisualStudio10TargetGenerator::GetCSharpSourceProperties( void cmVisualStudio10TargetGenerator::WriteCSharpSourceProperties( Elem& e2, const std::map<std::string, std::string>& tags) { - if (!tags.empty()) { - for (const auto& i : tags) { - e2.Element(i.first.c_str(), i.second); - } + for (const auto& i : tags) { + e2.Element(i.first.c_str(), i.second); } } diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index 5901004..1dea8e9 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -76,6 +76,7 @@ private: void WriteDotNetReference(Elem& e1, std::string const& ref, std::string const& hint, std::string const& config); + void WriteImports(Elem& e0); void WriteDotNetReferenceCustomTags(Elem& e2, std::string const& ref); void WriteEmbeddedResourceGroup(Elem& e0); void WriteWinRTReferences(Elem& e0); @@ -165,7 +166,7 @@ private: void WriteGroupSources(Elem& e0, std::string const& name, ToolSources const& sources, std::vector<cmSourceGroup>&); - void AddMissingSourceGroups(std::set<cmSourceGroup*>& groupsUsed, + void AddMissingSourceGroups(std::set<cmSourceGroup const*>& groupsUsed, const std::vector<cmSourceGroup>& allGroups); bool IsResxHeader(const std::string& headerFile); bool IsXamlHeader(const std::string& headerFile); diff --git a/Source/cmWorkerPool.cxx b/Source/cmWorkerPool.cxx new file mode 100644 index 0000000..464182c --- /dev/null +++ b/Source/cmWorkerPool.cxx @@ -0,0 +1,770 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmWorkerPool.h" + +#include "cmRange.h" +#include "cmUVHandlePtr.h" +#include "cmUVSignalHackRAII.h" // IWYU pragma: keep +#include "cm_uv.h" + +#include <algorithm> +#include <array> +#include <condition_variable> +#include <deque> +#include <functional> +#include <mutex> +#include <stddef.h> +#include <thread> + +/** + * @brief libuv pipe buffer class + */ +class cmUVPipeBuffer +{ +public: + typedef cmRange<char const*> DataRange; + typedef std::function<void(DataRange)> DataFunction; + /// On error the ssize_t argument is a non zero libuv error code + typedef std::function<void(ssize_t)> EndFunction; + +public: + /** + * Reset to construction state + */ + void reset(); + + /** + * Initializes uv_pipe(), uv_stream() and uv_handle() + * @return true on success + */ + bool init(uv_loop_t* uv_loop); + + /** + * Start reading + * @return true on success + */ + bool startRead(DataFunction dataFunction, EndFunction endFunction); + + //! libuv pipe + uv_pipe_t* uv_pipe() const { return UVPipe_.get(); } + //! uv_pipe() casted to libuv stream + uv_stream_t* uv_stream() const { return static_cast<uv_stream_t*>(UVPipe_); } + //! uv_pipe() casted to libuv handle + uv_handle_t* uv_handle() { return static_cast<uv_handle_t*>(UVPipe_); } + +private: + // -- Libuv callbacks + static void UVAlloc(uv_handle_t* handle, size_t suggestedSize, + uv_buf_t* buf); + static void UVData(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); + +private: + cm::uv_pipe_ptr UVPipe_; + std::vector<char> Buffer_; + DataFunction DataFunction_; + EndFunction EndFunction_; +}; + +void cmUVPipeBuffer::reset() +{ + if (UVPipe_.get() != nullptr) { + EndFunction_ = nullptr; + DataFunction_ = nullptr; + Buffer_.clear(); + Buffer_.shrink_to_fit(); + UVPipe_.reset(); + } +} + +bool cmUVPipeBuffer::init(uv_loop_t* uv_loop) +{ + reset(); + if (uv_loop == nullptr) { + return false; + } + int ret = UVPipe_.init(*uv_loop, 0, this); + return (ret == 0); +} + +bool cmUVPipeBuffer::startRead(DataFunction dataFunction, + EndFunction endFunction) +{ + if (UVPipe_.get() == nullptr) { + return false; + } + if (!dataFunction || !endFunction) { + return false; + } + DataFunction_ = std::move(dataFunction); + EndFunction_ = std::move(endFunction); + int ret = uv_read_start(uv_stream(), &cmUVPipeBuffer::UVAlloc, + &cmUVPipeBuffer::UVData); + return (ret == 0); +} + +void cmUVPipeBuffer::UVAlloc(uv_handle_t* handle, size_t suggestedSize, + uv_buf_t* buf) +{ + auto& pipe = *reinterpret_cast<cmUVPipeBuffer*>(handle->data); + pipe.Buffer_.resize(suggestedSize); + buf->base = pipe.Buffer_.data(); + buf->len = static_cast<unsigned long>(pipe.Buffer_.size()); +} + +void cmUVPipeBuffer::UVData(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) +{ + auto& pipe = *reinterpret_cast<cmUVPipeBuffer*>(stream->data); + if (nread > 0) { + if (buf->base != nullptr) { + // Call data function + pipe.DataFunction_(DataRange(buf->base, buf->base + nread)); + } + } else if (nread < 0) { + // Save the end function on the stack before resetting the pipe + EndFunction efunc; + efunc.swap(pipe.EndFunction_); + // Reset pipe before calling the end function + pipe.reset(); + // Call end function + efunc((nread == UV_EOF) ? 0 : nread); + } +} + +/** + * @brief External process management class + */ +class cmUVReadOnlyProcess +{ +public: + // -- Types + //! @brief Process settings + struct SetupT + { + std::string WorkingDirectory; + std::vector<std::string> Command; + cmWorkerPool::ProcessResultT* Result = nullptr; + bool MergedOutput = false; + }; + +public: + // -- Const accessors + SetupT const& Setup() const { return Setup_; } + cmWorkerPool::ProcessResultT* Result() const { return Setup_.Result; } + bool IsStarted() const { return IsStarted_; } + bool IsFinished() const { return IsFinished_; } + + // -- Runtime + void setup(cmWorkerPool::ProcessResultT* result, bool mergedOutput, + std::vector<std::string> const& command, + std::string const& workingDirectory = std::string()); + bool start(uv_loop_t* uv_loop, std::function<void()> finishedCallback); + +private: + // -- Libuv callbacks + static void UVExit(uv_process_t* handle, int64_t exitStatus, int termSignal); + void UVPipeOutData(cmUVPipeBuffer::DataRange data); + void UVPipeOutEnd(ssize_t error); + void UVPipeErrData(cmUVPipeBuffer::DataRange data); + void UVPipeErrEnd(ssize_t error); + void UVTryFinish(); + +private: + // -- Setup + SetupT Setup_; + // -- Runtime + bool IsStarted_ = false; + bool IsFinished_ = false; + std::function<void()> FinishedCallback_; + std::vector<const char*> CommandPtr_; + std::array<uv_stdio_container_t, 3> UVOptionsStdIO_; + uv_process_options_t UVOptions_; + cm::uv_process_ptr UVProcess_; + cmUVPipeBuffer UVPipeOut_; + cmUVPipeBuffer UVPipeErr_; +}; + +void cmUVReadOnlyProcess::setup(cmWorkerPool::ProcessResultT* result, + bool mergedOutput, + std::vector<std::string> const& command, + std::string const& workingDirectory) +{ + Setup_.WorkingDirectory = workingDirectory; + Setup_.Command = command; + Setup_.Result = result; + Setup_.MergedOutput = mergedOutput; +} + +bool cmUVReadOnlyProcess::start(uv_loop_t* uv_loop, + std::function<void()> finishedCallback) +{ + if (IsStarted() || (Result() == nullptr)) { + return false; + } + + // Reset result before the start + Result()->reset(); + + // Fill command string pointers + if (!Setup().Command.empty()) { + CommandPtr_.reserve(Setup().Command.size() + 1); + for (std::string const& arg : Setup().Command) { + CommandPtr_.push_back(arg.c_str()); + } + CommandPtr_.push_back(nullptr); + } else { + Result()->ErrorMessage = "Empty command"; + } + + if (!Result()->error()) { + if (!UVPipeOut_.init(uv_loop)) { + Result()->ErrorMessage = "libuv stdout pipe initialization failed"; + } + } + if (!Result()->error()) { + if (!UVPipeErr_.init(uv_loop)) { + Result()->ErrorMessage = "libuv stderr pipe initialization failed"; + } + } + if (!Result()->error()) { + // -- Setup process stdio options + // stdin + UVOptionsStdIO_[0].flags = UV_IGNORE; + UVOptionsStdIO_[0].data.stream = nullptr; + // stdout + UVOptionsStdIO_[1].flags = + static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); + UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream(); + // stderr + UVOptionsStdIO_[2].flags = + static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE); + UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream(); + + // -- Setup process options + std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0); + UVOptions_.exit_cb = &cmUVReadOnlyProcess::UVExit; + UVOptions_.file = CommandPtr_[0]; + UVOptions_.args = const_cast<char**>(CommandPtr_.data()); + UVOptions_.cwd = Setup_.WorkingDirectory.c_str(); + UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE; + UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size()); + UVOptions_.stdio = UVOptionsStdIO_.data(); + + // -- Spawn process + int uvErrorCode = UVProcess_.spawn(*uv_loop, UVOptions_, this); + if (uvErrorCode != 0) { + Result()->ErrorMessage = "libuv process spawn failed"; + if (const char* uvErr = uv_strerror(uvErrorCode)) { + Result()->ErrorMessage += ": "; + Result()->ErrorMessage += uvErr; + } + } + } + // -- Start reading from stdio streams + if (!Result()->error()) { + if (!UVPipeOut_.startRead( + [this](cmUVPipeBuffer::DataRange range) { + this->UVPipeOutData(range); + }, + [this](ssize_t error) { this->UVPipeOutEnd(error); })) { + Result()->ErrorMessage = "libuv start reading from stdout pipe failed"; + } + } + if (!Result()->error()) { + if (!UVPipeErr_.startRead( + [this](cmUVPipeBuffer::DataRange range) { + this->UVPipeErrData(range); + }, + [this](ssize_t error) { this->UVPipeErrEnd(error); })) { + Result()->ErrorMessage = "libuv start reading from stderr pipe failed"; + } + } + + if (!Result()->error()) { + IsStarted_ = true; + FinishedCallback_ = std::move(finishedCallback); + } else { + // Clear libuv handles and finish + UVProcess_.reset(); + UVPipeOut_.reset(); + UVPipeErr_.reset(); + CommandPtr_.clear(); + } + + return IsStarted(); +} + +void cmUVReadOnlyProcess::UVExit(uv_process_t* handle, int64_t exitStatus, + int termSignal) +{ + auto& proc = *reinterpret_cast<cmUVReadOnlyProcess*>(handle->data); + if (proc.IsStarted() && !proc.IsFinished()) { + // Set error message on demand + proc.Result()->ExitStatus = exitStatus; + proc.Result()->TermSignal = termSignal; + if (!proc.Result()->error()) { + if (termSignal != 0) { + proc.Result()->ErrorMessage = "Process was terminated by signal "; + proc.Result()->ErrorMessage += + std::to_string(proc.Result()->TermSignal); + } else if (exitStatus != 0) { + proc.Result()->ErrorMessage = "Process failed with return value "; + proc.Result()->ErrorMessage += + std::to_string(proc.Result()->ExitStatus); + } + } + + // Reset process handle + proc.UVProcess_.reset(); + // Try finish + proc.UVTryFinish(); + } +} + +void cmUVReadOnlyProcess::UVPipeOutData(cmUVPipeBuffer::DataRange data) +{ + Result()->StdOut.append(data.begin(), data.end()); +} + +void cmUVReadOnlyProcess::UVPipeOutEnd(ssize_t error) +{ + // Process pipe error + if ((error != 0) && !Result()->error()) { + Result()->ErrorMessage = + "Reading from stdout pipe failed with libuv error code "; + Result()->ErrorMessage += std::to_string(error); + } + // Try finish + UVTryFinish(); +} + +void cmUVReadOnlyProcess::UVPipeErrData(cmUVPipeBuffer::DataRange data) +{ + std::string* str = + Setup_.MergedOutput ? &Result()->StdOut : &Result()->StdErr; + str->append(data.begin(), data.end()); +} + +void cmUVReadOnlyProcess::UVPipeErrEnd(ssize_t error) +{ + // Process pipe error + if ((error != 0) && !Result()->error()) { + Result()->ErrorMessage = + "Reading from stderr pipe failed with libuv error code "; + Result()->ErrorMessage += std::to_string(error); + } + // Try finish + UVTryFinish(); +} + +void cmUVReadOnlyProcess::UVTryFinish() +{ + // There still might be data in the pipes after the process has finished. + // Therefore check if the process is finished AND all pipes are closed + // before signaling the worker thread to continue. + if ((UVProcess_.get() != nullptr) || (UVPipeOut_.uv_pipe() != nullptr) || + (UVPipeErr_.uv_pipe() != nullptr)) { + return; + } + IsFinished_ = true; + FinishedCallback_(); +} + +/** + * @brief Private worker pool internals + */ +class cmWorkerPoolInternal +{ +public: + // -- Types + + /** + * @brief Worker thread + */ + class WorkerT + { + public: + WorkerT(unsigned int index); + ~WorkerT(); + + WorkerT(WorkerT const&) = delete; + WorkerT& operator=(WorkerT const&) = delete; + + /** + * Start the thread + */ + void Start(cmWorkerPoolInternal* internal); + + /** + * @brief Run an external process + */ + bool RunProcess(cmWorkerPool::ProcessResultT& result, + std::vector<std::string> const& command, + std::string const& workingDirectory); + + // -- Accessors + unsigned int Index() const { return Index_; } + cmWorkerPool::JobHandleT& JobHandle() { return JobHandle_; } + + private: + // -- Libuv callbacks + static void UVProcessStart(uv_async_t* handle); + void UVProcessFinished(); + + private: + //! @brief Job handle + cmWorkerPool::JobHandleT JobHandle_; + //! @brief Worker index + unsigned int Index_; + // -- Process management + struct + { + std::mutex Mutex; + cm::uv_async_ptr Request; + std::condition_variable Condition; + std::unique_ptr<cmUVReadOnlyProcess> ROP; + } Proc_; + // -- System thread + std::thread Thread_; + }; + +public: + // -- Constructors + cmWorkerPoolInternal(cmWorkerPool* pool); + ~cmWorkerPoolInternal(); + + /** + * @brief Runs the libuv loop + */ + bool Process(); + + /** + * @brief Clear queue and abort threads + */ + void Abort(); + + /** + * @brief Push a job to the queue and notify a worker + */ + bool PushJob(cmWorkerPool::JobHandleT&& jobHandle); + + /** + * @brief Worker thread main loop method + */ + void Work(WorkerT* worker); + + // -- Request slots + static void UVSlotBegin(uv_async_t* handle); + static void UVSlotEnd(uv_async_t* handle); + +public: + // -- UV loop +#ifdef CMAKE_UV_SIGNAL_HACK + std::unique_ptr<cmUVSignalHackRAII> UVHackRAII; +#endif + std::unique_ptr<uv_loop_t> UVLoop; + cm::uv_async_ptr UVRequestBegin; + cm::uv_async_ptr UVRequestEnd; + + // -- Thread pool and job queue + std::mutex Mutex; + bool Aborting = false; + bool FenceProcessing = false; + unsigned int WorkersRunning = 0; + unsigned int WorkersIdle = 0; + unsigned int JobsProcessing = 0; + std::deque<cmWorkerPool::JobHandleT> Queue; + std::condition_variable Condition; + std::vector<std::unique_ptr<WorkerT>> Workers; + + // -- References + cmWorkerPool* Pool = nullptr; +}; + +cmWorkerPoolInternal::WorkerT::WorkerT(unsigned int index) + : Index_(index) +{ +} + +cmWorkerPoolInternal::WorkerT::~WorkerT() +{ + if (Thread_.joinable()) { + Thread_.join(); + } +} + +void cmWorkerPoolInternal::WorkerT::Start(cmWorkerPoolInternal* internal) +{ + Proc_.Request.init(*(internal->UVLoop), &WorkerT::UVProcessStart, this); + Thread_ = std::thread(&cmWorkerPoolInternal::Work, internal, this); +} + +bool cmWorkerPoolInternal::WorkerT::RunProcess( + cmWorkerPool::ProcessResultT& result, + std::vector<std::string> const& command, std::string const& workingDirectory) +{ + if (command.empty()) { + return false; + } + // Create process instance + { + std::lock_guard<std::mutex> lock(Proc_.Mutex); + Proc_.ROP = cm::make_unique<cmUVReadOnlyProcess>(); + Proc_.ROP->setup(&result, true, command, workingDirectory); + } + // Send asynchronous process start request to libuv loop + Proc_.Request.send(); + // Wait until the process has been finished and destroyed + { + std::unique_lock<std::mutex> ulock(Proc_.Mutex); + while (Proc_.ROP) { + Proc_.Condition.wait(ulock); + } + } + return !result.error(); +} + +void cmWorkerPoolInternal::WorkerT::UVProcessStart(uv_async_t* handle) +{ + auto* wrk = reinterpret_cast<WorkerT*>(handle->data); + bool startFailed = false; + { + auto& Proc = wrk->Proc_; + std::lock_guard<std::mutex> lock(Proc.Mutex); + if (Proc.ROP && !Proc.ROP->IsStarted()) { + startFailed = + !Proc.ROP->start(handle->loop, [wrk] { wrk->UVProcessFinished(); }); + } + } + // Clean up if starting of the process failed + if (startFailed) { + wrk->UVProcessFinished(); + } +} + +void cmWorkerPoolInternal::WorkerT::UVProcessFinished() +{ + { + std::lock_guard<std::mutex> lock(Proc_.Mutex); + if (Proc_.ROP && (Proc_.ROP->IsFinished() || !Proc_.ROP->IsStarted())) { + Proc_.ROP.reset(); + } + } + // Notify idling thread + Proc_.Condition.notify_one(); +} + +void cmWorkerPool::ProcessResultT::reset() +{ + ExitStatus = 0; + TermSignal = 0; + if (!StdOut.empty()) { + StdOut.clear(); + StdOut.shrink_to_fit(); + } + if (!StdErr.empty()) { + StdErr.clear(); + StdErr.shrink_to_fit(); + } + if (!ErrorMessage.empty()) { + ErrorMessage.clear(); + ErrorMessage.shrink_to_fit(); + } +} + +cmWorkerPoolInternal::cmWorkerPoolInternal(cmWorkerPool* pool) + : Pool(pool) +{ + // Initialize libuv loop + uv_disable_stdio_inheritance(); +#ifdef CMAKE_UV_SIGNAL_HACK + UVHackRAII = cm::make_unique<cmUVSignalHackRAII>(); +#endif + UVLoop = cm::make_unique<uv_loop_t>(); + uv_loop_init(UVLoop.get()); +} + +cmWorkerPoolInternal::~cmWorkerPoolInternal() +{ + uv_loop_close(UVLoop.get()); +} + +bool cmWorkerPoolInternal::Process() +{ + // Reset state + Aborting = false; + // Initialize libuv asynchronous request + UVRequestBegin.init(*UVLoop, &cmWorkerPoolInternal::UVSlotBegin, this); + UVRequestEnd.init(*UVLoop, &cmWorkerPoolInternal::UVSlotEnd, this); + // Send begin request + UVRequestBegin.send(); + // Run libuv loop + return (uv_run(UVLoop.get(), UV_RUN_DEFAULT) == 0); +} + +void cmWorkerPoolInternal::Abort() +{ + bool firstCall = false; + // Clear all jobs and set abort flag + { + std::lock_guard<std::mutex> guard(Mutex); + if (!Aborting) { + // Register abort and clear queue + Aborting = true; + Queue.clear(); + firstCall = true; + } + } + if (firstCall) { + // Wake threads + Condition.notify_all(); + } +} + +inline bool cmWorkerPoolInternal::PushJob(cmWorkerPool::JobHandleT&& jobHandle) +{ + std::lock_guard<std::mutex> guard(Mutex); + if (Aborting) { + return false; + } + + // Append the job to the queue + Queue.emplace_back(std::move(jobHandle)); + + // Notify an idle worker if there's one + if (WorkersIdle != 0) { + Condition.notify_one(); + } + + return true; +} + +void cmWorkerPoolInternal::UVSlotBegin(uv_async_t* handle) +{ + auto& gint = *reinterpret_cast<cmWorkerPoolInternal*>(handle->data); + // Create worker threads + { + unsigned int const num = gint.Pool->ThreadCount(); + // Create workers + gint.Workers.reserve(num); + for (unsigned int ii = 0; ii != num; ++ii) { + gint.Workers.emplace_back(cm::make_unique<WorkerT>(ii)); + } + // Start workers + for (auto& wrk : gint.Workers) { + wrk->Start(&gint); + } + } + // Destroy begin request + gint.UVRequestBegin.reset(); +} + +void cmWorkerPoolInternal::UVSlotEnd(uv_async_t* handle) +{ + auto& gint = *reinterpret_cast<cmWorkerPoolInternal*>(handle->data); + // Join and destroy worker threads + gint.Workers.clear(); + // Destroy end request + gint.UVRequestEnd.reset(); +} + +void cmWorkerPoolInternal::Work(WorkerT* worker) +{ + std::unique_lock<std::mutex> uLock(Mutex); + // Increment running workers count + ++WorkersRunning; + // Enter worker main loop + while (true) { + // Abort on request + if (Aborting) { + break; + } + // Wait for new jobs + if (Queue.empty()) { + ++WorkersIdle; + Condition.wait(uLock); + --WorkersIdle; + continue; + } + + // Check for fence jobs + if (FenceProcessing || Queue.front()->IsFence()) { + if (JobsProcessing != 0) { + Condition.wait(uLock); + continue; + } + // No jobs get processed. Set the fence job processing flag. + FenceProcessing = true; + } + + // Pop next job from queue + worker->JobHandle() = std::move(Queue.front()); + Queue.pop_front(); + + // Unlocked scope for job processing + ++JobsProcessing; + { + uLock.unlock(); + worker->JobHandle()->Work(Pool, worker->Index()); // Process job + worker->JobHandle().reset(); // Destroy job + uLock.lock(); + } + --JobsProcessing; + + // Was this a fence job? + if (FenceProcessing) { + FenceProcessing = false; + Condition.notify_all(); + } + } + + // Decrement running workers count + if (--WorkersRunning == 0) { + // Last worker thread about to finish. Send libuv event. + UVRequestEnd.send(); + } +} + +cmWorkerPool::JobT::~JobT() = default; + +bool cmWorkerPool::JobT::RunProcess(ProcessResultT& result, + std::vector<std::string> const& command, + std::string const& workingDirectory) +{ + // Get worker by index + auto* wrk = Pool_->Int_->Workers.at(WorkerIndex_).get(); + return wrk->RunProcess(result, command, workingDirectory); +} + +cmWorkerPool::cmWorkerPool() + : Int_(cm::make_unique<cmWorkerPoolInternal>(this)) +{ +} + +cmWorkerPool::~cmWorkerPool() = default; + +bool cmWorkerPool::Process(unsigned int threadCount, void* userData) +{ + // Setup user data + UserData_ = userData; + ThreadCount_ = (threadCount > 0) ? threadCount : 1u; + + // Run libuv loop + bool success = Int_->Process(); + + // Clear user data + UserData_ = nullptr; + ThreadCount_ = 0; + + return success; +} + +bool cmWorkerPool::PushJob(JobHandleT&& jobHandle) +{ + return Int_->PushJob(std::move(jobHandle)); +} + +void cmWorkerPool::Abort() +{ + Int_->Abort(); +} diff --git a/Source/cmWorkerPool.h b/Source/cmWorkerPool.h new file mode 100644 index 0000000..71c7d84 --- /dev/null +++ b/Source/cmWorkerPool.h @@ -0,0 +1,219 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmWorkerPool_h +#define cmWorkerPool_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmAlgorithms.h" // IWYU pragma: keep + +#include <memory> // IWYU pragma: keep +#include <stdint.h> +#include <string> +#include <utility> +#include <vector> + +// -- Types +class cmWorkerPoolInternal; + +/** @class cmWorkerPool + * @brief Thread pool with job queue + */ +class cmWorkerPool +{ +public: + /** + * Return value and output of an external process. + */ + struct ProcessResultT + { + void reset(); + bool error() const + { + return (ExitStatus != 0) || (TermSignal != 0) || !ErrorMessage.empty(); + } + + std::int64_t ExitStatus = 0; + int TermSignal = 0; + std::string StdOut; + std::string StdErr; + std::string ErrorMessage; + }; + + /** + * Abstract job class for concurrent job processing. + */ + class JobT + { + public: + JobT(JobT const&) = delete; + JobT& operator=(JobT const&) = delete; + + /** + * @brief Virtual destructor. + */ + virtual ~JobT(); + + /** + * @brief Fence job flag + * + * Fence jobs require that: + * - all jobs before in the queue have been processed + * - no jobs later in the queue will be processed before this job was + * processed + */ + bool IsFence() const { return Fence_; } + + protected: + /** + * @brief Protected default constructor + */ + JobT(bool fence = false) + : Fence_(fence) + { + } + + /** + * Abstract processing interface that must be implement in derived classes. + */ + virtual void Process() = 0; + + /** + * Get the worker pool. + * Only valid during the JobT::Process() call! + */ + cmWorkerPool* Pool() const { return Pool_; } + + /** + * Get the user data. + * Only valid during the JobT::Process() call! + */ + void* UserData() const { return Pool_->UserData(); }; + + /** + * Get the worker index. + * This is the index of the thread processing this job and is in the range + * [0..ThreadCount). + * Concurrently processing jobs will never have the same WorkerIndex(). + * Only valid during the JobT::Process() call! + */ + unsigned int WorkerIndex() const { return WorkerIndex_; } + + /** + * Run an external read only process. + * Use only during JobT::Process() call! + */ + bool RunProcess(ProcessResultT& result, + std::vector<std::string> const& command, + std::string const& workingDirectory); + + private: + //! Needs access to Work() + friend class cmWorkerPoolInternal; + //! Worker thread entry method. + void Work(cmWorkerPool* pool, unsigned int workerIndex) + { + Pool_ = pool; + WorkerIndex_ = workerIndex; + this->Process(); + } + + private: + cmWorkerPool* Pool_ = nullptr; + unsigned int WorkerIndex_ = 0; + bool Fence_ = false; + }; + + /** + * @brief Job handle type + */ + typedef std::unique_ptr<JobT> JobHandleT; + + /** + * @brief Fence job base class + */ + class JobFenceT : public JobT + { + public: + JobFenceT() + : JobT(true) + { + } + //! Does nothing + void Process() override{}; + }; + + /** + * @brief Fence job that aborts the worker pool. + * This class is useful as the last job in the job queue. + */ + class JobEndT : JobFenceT + { + public: + //! Does nothing + void Process() override { Pool()->Abort(); } + }; + +public: + // -- Methods + cmWorkerPool(); + ~cmWorkerPool(); + + /** + * @brief Blocking function that starts threads to process all Jobs in + * the queue. + * + * This method blocks until a job calls the Abort() method. + * @arg threadCount Number of threads to process jobs. + * @arg userData Common user data pointer available in all Jobs. + */ + bool Process(unsigned int threadCount, void* userData = nullptr); + + /** + * Number of worker threads passed to Process(). + * Only valid during Process(). + */ + unsigned int ThreadCount() const { return ThreadCount_; } + + /** + * User data reference passed to Process(). + * Only valid during Process(). + */ + void* UserData() const { return UserData_; } + + // -- Job processing interface + + /** + * @brief Clears the job queue and aborts all worker threads. + * + * This method is thread safe and can be called from inside a job. + */ + void Abort(); + + /** + * @brief Push job to the queue. + * + * This method is thread safe and can be called from inside a job or before + * Process(). + */ + bool PushJob(JobHandleT&& jobHandle); + + /** + * @brief Push job to the queue + * + * This method is thread safe and can be called from inside a job or before + * Process(). + */ + template <class T, typename... Args> + bool EmplaceJob(Args&&... args) + { + return PushJob(cm::make_unique<T>(std::forward<Args>(args)...)); + } + +private: + void* UserData_ = nullptr; + unsigned int ThreadCount_ = 0; + std::unique_ptr<cmWorkerPoolInternal> Int_; +}; + +#endif diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index c18c256..3c75957 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -7,7 +7,7 @@ #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" -#include "cmQtAutoGeneratorMocUic.h" +#include "cmQtAutoMocUic.h" #include "cmQtAutoRcc.h" #include "cmRange.h" #include "cmState.h" @@ -1018,7 +1018,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args) #ifdef CMAKE_BUILD_WITH_CMAKE if ((args[1] == "cmake_autogen") && (args.size() >= 4)) { - cmQtAutoGeneratorMocUic autoGen; + cmQtAutoMocUic autoGen; std::string const& infoDir = args[2]; std::string const& config = args[3]; return autoGen.Run(infoDir, config) ? 0 : 1; diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 3746965..2e0902c 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -2336,7 +2336,7 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release endmacro() macro(add_test_GhsMulti_rename_install test_name) add_test_GhsMulti( ${test_name} GhsMultiRenameInstall ${test_name} - "-DCMAKE_INSTALL_PREFIX=. -DRUN_TEST=${test_name}" ${CMAKE_CMAKE_COMMAND} -P ./cmake_install.cmake) + "-DCMAKE_INSTALL_PREFIX=. -DRUN_TEST=${test_name}" ${CMAKE_CMAKE_COMMAND} --build . --target install) endmacro() #unset ghs config variables unset(ghs_config_name) @@ -2391,6 +2391,9 @@ ${CMake_BINARY_DIR}/bin/cmake -DDIR=dev -P ${CMake_SOURCE_DIR}/Utilities/Release add_test_GhsMulti(compiler_options_kernel GhsMultiCompilerOptions Kernel "-DRUN_TEST=KERNEL_FLAGS -DRUN_TEST_BUILD_TYPE=DEBUG") add_test_GhsMulti(try_compile_copy GhsMultiCopyFile "" "") add_test_GhsMulti(ghs_platform GhsMultiPlatform "" "") + add_test_GhsMulti(custom_target GhsMultiCustomTarget "" "") + add_test_GhsMulti(dep_order GhsMultiDepOrder "" "") + add_test_GhsMulti(external_project GhsMultiExternalProject "" "") list(APPEND TEST_BUILD_DIRS "${CMake_BINARY_DIR}/Tests/GhsMulti/${ghs_config_name}") #unset ghs config variables unset(ghs_config_name) diff --git a/Tests/FindBoost/CMakeLists.txt b/Tests/FindBoost/CMakeLists.txt index 17a8ec7..58d795b 100644 --- a/Tests/FindBoost/CMakeLists.txt +++ b/Tests/FindBoost/CMakeLists.txt @@ -33,3 +33,16 @@ add_test(NAME FindBoost.TestHeaders COMMAND --build-options ${build_options} --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION> ) + +if (CMake_TEST_FindBoost_Python) + add_test(NAME FindBoost.TestPython COMMAND + ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION> + --build-and-test + "${CMake_SOURCE_DIR}/Tests/FindBoost/TestPython" + "${CMake_BINARY_DIR}/Tests/FindBoost/TestPython" + ${build_generator_args} + --build-project TestFindBoostPython + --build-options ${build_options} + --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION> + ) +endif () diff --git a/Tests/FindBoost/TestPython/CMakeLists.txt b/Tests/FindBoost/TestPython/CMakeLists.txt new file mode 100644 index 0000000..4d137ca --- /dev/null +++ b/Tests/FindBoost/TestPython/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(TestFindBoostPython CXX) +include(CTest) + +find_package(Boost OPTIONAL_COMPONENTS python27 python34 python35 python36 python37) + +set(FAILTEST TRUE) +foreach (v IN ITEMS 27 34 35 36 37) + if (Boost_PYTHON${v}_FOUND) + set(FAILTEST FALSE) + break() + endif () +endforeach () + +if (FAILTEST) + message(FATAL_ERROR "No Boost Python module found") +endif () diff --git a/Tests/GhsMulti/GhsMultiCustomTarget/CMakeLists.txt b/Tests/GhsMulti/GhsMultiCustomTarget/CMakeLists.txt new file mode 100644 index 0000000..93d668b --- /dev/null +++ b/Tests/GhsMulti/GhsMultiCustomTarget/CMakeLists.txt @@ -0,0 +1,110 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) + +project(test C) + +# Tests assume no previous builds in the build directory +file(REMOVE_RECURSE ${CMAKE_CURRENT_BINARY_DIR}/build) + +macro (test_output) + if (BUILD_OUTPUT STREQUAL EXPECTED_LINES ) + message("Build OK") + else() + message("BUILD_OUTPUT") + foreach(Line IN LISTS BUILD_OUTPUT) + message("${Line}") + endforeach() + message("EXPECTED_LINES") + foreach(Line IN LISTS EXPECTED_LINES) + message("${Line}") + endforeach() + message(SEND_ERROR "Build KO") + endif() +endmacro() + +message("Copy project") +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt.in + ${CMAKE_CURRENT_BINARY_DIR}/src/CMakeLists.txt COPYONLY) + +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/exe1.c + ${CMAKE_CURRENT_SOURCE_DIR}/lib1.c + DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/src +) + +message("Building ALL target") +try_compile(RESULT + ${CMAKE_CURRENT_BINARY_DIR}/build + ${CMAKE_CURRENT_BINARY_DIR}/src + test + CMAKE_FLAGS + -DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS} + OUTPUT_VARIABLE BUILD_OUTPUT) + +message("Output from build:\n${BUILD_OUTPUT}") + +#filter outputs +string(REPLACE "\r" "" BUILD_OUTPUT "${BUILD_OUTPUT}") +string(REPLACE "\n" ";" BUILD_OUTPUT "${BUILD_OUTPUT}") +list(FILTER BUILD_OUTPUT INCLUDE REGEX "^.*CT:") + +unset(EXPECTED_LINES) +list(APPEND EXPECTED_LINES "CT: Processing target_empty_prebuild") +list(APPEND EXPECTED_LINES "CT: Processing target_empty_postbuild") +list(APPEND EXPECTED_LINES "CT: Processing target_cmd") +list(APPEND EXPECTED_LINES "CT: Processing target_cmd_prebuild") +list(APPEND EXPECTED_LINES "CT: Processing target_cmd_postbuild") + +test_output() + +message("Building target_update_files target") +try_compile(RESULT + ${CMAKE_CURRENT_BINARY_DIR}/build + ${CMAKE_CURRENT_BINARY_DIR}/src + test target_update_files + CMAKE_FLAGS + -DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS} + OUTPUT_VARIABLE BUILD_OUTPUT) + +message("Output from build:\n${BUILD_OUTPUT}") + +#filter outputs +string(REPLACE "\r" "" BUILD_OUTPUT "${BUILD_OUTPUT}") +string(REPLACE "\n" ";" BUILD_OUTPUT "${BUILD_OUTPUT}") +list(FILTER BUILD_OUTPUT INCLUDE REGEX "^.*CT:") + +unset(EXPECTED_LINES) +list(APPEND EXPECTED_LINES "CT: Processing target_empty_prebuild") +list(APPEND EXPECTED_LINES "CT: Processing target_empty_postbuild") +list(APPEND EXPECTED_LINES "CT: generate C file another_file") +list(APPEND EXPECTED_LINES "CT: generate text file dependsA") +list(APPEND EXPECTED_LINES "CT: generate text file out_of_order_dep") +list(APPEND EXPECTED_LINES "CT: generate text files A, B, and C") +list(APPEND EXPECTED_LINES "CT: Processing target_update_files") + +test_output() + +message("Rerun target_update_files target") +try_compile(RESULT + ${CMAKE_CURRENT_BINARY_DIR}/build + ${CMAKE_CURRENT_BINARY_DIR}/src + test target_update_files + CMAKE_FLAGS + -DCMAKE_EXE_LINKER_FLAGS=${CMAKE_EXE_LINKER_FLAGS} + OUTPUT_VARIABLE BUILD_OUTPUT) + +message("Output from build:\n${BUILD_OUTPUT}") + +#filter outputs +string(REPLACE "\r" "" BUILD_OUTPUT "${BUILD_OUTPUT}") +string(REPLACE "\n" ";" BUILD_OUTPUT "${BUILD_OUTPUT}") +list(FILTER BUILD_OUTPUT INCLUDE REGEX "^.*CT:") + +unset(EXPECTED_LINES) +list(APPEND EXPECTED_LINES "CT: Processing target_empty_prebuild") +list(APPEND EXPECTED_LINES "CT: Processing target_empty_postbuild") +list(APPEND EXPECTED_LINES "CT: generate text files A, B, and C") +list(APPEND EXPECTED_LINES "CT: Processing target_update_files") + +test_output() diff --git a/Tests/GhsMulti/GhsMultiCustomTarget/CMakeLists.txt.in b/Tests/GhsMulti/GhsMultiCustomTarget/CMakeLists.txt.in new file mode 100644 index 0000000..fed946c --- /dev/null +++ b/Tests/GhsMulti/GhsMultiCustomTarget/CMakeLists.txt.in @@ -0,0 +1,108 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) + +project(test C) + +if(CMAKE_C_COMPILER_ID STREQUAL "GHS") + add_link_options("-non_shared") +endif() + +add_library(lib1 lib1.c) + +set(TEST_MISSING_TARGET_SRC 0) +set(TEST_MISSING_TARGET_DEP 0) +set(TEST_MISSING_DEP 0) +set(TEST_DEP_CYCLE 0) + +add_executable(exe1 exe1.c) +target_link_libraries(exe1 lib1) + +add_custom_target(target_cmd ALL + COMMAND ${CMAKE_COMMAND} -E echo "target_cmd" > target_cmd + COMMAND ${CMAKE_COMMAND} -E echo "target_cmd_extra" > target_cmd_extra.txt + BYPRODUCTS target_cmd target_cmd_extra.txt + COMMENT "CT: Processing target_cmd") + +add_custom_command(TARGET target_cmd PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "target_cmd_prebuild" > target_cmd_prebuild.txt + BYPRODUCTS target_cmd_prebuild.txt + COMMENT "CT: Processing target_cmd_prebuild") +#event does not run for custom targets +add_custom_command(TARGET target_cmd PRE_LINK + COMMAND ${CMAKE_COMMAND} -E echo "executing target_cmd_prelink commands" + COMMAND ${CMAKE_COMMAND} -E echo "target_cmd_prelink" > target_cmd_prelink.txt + BYPRODUCTS target_cmd_prelink.txt + COMMENT "CT: Processing target_cmd_prelink") +add_custom_command(TARGET target_cmd POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "executing target_cmd_postbuild commands" + COMMAND ${CMAKE_COMMAND} -E echo "target_cmd_postbuild" > target_cmd_postbuild.txt + BYPRODUCTS target_cmd_postbuild.txt + COMMENT "CT: Processing target_cmd_postbuild") + +add_custom_target(target_empty ALL + COMMENT "CT: Processing target_empty") + +add_custom_command(TARGET target_empty PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "target_empty_prebuild" > target_empty_prebuild.txt + BYPRODUCTS target_empty_prebuild.txt + COMMENT "CT: Processing target_empty_prebuild") +add_custom_command(TARGET target_empty POST_BUILD + COMMAND ${CMAKE_COMMAND} -E echo "target_empty_postbuild" > target_empty_postbuild.txt + BYPRODUCTS target_empty_postbuild.txt + COMMENT "CT: Processing target_empty_postbuild") + +add_dependencies(target_cmd target_empty) + +add_custom_command( + OUTPUT out_of_order_dep.txt + COMMAND ${CMAKE_COMMAND} -E echo "out_of_order_dep" > out_of_order_dep.txt + COMMENT "CT: generate text file out_of_order_dep" + DEPENDS dependsA.txt +) + +if(TEST_MISSING_TARGET_SRC) + set(SRC_FILE does_not_exist) +endif() +if(TEST_MISSING_TARGET_DEP) + set(DEP_FILE does_not_exist) +endif() + +add_custom_target(target_update_files + DEPENDS genc_do_not_list.txt ${DEP_FILE} + SOURCES gena.txt genb.txt another_file.c ${SRC_FILE} + BYPRODUCTS junkit.txt + COMMAND ${CMAKE_COMMAND} -E copy another_file.c junkit.txt + COMMENT "CT: Processing target_update_files") + +add_custom_command( + OUTPUT force_rebuild gena.txt genb.txt genc_do_not_list.txt + COMMAND ${CMAKE_COMMAND} -E copy dependsA.txt gena.txt + COMMAND ${CMAKE_COMMAND} -E echo "genb" > genb.txt + COMMAND ${CMAKE_COMMAND} -E echo "genc" > genc_do_not_list.txt + DEPENDS out_of_order_dep.txt dependsA.txt + COMMENT "CT: generate text files A, B, and C" +) + +if(TEST_MISSING_DEP) + set(MISSING_DEP MISSING_DEP) +endif() +if(TEST_DEP_CYCLE) + set(DEP_CYCLE out_of_order_dep.txt) +endif() + +add_custom_command( + OUTPUT dependsA.txt + COMMAND ${CMAKE_COMMAND} -E echo "dependsA" > dependsA.txt + DEPENDS ${MISSING_DEP} ${DEP_CYCLE} another_file.c + COMMENT "CT: generate text file dependsA" +) + +add_custom_command( + OUTPUT another_file.c + COMMAND ${CMAKE_COMMAND} -E echo "//auto-gen file" > another_file.c + COMMENT "CT: generate C file another_file" +) + +add_dependencies(target_update_files target_empty) diff --git a/Tests/GhsMulti/GhsMultiCustomTarget/exe1.c b/Tests/GhsMulti/GhsMultiCustomTarget/exe1.c new file mode 100644 index 0000000..29ad70a --- /dev/null +++ b/Tests/GhsMulti/GhsMultiCustomTarget/exe1.c @@ -0,0 +1,5 @@ +extern int func(void); +int main(void) +{ + return func(); +} diff --git a/Tests/GhsMulti/GhsMultiCustomTarget/lib1.c b/Tests/GhsMulti/GhsMultiCustomTarget/lib1.c new file mode 100644 index 0000000..b35e9cc --- /dev/null +++ b/Tests/GhsMulti/GhsMultiCustomTarget/lib1.c @@ -0,0 +1,4 @@ +int func(void) +{ + return 2; +} diff --git a/Tests/GhsMulti/GhsMultiDepOrder/CMakeLists.txt b/Tests/GhsMulti/GhsMultiDepOrder/CMakeLists.txt new file mode 100644 index 0000000..2e2871b --- /dev/null +++ b/Tests/GhsMulti/GhsMultiDepOrder/CMakeLists.txt @@ -0,0 +1,12 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) + +project(test C) + +#set_property( GLOBAL PROPERTY GLOBAL_DEPENDS_DEBUG_MODE 1) +add_subdirectory(exec) +add_subdirectory(lib) +add_subdirectory(protolib) +add_dependencies(lib1 proto) diff --git a/Tests/GhsMulti/GhsMultiDepOrder/exec/CMakeLists.txt b/Tests/GhsMulti/GhsMultiDepOrder/exec/CMakeLists.txt new file mode 100644 index 0000000..85ee805 --- /dev/null +++ b/Tests/GhsMulti/GhsMultiDepOrder/exec/CMakeLists.txt @@ -0,0 +1,11 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) + +add_executable(exe1 exe1.c) +target_link_libraries(exe1 lib1) +target_include_directories(exe1 PRIVATE "${test_BINARY_DIR}") +if(CMAKE_C_COMPILER_ID STREQUAL "GHS") + target_link_options(exe1 PRIVATE "-non_shared") +endif() diff --git a/Tests/GhsMulti/GhsMultiDepOrder/exec/exe1.c b/Tests/GhsMulti/GhsMultiDepOrder/exec/exe1.c new file mode 100644 index 0000000..fbf4ed4 --- /dev/null +++ b/Tests/GhsMulti/GhsMultiDepOrder/exec/exe1.c @@ -0,0 +1,8 @@ +#include "lib1.h" +#include "p.h" + +int main(void) +{ + return func1() + func2() + func3() + func1p() + func2p() + func3p() + + PROTO1 + PROTO2 + PROTO3; +} diff --git a/Tests/GhsMulti/GhsMultiDepOrder/lib/CMakeLists.txt b/Tests/GhsMulti/GhsMultiDepOrder/lib/CMakeLists.txt new file mode 100644 index 0000000..ae30fa2 --- /dev/null +++ b/Tests/GhsMulti/GhsMultiDepOrder/lib/CMakeLists.txt @@ -0,0 +1,17 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +add_library(lib1 STATIC + func1.c lib1.h + "${test_BINARY_DIR}/protolib/proto1.c" + "${test_BINARY_DIR}/protolib/proto1.h") +set_source_files_properties( + "${test_BINARY_DIR}/protolib/proto1.c" + "${test_BINARY_DIR}/protolib/proto1.h" + PROPERTIES GENERATED 1) +target_include_directories(lib1 PRIVATE "${test_BINARY_DIR}/protolib" + PUBLIC .) +add_custom_command( TARGET lib1 POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy "${test_BINARY_DIR}/protolib/proto1.h" "${test_BINARY_DIR}/p.h" + COMMENT "Copy ${test_BINARY_DIR}/protolib/proto1.h ${test_BINARY_DIR}/p.h" + BYPRODUCTS "${test_BINARY_DIR}/p.h") diff --git a/Tests/GhsMulti/GhsMultiDepOrder/lib/func1.c b/Tests/GhsMulti/GhsMultiDepOrder/lib/func1.c new file mode 100644 index 0000000..53334fe --- /dev/null +++ b/Tests/GhsMulti/GhsMultiDepOrder/lib/func1.c @@ -0,0 +1,17 @@ +#include "lib1.h" +#include "proto1.h" + +int func1(void) +{ + return 1 + PROTO1; +} + +int func2(void) +{ + return 2 + PROTO2; +} + +int func3(void) +{ + return 3 + PROTO3; +} diff --git a/Tests/GhsMulti/GhsMultiDepOrder/lib/lib1.h b/Tests/GhsMulti/GhsMultiDepOrder/lib/lib1.h new file mode 100644 index 0000000..5e99f02 --- /dev/null +++ b/Tests/GhsMulti/GhsMultiDepOrder/lib/lib1.h @@ -0,0 +1,3 @@ +extern int func1(void); +extern int func2(void); +extern int func3(void); diff --git a/Tests/GhsMulti/GhsMultiDepOrder/protolib/CMakeLists.txt b/Tests/GhsMulti/GhsMultiDepOrder/protolib/CMakeLists.txt new file mode 100644 index 0000000..8cb6869 --- /dev/null +++ b/Tests/GhsMulti/GhsMultiDepOrder/protolib/CMakeLists.txt @@ -0,0 +1,28 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) + +add_custom_target(proto ALL + DEPENDS proto1.c + proto1.h + SOURCES + ${test_SOURCE_DIR}/protolib/proto1.c.in + ${test_SOURCE_DIR}/protolib/proto1.h.in + COMMENT "Creating proto files") + +add_custom_command( + OUTPUT proto1.c + COMMAND ${CMAKE_COMMAND} -E copy + ${test_SOURCE_DIR}/protolib/proto1.c.in proto1.c + DEPENDS ${test_SOURCE_DIR}/protolib/proto1.c.in + COMMENT "generate proto C files" +) + +add_custom_command( + OUTPUT proto1.h + COMMAND ${CMAKE_COMMAND} -E copy + ${test_SOURCE_DIR}/protolib/proto1.h.in proto1.h + DEPENDS ${test_SOURCE_DIR}/protolib/proto1.h.in + COMMENT "generate proto H files" +) diff --git a/Tests/GhsMulti/GhsMultiDepOrder/protolib/proto1.c.in b/Tests/GhsMulti/GhsMultiDepOrder/protolib/proto1.c.in new file mode 100644 index 0000000..0efb1bd --- /dev/null +++ b/Tests/GhsMulti/GhsMultiDepOrder/protolib/proto1.c.in @@ -0,0 +1,16 @@ +#include "proto1.h" + +int func1p(void) +{ + return 1; +} + +int func2p(void) +{ + return 2; +} + +int func3p(void) +{ + return 3; +} diff --git a/Tests/GhsMulti/GhsMultiDepOrder/protolib/proto1.h.in b/Tests/GhsMulti/GhsMultiDepOrder/protolib/proto1.h.in new file mode 100644 index 0000000..f2f93af --- /dev/null +++ b/Tests/GhsMulti/GhsMultiDepOrder/protolib/proto1.h.in @@ -0,0 +1,7 @@ +extern int func1p(void); +extern int func2p(void); +extern int func3p(void); + +#define PROTO1 0x1 +#define PROTO2 0x2 +#define PROTO3 0x3 diff --git a/Tests/GhsMulti/GhsMultiExternalProject/CMakeLists.txt b/Tests/GhsMulti/GhsMultiExternalProject/CMakeLists.txt new file mode 100644 index 0000000..24126c8 --- /dev/null +++ b/Tests/GhsMulti/GhsMultiExternalProject/CMakeLists.txt @@ -0,0 +1,14 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) + +project(test C) +include(ExternalProject) + +ExternalProject_Add(another_project + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/empty + BINARY_DIR empty_build + INSTALL_COMMAND "" + TEST_COMMAND "" + ) diff --git a/Tests/GhsMulti/GhsMultiExternalProject/empty/CMakeLists.txt b/Tests/GhsMulti/GhsMultiExternalProject/empty/CMakeLists.txt new file mode 100644 index 0000000..6846a98 --- /dev/null +++ b/Tests/GhsMulti/GhsMultiExternalProject/empty/CMakeLists.txt @@ -0,0 +1,8 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) + +project(empty NONE) + +message("EMPTY PROJECT") diff --git a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/App/CMakeLists.txt b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/App/CMakeLists.txt index e431217..3837b5a 100644 --- a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/App/CMakeLists.txt +++ b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/App/CMakeLists.txt @@ -1,4 +1,3 @@ add_executable(App Main.c) -target_include_directories(App PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../Lib) target_link_libraries(App Lib) target_compile_options(App PUBLIC "-non_shared") diff --git a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/Lib/CMakeLists.txt b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/Lib/CMakeLists.txt index 00e0f59..bb9849a 100644 --- a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/Lib/CMakeLists.txt +++ b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityDDInt/Lib/CMakeLists.txt @@ -1 +1,2 @@ add_library(Lib HelperFun.c HelperFun.h) +target_include_directories(Lib PUBLIC .) diff --git a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/CMakeLists.txt b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/CMakeLists.txt index c5db155..3f2f0eb 100644 --- a/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/CMakeLists.txt +++ b/Tests/GhsMulti/GhsMultiIntegrity/GhsMultiIntegrityMonolith/CMakeLists.txt @@ -18,3 +18,4 @@ target_link_options(kernel PRIVATE -kernel) # create monolith INTEGRITY application add_executable(monolith test.int) +add_dependencies(monolith vas) diff --git a/Tests/GhsMulti/GhsMultiPlatform/file1.c b/Tests/GhsMulti/GhsMultiPlatform/file1.c deleted file mode 100644 index 4132aa4..0000000 --- a/Tests/GhsMulti/GhsMultiPlatform/file1.c +++ /dev/null @@ -1,4 +0,0 @@ -int main(void) -{ - return -42; -} diff --git a/Tests/GhsMulti/GhsMultiRenameInstall/CMakeLists.txt b/Tests/GhsMulti/GhsMultiRenameInstall/CMakeLists.txt index f5792b4..b2540d9 100644 --- a/Tests/GhsMulti/GhsMultiRenameInstall/CMakeLists.txt +++ b/Tests/GhsMulti/GhsMultiRenameInstall/CMakeLists.txt @@ -13,7 +13,7 @@ endif() if(RUN_TEST STREQUAL "SINGLE_EXEC") add_executable(exe1 exe.c) - set(targets_to_install ${targets_to_install} exe1) + set(targets_to_install exe1) endif() if(RUN_TEST STREQUAL "SINGLE_EXEC_RENAMED") @@ -22,7 +22,7 @@ if(RUN_TEST STREQUAL "SINGLE_EXEC_RENAMED") set_property(TARGET exe1 PROPERTY RUNTIME_OUTPUT_DIRECTORY ${name}_bin_$<CONFIG>) set_property(TARGET exe1 PROPERTY OUTPUT_NAME ${name}_$<CONFIG>) set_property(TARGET exe1 PROPERTY SUFFIX .bin) - set(targets_to_install ${targets_to_install} exe1) + set(targets_to_install exe1) endif() if(RUN_TEST STREQUAL "EXEC_AND_LIB") @@ -33,7 +33,7 @@ if(RUN_TEST STREQUAL "EXEC_AND_LIB") add_executable(exe1 exe1.c) target_link_libraries(exe1 lib1) - set(targets_to_install ${targets_to_install} exe1 lib1) + set(targets_to_install exe1 lib1) endif() install(TARGETS ${targets_to_install} diff --git a/Tests/GhsMulti/GhsMultiUnsupportedTargets/CMakeLists.txt b/Tests/GhsMulti/GhsMultiUnsupportedTargets/CMakeLists.txt index ed3094b..f5f3c55 100644 --- a/Tests/GhsMulti/GhsMultiUnsupportedTargets/CMakeLists.txt +++ b/Tests/GhsMulti/GhsMultiUnsupportedTargets/CMakeLists.txt @@ -5,8 +5,6 @@ cmake_minimum_required(VERSION 3.12 FATAL_ERROR) project(test C) -add_custom_target(testTarget ALL echo this is a test) - add_library(sharedLib SHARED file.c) add_library(moduleLib MODULE file.c) diff --git a/Tests/QtAutogen/ManySources/CMakeLists.txt b/Tests/QtAutogen/ManySources/CMakeLists.txt new file mode 100644 index 0000000..df8a2a6 --- /dev/null +++ b/Tests/QtAutogen/ManySources/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.10) +project(ManySources) +include("../AutogenGuiTest.cmake") + +# Test AUTOMOC and AUTOUIC on many source files to stress the concurrent +# parsing and processing framework. + +set(CSD ${CMAKE_CURRENT_SOURCE_DIR}) +set(CBD ${CMAKE_CURRENT_BINARY_DIR}) + +set(SRCS main.cpp) +set(MAIN_INCLUDES "\n// Item includes\n") +set(MAIN_ITEMS "\n// Items\n") + +set(NUM 24) +foreach(III RANGE 1 ${NUM}) + configure_file(${CSD}/object.h.in ${CBD}/object_${III}.h) + configure_file(${CSD}/item.h.in ${CBD}/item_${III}.h) + configure_file(${CSD}/item.cpp.in ${CBD}/item_${III}.cpp) + configure_file(${CSD}/view.ui.in ${CBD}/view_${III}.ui) + configure_file(${CSD}/data.qrc.in ${CBD}/data_${III}.qrc) + + list(APPEND SRCS ${CBD}/item_${III}.cpp) + list(APPEND SRCS ${CBD}/data_${III}.qrc) + + string(APPEND MAIN_INCLUDES "#include \"item_${III}.h\"\n") + string(APPEND MAIN_ITEMS "Item_${III} item_${III};\n") + string(APPEND MAIN_ITEMS "item_${III}.TheSlot();\n") +endforeach() + +configure_file(${CSD}/main.cpp.in ${CBD}/main.cpp) + +add_executable(manySources ${SRCS} ${CBD}/main.cpp) +target_link_libraries(manySources ${QT_LIBRARIES}) +set_target_properties(manySources PROPERTIES AUTOMOC 1 AUTOUIC 1 AUTORCC 1) diff --git a/Tests/QtAutogen/ManySources/data.qrc.in b/Tests/QtAutogen/ManySources/data.qrc.in new file mode 100644 index 0000000..870d486 --- /dev/null +++ b/Tests/QtAutogen/ManySources/data.qrc.in @@ -0,0 +1,7 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>object_@III@.h</file> + <file>item_@III@.h</file> + <file>item_@III@.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/ManySources/item.cpp.in b/Tests/QtAutogen/ManySources/item.cpp.in new file mode 100644 index 0000000..c34ad16 --- /dev/null +++ b/Tests/QtAutogen/ManySources/item.cpp.in @@ -0,0 +1,27 @@ +#include "item_@III@.h" +#include "object_@III@.h" +// AUTOUIC include +#include <ui_view_@III@.h> + +class LocalObject_@III@ : public QObject +{ + Q_OBJECT; + +public: + LocalObject_@III@() = default; + ~LocalObject_@III@() = default; +}; + +void Item_@III@ ::TheSlot() +{ + LocalObject_@III@ localObject; + Object_@III@ obj; + obj.ObjectSlot(); + + Ui_View_@III@ ui_view; +} + +// AUTOMOC includes +#include "item_@III@.moc" +#include "moc_item_@III@.cpp" +#include "moc_object_@III@.cpp" diff --git a/Tests/QtAutogen/ManySources/item.h.in b/Tests/QtAutogen/ManySources/item.h.in new file mode 100644 index 0000000..67ad794 --- /dev/null +++ b/Tests/QtAutogen/ManySources/item.h.in @@ -0,0 +1,15 @@ +#ifndef ITEM_@III@HPP +#define ITEM_@III@HPP + +#include <QObject> + +class Item_@III@ : public QObject +{ + Q_OBJECT + +public: + Q_SLOT + void TheSlot(); +}; + +#endif diff --git a/Tests/QtAutogen/ManySources/main.cpp.in b/Tests/QtAutogen/ManySources/main.cpp.in new file mode 100644 index 0000000..e1dda40 --- /dev/null +++ b/Tests/QtAutogen/ManySources/main.cpp.in @@ -0,0 +1,7 @@ +@MAIN_INCLUDES@ + +int main(int argv, char** args) +{ + @MAIN_ITEMS@ + return 0; +} diff --git a/Tests/QtAutogen/ManySources/object.h.in b/Tests/QtAutogen/ManySources/object.h.in new file mode 100644 index 0000000..a747cbc --- /dev/null +++ b/Tests/QtAutogen/ManySources/object.h.in @@ -0,0 +1,15 @@ +#ifndef OBJECT_@III@H +#define OBJECT_@III@H + +#include <QObject> + +class Object_@III@ : public QObject +{ + Q_OBJECT + +public: + Q_SLOT + void ObjectSlot(){}; +}; + +#endif diff --git a/Tests/QtAutogen/ManySources/view.ui.in b/Tests/QtAutogen/ManySources/view.ui.in new file mode 100644 index 0000000..6901fe3 --- /dev/null +++ b/Tests/QtAutogen/ManySources/view.ui.in @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>View_@III@</class> + <widget class="QWidget" name="Base"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeView" name="treeView"/> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Tests/QtAutogen/Tests.cmake b/Tests/QtAutogen/Tests.cmake index 096d5e3..6771828 100644 --- a/Tests/QtAutogen/Tests.cmake +++ b/Tests/QtAutogen/Tests.cmake @@ -5,6 +5,7 @@ ADD_AUTOGEN_TEST(AutogenTargetDepends) ADD_AUTOGEN_TEST(Complex QtAutogen) ADD_AUTOGEN_TEST(GlobalAutogenTarget) ADD_AUTOGEN_TEST(LowMinimumVersion lowMinimumVersion) +ADD_AUTOGEN_TEST(ManySources manySources) ADD_AUTOGEN_TEST(MocOnly mocOnly) ADD_AUTOGEN_TEST(MocOptions mocOptions) ADD_AUTOGEN_TEST(ObjectLibrary someProgram) diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-result.txt b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-result.txt index d00491f..d00491f 100644 --- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-result.txt +++ b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-result.txt diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt new file mode 100644 index 0000000..3b2a814 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt @@ -0,0 +1,8 @@ +CMake Error at ImportedTarget-TARGET_PDB_FILE_BASE_NAME.cmake:2 \(add_custom_target\): + Error evaluating generator expression: + + \$<TARGET_PDB_FILE_BASE_NAME:empty> + + TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME.cmake b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME.cmake new file mode 100644 index 0000000..489d8e6 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME.cmake @@ -0,0 +1,2 @@ +add_library(empty UNKNOWN IMPORTED) +add_custom_target(custom COMMAND echo $<TARGET_PDB_FILE_BASE_NAME:empty>) diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt deleted file mode 100644 index 783bfb3..0000000 --- a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt +++ /dev/null @@ -1,8 +0,0 @@ -CMake Error at ImportedTarget-TARGET_PDB_OUTPUT_NAME.cmake:2 \(add_custom_target\): - Error evaluating generator expression: - - \$<TARGET_PDB_OUTPUT_NAME:empty> - - TARGET_PDB_OUTPUT_NAME not allowed for IMPORTED targets. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME.cmake deleted file mode 100644 index 010b38e..0000000 --- a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME.cmake +++ /dev/null @@ -1,2 +0,0 @@ -add_library(empty UNKNOWN IMPORTED) -add_custom_target(custom COMMAND echo $<TARGET_PDB_OUTPUT_NAME:empty>) diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-result.txt index d00491f..d00491f 100644 --- a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-result.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-result.txt diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-stderr.txt new file mode 100644 index 0000000..b061ce3 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-stderr.txt @@ -0,0 +1,8 @@ +CMake Error at NonValidCompiler-TARGET_PDB_FILE_BASE_NAME.cmake:6 \(file\): + Error evaluating generator expression: + + \$<TARGET_PDB_FILE_BASE_NAME:empty> + + TARGET_PDB_FILE_BASE_NAME is not supported by the target linker. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME.cmake index 07951de..811c3f7 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME.cmake +++ b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME.cmake @@ -5,5 +5,5 @@ add_library(empty STATIC empty.c) file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt" - CONTENT "[$<TARGET_PDB_OUTPUT_NAME:empty>]" + CONTENT "[$<TARGET_PDB_FILE_BASE_NAME:empty>]" ) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-stderr.txt deleted file mode 100644 index 00ec496..0000000 --- a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-stderr.txt +++ /dev/null @@ -1,8 +0,0 @@ -CMake Error at NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake:6 \(file\): - Error evaluating generator expression: - - \$<TARGET_PDB_OUTPUT_NAME:empty> - - TARGET_PDB_OUTPUT_NAME is not supported by the target linker. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-result.txt index d00491f..d00491f 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-result.txt +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-result.txt diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt new file mode 100644 index 0000000..c7d245c --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt @@ -0,0 +1,9 @@ +CMake Error at NonValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake:6 \(file\): + Error evaluating generator expression: + + \$<TARGET_PDB_FILE_BASE_NAME:empty> + + TARGET_PDB_FILE_BASE_NAME is allowed only for targets with linker created + artifacts. +Call Stack \(most recent call first\): + CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake index 07951de..811c3f7 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake +++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake @@ -5,5 +5,5 @@ add_library(empty STATIC empty.c) file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt" - CONTENT "[$<TARGET_PDB_OUTPUT_NAME:empty>]" + CONTENT "[$<TARGET_PDB_FILE_BASE_NAME:empty>]" ) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt deleted file mode 100644 index 8ac349e..0000000 --- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt +++ /dev/null @@ -1,9 +0,0 @@ -CMake Error at NonValidTarget-TARGET_PDB_OUTPUT_NAME.cmake:6 \(file\): - Error evaluating generator expression: - - \$<TARGET_PDB_OUTPUT_NAME:empty> - - TARGET_PDB_OUTPUT_NAME is allowed only for targets with linker created - artifacts. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/GeneratorExpression/OUTPUT_NAME-recursion.cmake b/Tests/RunCMake/GeneratorExpression/OUTPUT_NAME-recursion.cmake index 775f68a..006b0da 100644 --- a/Tests/RunCMake/GeneratorExpression/OUTPUT_NAME-recursion.cmake +++ b/Tests/RunCMake/GeneratorExpression/OUTPUT_NAME-recursion.cmake @@ -3,4 +3,4 @@ add_executable(empty1 empty.c) set_property(TARGET empty1 PROPERTY OUTPUT_NAME $<TARGET_FILE_NAME:empty1>) add_executable(empty2 empty.c) -set_property(TARGET empty2 PROPERTY OUTPUT_NAME $<TARGET_OUTPUT_NAME:empty2>) +set_property(TARGET empty2 PROPERTY OUTPUT_NAME $<TARGET_FILE_BASE_NAME:empty2>) diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake index 0b0fb78..477b593 100644 --- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake +++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake @@ -41,10 +41,10 @@ run_cmake(TARGET_FILE_SUFFIX) run_cmake(TARGET_FILE_SUFFIX-imported-target) run_cmake(TARGET_FILE_SUFFIX-non-valid-target) run_cmake(TARGET_LINKER_FILE_SUFFIX-non-valid-target) -run_cmake(TARGET_OUTPUT_NAME) -run_cmake(TARGET_OUTPUT_NAME-imported-target) -run_cmake(TARGET_OUTPUT_NAME-non-valid-target) -run_cmake(TARGET_LINKER_OUTPUT_NAME-non-valid-target) +run_cmake(TARGET_FILE_BASE_NAME) +run_cmake(TARGET_FILE_BASE_NAME-imported-target) +run_cmake(TARGET_FILE_BASE_NAME-non-valid-target) +run_cmake(TARGET_LINKER_FILE_BASE_NAME-non-valid-target) run_cmake(TARGET_PROPERTY-LOCATION) run_cmake(TARGET_PROPERTY-SOURCES) run_cmake(LINK_ONLY-not-linking) @@ -78,15 +78,15 @@ run_cmake(FILTER-Include) run_cmake(ImportedTarget-TARGET_BUNDLE_DIR) run_cmake(ImportedTarget-TARGET_BUNDLE_CONTENT_DIR) run_cmake(ImportedTarget-TARGET_PDB_FILE) -run_cmake(ImportedTarget-TARGET_PDB_OUTPUT_NAME) +run_cmake(ImportedTarget-TARGET_PDB_FILE_BASE_NAME) if(LINKER_SUPPORTS_PDB) run_cmake(NonValidTarget-TARGET_PDB_FILE) run_cmake(ValidTarget-TARGET_PDB_FILE) - run_cmake(NonValidTarget-TARGET_PDB_OUTPUT_NAME) - run_cmake(ValidTarget-TARGET_PDB_OUTPUT_NAME) + run_cmake(NonValidTarget-TARGET_PDB_FILE_BASE_NAME) + run_cmake(ValidTarget-TARGET_PDB_FILE_BASE_NAME) else() run_cmake(NonValidCompiler-TARGET_PDB_FILE) - run_cmake(NonValidCompiler-TARGET_PDB_OUTPUT_NAME) + run_cmake(NonValidCompiler-TARGET_PDB_FILE_BASE_NAME) endif() set(RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0085:STRING=OLD) diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-check.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-check.cmake new file mode 100644 index 0000000..793edb1 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-check.cmake @@ -0,0 +1,2 @@ + +include ("${RunCMake_TEST_BINARY_DIR}/TARGET_FILE_BASE_NAME-generated.cmake") diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target-check.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target-check.cmake new file mode 100644 index 0000000..793edb1 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target-check.cmake @@ -0,0 +1,2 @@ + +include ("${RunCMake_TEST_BINARY_DIR}/TARGET_FILE_BASE_NAME-generated.cmake") diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target.cmake index 548a2d7..aa54b31 100644 --- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target.cmake +++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target.cmake @@ -17,11 +17,11 @@ add_library (static1 STATIC IMPORTED) string (APPEND GENERATE_CONTENT [[ -check_value ("TARGET_OUTPUT_NAME executable default" "$<TARGET_OUTPUT_NAME:exec1>" "exec1") -check_value ("TARGET_OUTPUT_NAME shared default" "$<TARGET_OUTPUT_NAME:shared1>" "shared1") -check_value ("TARGET_LINKER_OUTPUT_NAME shared linker default" "$<TARGET_LINKER_OUTPUT_NAME:shared1>" "shared1") -check_value ("TARGET_OUTPUT_NAME static default" "$<TARGET_OUTPUT_NAME:static1>" "static1") -check_value ("TARGET_LINKER_OUTPUT_NAME static linker default" "$<TARGET_LINKER_OUTPUT_NAME:static1>" "static1") +check_value ("TARGET_FILE_BASE_NAME executable default" "$<TARGET_FILE_BASE_NAME:exec1>" "exec1") +check_value ("TARGET_FILE_BASE_NAME shared default" "$<TARGET_FILE_BASE_NAME:shared1>" "shared1") +check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker default" "$<TARGET_LINKER_FILE_BASE_NAME:shared1>" "shared1") +check_value ("TARGET_FILE_BASE_NAME static default" "$<TARGET_FILE_BASE_NAME:static1>" "static1") +check_value ("TARGET_LINKER_FILE_BASE_NAME static linker default" "$<TARGET_LINKER_FILE_BASE_NAME:static1>" "static1") ]]) @@ -34,11 +34,11 @@ set_property (TARGET static2 PROPERTY OUTPUT_NAME static2_custom) string (APPEND GENERATE_CONTENT [[ -check_value ("TARGET_OUTPUT_NAME executable custom" "$<TARGET_OUTPUT_NAME:exec2>" "exec2_custom") -check_value ("TARGET_OUTPUT_NAME shared custom" "$<TARGET_OUTPUT_NAME:shared2>" "shared2_custom") -check_value ("TARGET_LINKER_OUTPUT_NAME shared linker custom" "$<TARGET_LINKER_OUTPUT_NAME:shared2>" "shared2_custom") -check_value ("TARGET_OUTPUT_NAME static custom" "$<TARGET_OUTPUT_NAME:static2>" "static2_custom") -check_value ("TARGET_LINKER_OUTPUT_NAME static linker custom" "$<TARGET_LINKER_OUTPUT_NAME:static2>" "static2_custom") +check_value ("TARGET_FILE_BASE_NAME executable custom" "$<TARGET_FILE_BASE_NAME:exec2>" "exec2_custom") +check_value ("TARGET_FILE_BASE_NAME shared custom" "$<TARGET_FILE_BASE_NAME:shared2>" "shared2_custom") +check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker custom" "$<TARGET_LINKER_FILE_BASE_NAME:shared2>" "shared2_custom") +check_value ("TARGET_FILE_BASE_NAME static custom" "$<TARGET_FILE_BASE_NAME:static2>" "static2_custom") +check_value ("TARGET_LINKER_FILE_BASE_NAME static linker custom" "$<TARGET_LINKER_FILE_BASE_NAME:static2>" "static2_custom") ]]) @@ -60,11 +60,11 @@ set_property (TARGET static3 PROPERTY PDB_NAME static3_pdb) string (APPEND GENERATE_CONTENT [[ -check_value ("TARGET_OUTPUT_NAME executable all properties" "$<TARGET_OUTPUT_NAME:exec3>" "exec3_runtime") -check_value ("TARGET_OUTPUT_NAME shared all properties" "$<TARGET_OUTPUT_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_runtime,shared3_library>") -check_value ("TARGET_LINKER_OUTPUT_NAME shared linker all properties" "$<TARGET_LINKER_OUTPUT_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_archive,shared3_library>") -check_value ("TARGET_OUTPUT_NAME static all properties" "$<TARGET_OUTPUT_NAME:static3>" "static3_archive") -check_value ("TARGET_LINKER_OUTPUT_NAME static linker all properties" "$<TARGET_LINKER_OUTPUT_NAME:static3>" "static3_archive") +check_value ("TARGET_FILE_BASE_NAME executable all properties" "$<TARGET_FILE_BASE_NAME:exec3>" "exec3_runtime") +check_value ("TARGET_FILE_BASE_NAME shared all properties" "$<TARGET_FILE_BASE_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_runtime,shared3_library>") +check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker all properties" "$<TARGET_LINKER_FILE_BASE_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_archive,shared3_library>") +check_value ("TARGET_FILE_BASE_NAME static all properties" "$<TARGET_FILE_BASE_NAME:static3>" "static3_archive") +check_value ("TARGET_LINKER_FILE_BASE_NAME static linker all properties" "$<TARGET_LINKER_FILE_BASE_NAME:static3>" "static3_archive") ]]) @@ -75,5 +75,5 @@ if(_isMultiConfig) set(GENERATE_CONDITION CONDITION $<CONFIG:${FIRST_CONFIG}>) endif() -file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_OUTPUT_NAME-generated.cmake" +file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_FILE_BASE_NAME-generated.cmake" CONTENT "${GENERATE_CONTENT}" ${GENERATE_CONDITION}) diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-result.txt index d00491f..d00491f 100644 --- a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-result.txt +++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-result.txt diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-stderr.txt b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-stderr.txt new file mode 100644 index 0000000..ecb9e5d --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at TARGET_FILE_BASE_NAME-non-valid-target.cmake:[0-9]+ \(file\): + Error evaluating generator expression: + + \$<TARGET_FILE_BASE_NAME:empty> + + Target "empty" is not an executable or library\. diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target.cmake index 5248dfa..8622b7d 100644 --- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target.cmake +++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target.cmake @@ -3,5 +3,5 @@ add_custom_target(empty) file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt" - CONTENT "[$<TARGET_OUTPUT_NAME:empty>]" + CONTENT "[$<TARGET_FILE_BASE_NAME:empty>]" ) diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME.cmake new file mode 100644 index 0000000..5ea53a0 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME.cmake @@ -0,0 +1,96 @@ + +cmake_minimum_required(VERSION 3.14) + +enable_language (C) + +set (GENERATE_CONTENT [[ +macro (CHECK_VALUE test_msg value expected) + if (NOT "${value}" STREQUAL "${expected}") + string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n") + endif() +endmacro() +]]) + +add_executable (exec1 empty.c) +add_library (shared1 SHARED empty.c) +add_library (static1 STATIC empty.c) + +string (APPEND GENERATE_CONTENT [[ + +check_value ("TARGET_FILE_BASE_NAME executable default" "$<TARGET_FILE_BASE_NAME:exec1>" "exec1") +check_value ("TARGET_FILE_BASE_NAME shared default" "$<TARGET_FILE_BASE_NAME:shared1>" "shared1") +check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker default" "$<TARGET_LINKER_FILE_BASE_NAME:shared1>" "shared1") +check_value ("TARGET_FILE_BASE_NAME static default" "$<TARGET_FILE_BASE_NAME:static1>" "static1") +check_value ("TARGET_LINKER_FILE_BASE_NAME static linker default" "$<TARGET_LINKER_FILE_BASE_NAME:static1>" "static1") +]]) +if (CMAKE_C_LINKER_SUPPORTS_PDB) + string(APPEND GENERATE_CONTENT [[ +check_value ("TARGET_PDB_FILE_BASE_NAME executable PDB default" "$<TARGET_PDB_FILE_BASE_NAME:exec1>" "exec1") +check_value ("TARGET_PDB_FILE_BASE_NAME shared PDB default" "$<TARGET_PDB_FILE_BASE_NAME:shared1>" "shared1") +]]) +endif() + + +add_executable (exec2 empty.c) +set_property (TARGET exec2 PROPERTY OUTPUT_NAME exec2_custom) +add_library (shared2 SHARED empty.c) +set_property (TARGET shared2 PROPERTY OUTPUT_NAME shared2_custom) +add_library (static2 STATIC empty.c) +set_property (TARGET static2 PROPERTY OUTPUT_NAME static2_custom) + +string (APPEND GENERATE_CONTENT [[ + +check_value ("TARGET_FILE_BASE_NAME executable custom" "$<TARGET_FILE_BASE_NAME:exec2>" "exec2_custom") +check_value ("TARGET_FILE_BASE_NAME shared custom" "$<TARGET_FILE_BASE_NAME:shared2>" "shared2_custom") +check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker custom" "$<TARGET_LINKER_FILE_BASE_NAME:shared2>" "shared2_custom") +check_value ("TARGET_FILE_BASE_NAME static custom" "$<TARGET_FILE_BASE_NAME:static2>" "static2_custom") +check_value ("TARGET_LINKER_FILE_BASE_NAME static linker custom" "$<TARGET_LINKER_FILE_BASE_NAME:static2>" "static2_custom") +]]) +if (CMAKE_C_LINKER_SUPPORTS_PDB) + string (APPEND GENERATE_CONTENT [[ +check_value ("TARGET_PDB_FILE_BASE_NAME executable PDB custom" "$<TARGET_PDB_FILE_BASE_NAME:exec2>" "exec2_custom") +check_value ("TARGET_PDB_FILE_BASE_NAME shared PDB custom" "$<TARGET_PDB_FILE_BASE_NAME:shared2>" "shared2_custom") + ]]) +endif() + +add_executable (exec3 empty.c) +set_property (TARGET exec3 PROPERTY RUNTIME_OUTPUT_NAME exec3_runtime) +set_property (TARGET exec3 PROPERTY LIBRARY_OUTPUT_NAME exec3_library) +set_property (TARGET exec3 PROPERTY ARCHIVE_OUTPUT_NAME exec3_archive) +set_property (TARGET exec3 PROPERTY PDB_NAME exec3_pdb) +add_library (shared3 SHARED empty.c) +set_property (TARGET shared3 PROPERTY RUNTIME_OUTPUT_NAME shared3_runtime) +set_property (TARGET shared3 PROPERTY LIBRARY_OUTPUT_NAME shared3_library) +set_property (TARGET shared3 PROPERTY ARCHIVE_OUTPUT_NAME shared3_archive) +set_property (TARGET shared3 PROPERTY PDB_NAME shared3_pdb) +add_library (static3 STATIC empty.c) +set_property (TARGET static3 PROPERTY RUNTIME_OUTPUT_NAME static3_runtime) +set_property (TARGET static3 PROPERTY LIBRARY_OUTPUT_NAME static3_library) +set_property (TARGET static3 PROPERTY ARCHIVE_OUTPUT_NAME static3_archive) +set_property (TARGET static3 PROPERTY PDB_NAME static3_pdb) + +string (APPEND GENERATE_CONTENT [[ + +check_value ("TARGET_FILE_BASE_NAME executable all properties" "$<TARGET_FILE_BASE_NAME:exec3>" "exec3_runtime") +check_value ("TARGET_FILE_BASE_NAME shared all properties" "$<TARGET_FILE_BASE_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_runtime,shared3_library>") +check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker all properties" "$<TARGET_LINKER_FILE_BASE_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_archive,shared3_library>") +check_value ("TARGET_FILE_BASE_NAME static all properties" "$<TARGET_FILE_BASE_NAME:static3>" "static3_archive") +check_value ("TARGET_LINKER_FILE_BASE_NAME static linker all properties" "$<TARGET_LINKER_FILE_BASE_NAME:static3>" "static3_archive") +]]) +if (CMAKE_C_LINKER_SUPPORTS_PDB) + string (APPEND GENERATE_CONTENT [[ +check_value ("TARGET_PDB_FILE_BASE_NAME executable PDB all properties" "$<TARGET_PDB_FILE_BASE_NAME:exec3>" "exec3_pdb") +check_value ("TARGET_PDB_FILE_BASE_NAME shared PDB all properties" "$<TARGET_PDB_FILE_BASE_NAME:shared3>" "shared3_pdb") +]]) +endif() + + +unset(GENERATE_CONDITION) +get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(_isMultiConfig) + list(GET CMAKE_CONFIGURATION_TYPES 0 FIRST_CONFIG) + set(GENERATE_CONDITION CONDITION $<CONFIG:${FIRST_CONFIG}>) +endif() + +file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_FILE_BASE_NAME-generated.cmake" + CONTENT "${GENERATE_CONTENT}" ${GENERATE_CONDITION}) diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-result.txt index d00491f..d00491f 100644 --- a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt +++ b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-result.txt diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-stderr.txt b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-stderr.txt new file mode 100644 index 0000000..1ae2f2c --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-stderr.txt @@ -0,0 +1,6 @@ +CMake Error at TARGET_LINKER_FILE_BASE_NAME-non-valid-target.cmake:[0-9]+ \(file\): + Error evaluating generator expression: + + \$<TARGET_LINKER_FILE_BASE_NAME:empty> + + Target "empty" is not an executable or library\. diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target.cmake index c439535..776fb4b 100644 --- a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target.cmake +++ b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target.cmake @@ -3,5 +3,5 @@ add_custom_target(empty) file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt" - CONTENT "[$<TARGET_LINKER_OUTPUT_NAME:empty>]" + CONTENT "[$<TARGET_LINKER_FILE_BASE_NAME:empty>]" ) diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-stderr.txt b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-stderr.txt deleted file mode 100644 index 0e09469..0000000 --- a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -CMake Error at TARGET_LINKER_OUTPUT_NAME-non-valid-target.cmake:[0-9]+ \(file\): - Error evaluating generator expression: - - \$<TARGET_LINKER_OUTPUT_NAME:empty> - - Target "empty" is not an executable or library\. diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-check.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-check.cmake deleted file mode 100644 index fa4f2b9..0000000 --- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-check.cmake +++ /dev/null @@ -1,2 +0,0 @@ - -include ("${RunCMake_TEST_BINARY_DIR}/TARGET_OUTPUT_NAME-generated.cmake") diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target-check.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target-check.cmake deleted file mode 100644 index fa4f2b9..0000000 --- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target-check.cmake +++ /dev/null @@ -1,2 +0,0 @@ - -include ("${RunCMake_TEST_BINARY_DIR}/TARGET_OUTPUT_NAME-generated.cmake") diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-stderr.txt b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-stderr.txt deleted file mode 100644 index 9672a99..0000000 --- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-stderr.txt +++ /dev/null @@ -1,6 +0,0 @@ -CMake Error at TARGET_OUTPUT_NAME-non-valid-target.cmake:[0-9]+ \(file\): - Error evaluating generator expression: - - \$<TARGET_OUTPUT_NAME:empty> - - Target "empty" is not an executable or library\. diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME.cmake deleted file mode 100644 index b7bae15..0000000 --- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME.cmake +++ /dev/null @@ -1,96 +0,0 @@ - -cmake_minimum_required(VERSION 3.14) - -enable_language (C) - -set (GENERATE_CONTENT [[ -macro (CHECK_VALUE test_msg value expected) - if (NOT "${value}" STREQUAL "${expected}") - string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n") - endif() -endmacro() -]]) - -add_executable (exec1 empty.c) -add_library (shared1 SHARED empty.c) -add_library (static1 STATIC empty.c) - -string (APPEND GENERATE_CONTENT [[ - -check_value ("TARGET_OUTPUT_NAME executable default" "$<TARGET_OUTPUT_NAME:exec1>" "exec1") -check_value ("TARGET_OUTPUT_NAME shared default" "$<TARGET_OUTPUT_NAME:shared1>" "shared1") -check_value ("TARGET_LINKER_OUTPUT_NAME shared linker default" "$<TARGET_LINKER_OUTPUT_NAME:shared1>" "shared1") -check_value ("TARGET_OUTPUT_NAME static default" "$<TARGET_OUTPUT_NAME:static1>" "static1") -check_value ("TARGET_LINKER_OUTPUT_NAME static linker default" "$<TARGET_LINKER_OUTPUT_NAME:static1>" "static1") -]]) -if (CMAKE_C_LINKER_SUPPORTS_PDB) - string(APPEND GENERATE_CONTENT [[ -check_value ("TARGET_PDB_OUTPUT_NAME executable PDB default" "$<TARGET_PDB_OUTPUT_NAME:exec1>" "exec1") -check_value ("TARGET_PDB_OUTPUT_NAME shared PDB default" "$<TARGET_PDB_OUTPUT_NAME:shared1>" "shared1") -]]) -endif() - - -add_executable (exec2 empty.c) -set_property (TARGET exec2 PROPERTY OUTPUT_NAME exec2_custom) -add_library (shared2 SHARED empty.c) -set_property (TARGET shared2 PROPERTY OUTPUT_NAME shared2_custom) -add_library (static2 STATIC empty.c) -set_property (TARGET static2 PROPERTY OUTPUT_NAME static2_custom) - -string (APPEND GENERATE_CONTENT [[ - -check_value ("TARGET_OUTPUT_NAME executable custom" "$<TARGET_OUTPUT_NAME:exec2>" "exec2_custom") -check_value ("TARGET_OUTPUT_NAME shared custom" "$<TARGET_OUTPUT_NAME:shared2>" "shared2_custom") -check_value ("TARGET_LINKER_OUTPUT_NAME shared linker custom" "$<TARGET_LINKER_OUTPUT_NAME:shared2>" "shared2_custom") -check_value ("TARGET_OUTPUT_NAME static custom" "$<TARGET_OUTPUT_NAME:static2>" "static2_custom") -check_value ("TARGET_LINKER_OUTPUT_NAME static linker custom" "$<TARGET_LINKER_OUTPUT_NAME:static2>" "static2_custom") -]]) -if (CMAKE_C_LINKER_SUPPORTS_PDB) - string (APPEND GENERATE_CONTENT [[ -check_value ("TARGET_PDB_OUTPUT_NAME executable PDB custom" "$<TARGET_PDB_OUTPUT_NAME:exec2>" "exec2_custom") -check_value ("TARGET_PDB_OUTPUT_NAME shared PDB custom" "$<TARGET_PDB_OUTPUT_NAME:shared2>" "shared2_custom") - ]]) -endif() - -add_executable (exec3 empty.c) -set_property (TARGET exec3 PROPERTY RUNTIME_OUTPUT_NAME exec3_runtime) -set_property (TARGET exec3 PROPERTY LIBRARY_OUTPUT_NAME exec3_library) -set_property (TARGET exec3 PROPERTY ARCHIVE_OUTPUT_NAME exec3_archive) -set_property (TARGET exec3 PROPERTY PDB_NAME exec3_pdb) -add_library (shared3 SHARED empty.c) -set_property (TARGET shared3 PROPERTY RUNTIME_OUTPUT_NAME shared3_runtime) -set_property (TARGET shared3 PROPERTY LIBRARY_OUTPUT_NAME shared3_library) -set_property (TARGET shared3 PROPERTY ARCHIVE_OUTPUT_NAME shared3_archive) -set_property (TARGET shared3 PROPERTY PDB_NAME shared3_pdb) -add_library (static3 STATIC empty.c) -set_property (TARGET static3 PROPERTY RUNTIME_OUTPUT_NAME static3_runtime) -set_property (TARGET static3 PROPERTY LIBRARY_OUTPUT_NAME static3_library) -set_property (TARGET static3 PROPERTY ARCHIVE_OUTPUT_NAME static3_archive) -set_property (TARGET static3 PROPERTY PDB_NAME static3_pdb) - -string (APPEND GENERATE_CONTENT [[ - -check_value ("TARGET_OUTPUT_NAME executable all properties" "$<TARGET_OUTPUT_NAME:exec3>" "exec3_runtime") -check_value ("TARGET_OUTPUT_NAME shared all properties" "$<TARGET_OUTPUT_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_runtime,shared3_library>") -check_value ("TARGET_LINKER_OUTPUT_NAME shared linker all properties" "$<TARGET_LINKER_OUTPUT_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_archive,shared3_library>") -check_value ("TARGET_OUTPUT_NAME static all properties" "$<TARGET_OUTPUT_NAME:static3>" "static3_archive") -check_value ("TARGET_LINKER_OUTPUT_NAME static linker all properties" "$<TARGET_LINKER_OUTPUT_NAME:static3>" "static3_archive") -]]) -if (CMAKE_C_LINKER_SUPPORTS_PDB) - string (APPEND GENERATE_CONTENT [[ -check_value ("TARGET_PDB_OUTPUT_NAME executable PDB all properties" "$<TARGET_PDB_OUTPUT_NAME:exec3>" "exec3_pdb") -check_value ("TARGET_PDB_OUTPUT_NAME shared PDB all properties" "$<TARGET_PDB_OUTPUT_NAME:shared3>" "shared3_pdb") -]]) -endif() - - -unset(GENERATE_CONDITION) -get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(_isMultiConfig) - list(GET CMAKE_CONFIGURATION_TYPES 0 FIRST_CONFIG) - set(GENERATE_CONDITION CONDITION $<CONFIG:${FIRST_CONFIG}>) -endif() - -file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_OUTPUT_NAME-generated.cmake" - CONTENT "${GENERATE_CONTENT}" ${GENERATE_CONDITION}) diff --git a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME-check.cmake b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME-check.cmake new file mode 100644 index 0000000..996d2d4 --- /dev/null +++ b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME-check.cmake @@ -0,0 +1,7 @@ +file(STRINGS ${RunCMake_TEST_BINARY_DIR}/test.txt TEST_TXT ENCODING UTF-8) + +list(GET TEST_TXT 0 PDB_FILE_BASE_NAME) + +if(NOT PDB_FILE_BASE_NAME MATCHES "empty") + set(RunCMake_TEST_FAILED "unexpected PDB_FILE_BASE_NAME [${PDB_FILE_BASE_NAME}]") +endif() diff --git a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake index ba70b43..cc53bdf 100644 --- a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME.cmake +++ b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake @@ -11,6 +11,6 @@ endif() file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt" - CONTENT "$<TARGET_PDB_OUTPUT_NAME:empty>" + CONTENT "$<TARGET_PDB_FILE_BASE_NAME:empty>" ${GENERATE_CONDITION} ) diff --git a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME-check.cmake b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME-check.cmake deleted file mode 100644 index 8d1103e..0000000 --- a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME-check.cmake +++ /dev/null @@ -1,7 +0,0 @@ -file(STRINGS ${RunCMake_TEST_BINARY_DIR}/test.txt TEST_TXT ENCODING UTF-8) - -list(GET TEST_TXT 0 PDB_OUTPUT_NAME) - -if(NOT PDB_OUTPUT_NAME MATCHES "empty") - set(RunCMake_TEST_FAILED "unexpected PDB_OUTPUT_NAME [${PDB_OUTPUT_NAME}]") -endif() diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake index df253a9..219e529 100644 --- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake +++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake @@ -18,3 +18,4 @@ run_cmake(VsCSharpDeployFiles) run_cmake(VSCSharpDefines) run_cmake(VsSdkDirectories) run_cmake(VsGlobals) +run_cmake(VsProjectImport) diff --git a/Tests/RunCMake/VS10Project/VsProjectImport-check.cmake b/Tests/RunCMake/VS10Project/VsProjectImport-check.cmake new file mode 100644 index 0000000..e438bf4 --- /dev/null +++ b/Tests/RunCMake/VS10Project/VsProjectImport-check.cmake @@ -0,0 +1,28 @@ +set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj") +if(NOT EXISTS "${vcProjectFile}") + set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.") + return() +endif() + +set(test1Import "path\\\\to\\\\nuget_packages\\\\Foo.1.0.0\\\\build\\\\Foo.props") +set(test2Import "path\\\\to\\\\nuget_packages\\\\Bar.1.0.0\\\\build\\\\Bar.props") + +set(import1Found FALSE) +set(import2Found FALSE) + +file(STRINGS "${vcProjectFile}" lines) + +foreach(i 1 2) + set(testImport "${test${i}Import}") + foreach(line IN LISTS lines) + if(line MATCHES "^ *<Import Project=\".*${test1Import}\" />$") + message(STATUS "foo.vcxproj is using project import ${testImport}") + set(import${i}Found TRUE) + endif() + endforeach() +endforeach() + +if(NOT import1Found OR NOT import2Found) + set(RunCMake_TEST_FAILED "Imported project not found.") + return() +endif() diff --git a/Tests/RunCMake/VS10Project/VsProjectImport.cmake b/Tests/RunCMake/VS10Project/VsProjectImport.cmake new file mode 100644 index 0000000..70bdded --- /dev/null +++ b/Tests/RunCMake/VS10Project/VsProjectImport.cmake @@ -0,0 +1,11 @@ +enable_language(CXX) +add_library(foo foo.cpp) + +set(test1Import "path/to/nuget_packages/Foo.1.0.0/build/Foo.props") +set(test2Import "path/to/nuget_packages/Bar.1.0.0/build/Bar.props") + +set_property(TARGET foo PROPERTY + VS_PROJECT_IMPORT + ${test1Import} + ${test2Import} + ) diff --git a/Tests/RunCMake/string/Repeat.cmake b/Tests/RunCMake/string/Repeat.cmake new file mode 100644 index 0000000..fc390aa --- /dev/null +++ b/Tests/RunCMake/string/Repeat.cmake @@ -0,0 +1,45 @@ +string(REPEAT "q" 4 q_out) + +if(NOT DEFINED q_out) + message(FATAL_ERROR "q_out is not defined") +endif() + +if(NOT q_out STREQUAL "qqqq") + message(FATAL_ERROR "unexpected result") +endif() + +string(REPEAT "1234" 0 zero_out) + +if(NOT DEFINED zero_out) + message(FATAL_ERROR "zero_out is not defined") +endif() + +if(NOT zero_out STREQUAL "") + message(FATAL_ERROR "unexpected result") +endif() + +unset(zero_out) + +string(REPEAT "" 100 zero_out) + +if(NOT DEFINED zero_out) + message(FATAL_ERROR "zero_out is not defined") +endif() + +if(NOT zero_out STREQUAL "") + message(FATAL_ERROR "unexpected result") +endif() + +string(REPEAT "1" 1 one_out) + +if(NOT one_out STREQUAL "1") + message(FATAL_ERROR "unexpected result") +endif() + +unset(one_out) + +string(REPEAT "one" 1 one_out) + +if(NOT one_out STREQUAL "one") + message(FATAL_ERROR "unexpected result") +endif() diff --git a/Tests/RunCMake/string/RepeatNegativeCount-result.txt b/Tests/RunCMake/string/RepeatNegativeCount-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/string/RepeatNegativeCount-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/string/RepeatNegativeCount-stderr.txt b/Tests/RunCMake/string/RepeatNegativeCount-stderr.txt new file mode 100644 index 0000000..bbd498e --- /dev/null +++ b/Tests/RunCMake/string/RepeatNegativeCount-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at RepeatNegativeCount.cmake:[0-9]+ \(string\): + repeat count is not a positive number. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/string/RepeatNegativeCount.cmake b/Tests/RunCMake/string/RepeatNegativeCount.cmake new file mode 100644 index 0000000..769e7c0 --- /dev/null +++ b/Tests/RunCMake/string/RepeatNegativeCount.cmake @@ -0,0 +1 @@ +string(REPEAT "blah" -1 out) diff --git a/Tests/RunCMake/string/RepeatNoArgs-result.txt b/Tests/RunCMake/string/RepeatNoArgs-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/string/RepeatNoArgs-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/string/RepeatNoArgs-stderr.txt b/Tests/RunCMake/string/RepeatNoArgs-stderr.txt new file mode 100644 index 0000000..5abcb3b --- /dev/null +++ b/Tests/RunCMake/string/RepeatNoArgs-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at RepeatNoArgs.cmake:[0-9]+ \(string\): + sub-command REPEAT requires three arguments. +Call Stack \(most recent call first\): + CMakeLists.txt:[0-9]+ \(include\) diff --git a/Tests/RunCMake/string/RepeatNoArgs.cmake b/Tests/RunCMake/string/RepeatNoArgs.cmake new file mode 100644 index 0000000..e327e99 --- /dev/null +++ b/Tests/RunCMake/string/RepeatNoArgs.cmake @@ -0,0 +1 @@ +string(REPEAT) diff --git a/Tests/RunCMake/string/RunCMakeTest.cmake b/Tests/RunCMake/string/RunCMakeTest.cmake index 211337a..c432b4e 100644 --- a/Tests/RunCMake/string/RunCMakeTest.cmake +++ b/Tests/RunCMake/string/RunCMakeTest.cmake @@ -33,3 +33,7 @@ run_cmake(UTF-16BE) run_cmake(UTF-16LE) run_cmake(UTF-32BE) run_cmake(UTF-32LE) + +run_cmake(Repeat) +run_cmake(RepeatNoArgs) +run_cmake(RepeatNegativeCount) |