diff options
188 files changed, 5917 insertions, 3569 deletions
diff --git a/Help/command/add_custom_command.rst b/Help/command/add_custom_command.rst index 1b0aa14..d4f644a 100644 --- a/Help/command/add_custom_command.rst +++ b/Help/command/add_custom_command.rst @@ -146,8 +146,10 @@ The options are: Specify the primary input source file to the command. This is treated just like any value given to the ``DEPENDS`` option but also suggests to Visual Studio generators where to hang - the custom command. At most one custom command may specify a - given source file as its main dependency. + the custom command. Each source file may have at most one command + specifying it as its main dependency. A compile command (i.e. for a + library or an executable) counts as an implicit main dependency which + gets silently overwritten by a custom command specification. ``OUTPUT`` Specify the output files the command is expected to produce. diff --git a/Help/command/install.rst b/Help/command/install.rst index f43b2a0..2506f98 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -38,7 +38,21 @@ signatures that specify them. The common options are: ``CONFIGURATIONS`` Specify a list of build configurations for which the install rule - applies (Debug, Release, etc.). + applies (Debug, Release, etc.). Note that the values specified for + this option only apply to options listed AFTER the ``CONFIGURATIONS`` + option. For example, to set separate install paths for the Debug and + Release configurations, do the following: + + .. code-block:: cmake + + install(TARGETS target + CONFIGURATIONS Debug + RUNTIME DESTINATION Debug/bin) + install(TARGETS target + CONFIGURATIONS Release + RUNTIME DESTINATION Release/bin) + + Note that ``CONFIGURATIONS`` appears BEFORE ``RUNTIME DESTINATION``. ``COMPONENT`` Specify an installation component name with which the install rule diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index dc5621a..0f6d4cf 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -97,21 +97,35 @@ Available logical expressions are: compile features and a list of supported compilers. ``$<COMPILE_LANGUAGE:lang>`` ``1`` when the language used for compilation unit matches ``lang``, - otherwise ``0``. This expression may be used to specify compile options for - source files of a particular language in a target. For example, to specify - the use of the ``-fno-exceptions`` compile option (compiler id checks - elided): + otherwise ``0``. This expression may be used to specify compile options, + compile definitions, and include directories for source files of a + particular language in a target. For example: .. code-block:: cmake - add_executable(myapp main.cpp foo.c bar.cpp) + add_executable(myapp main.cpp foo.c bar.cpp zot.cu) target_compile_options(myapp PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-fno-exceptions> ) + target_compile_definitions(myapp + PRIVATE $<$<COMPILE_LANGUAGE:CXX>:COMPILING_CXX> + $<$<COMPILE_LANGUAGE:CUDA>:COMPILING_CUDA> + ) + target_include_directories(myapp + PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/opt/foo/cxx_headers> + ) + + This specifies the use of the ``-fno-exceptions`` compile option, + ``COMPILING_CXX`` compile definition, and ``cxx_headers`` include + directory for C++ only (compiler id checks elided). It also specifies + a ``COMPILING_CUDA`` compile definition for CUDA. - Note that with :ref:`Visual Studio Generators` there is no way to represent + Note that with :ref:`Visual Studio Generators` and :generator:`Xcode` there + is no way to represent target-wide compile definitions or include directories + separately for ``C`` and ``CXX`` languages. + Also, with :ref:`Visual Studio Generators` there is no way to represent target-wide flags separately for ``C`` and ``CXX`` languages. Under these - generators, target-wide flags for both C and C++ sources will be evaluated + generators, expressions for both C and C++ sources will be evaluated using ``CXX`` if there are any C++ sources and otherwise using ``C``. A workaround is to create separate libraries for each source file language instead: @@ -124,20 +138,6 @@ Available logical expressions are: add_executable(myapp main.cpp) target_link_libraries(myapp myapp_c myapp_cxx) - The ``Makefile`` and ``Ninja`` based generators can also use this - expression to specify compile-language specific compile definitions - and include directories: - - .. code-block:: cmake - - add_executable(myapp main.cpp foo.c bar.cpp) - target_compile_definitions(myapp - PRIVATE $<$<COMPILE_LANGUAGE:CXX>:COMPILING_CXX> - ) - target_include_directories(myapp - PRIVATE $<$<COMPILE_LANGUAGE:CXX>:/opt/foo/cxx_headers> - ) - Informational Expressions ========================= diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst index 2c63c4b..88b39d4 100644 --- a/Help/manual/cmake-properties.7.rst +++ b/Help/manual/cmake-properties.7.rst @@ -121,6 +121,7 @@ Properties on Targets /prop_tgt/ARCHIVE_OUTPUT_NAME_CONFIG /prop_tgt/ARCHIVE_OUTPUT_NAME /prop_tgt/AUTOGEN_BUILD_DIR + /prop_tgt/AUTOGEN_PARALLEL /prop_tgt/AUTOGEN_TARGET_DEPENDS /prop_tgt/AUTOMOC_COMPILER_PREDEFINES /prop_tgt/AUTOMOC_DEPEND_FILTERS diff --git a/Help/manual/cmake-qt.7.rst b/Help/manual/cmake-qt.7.rst index cafeae1..e5c593f 100644 --- a/Help/manual/cmake-qt.7.rst +++ b/Help/manual/cmake-qt.7.rst @@ -96,7 +96,8 @@ following targets by setting the :variable:`CMAKE_AUTOMOC` variable. The options to pass to ``moc``. The :variable:`CMAKE_AUTOMOC_MOC_OPTIONS` variable may be populated to pre-set the options for all following targets. -Additional macro names to search for can be added to :prop_tgt:`AUTOMOC_MACRO_NAMES`. +Additional macro names to search for can be added to +:prop_tgt:`AUTOMOC_MACRO_NAMES`. Additional ``moc`` dependency file names can be extracted from source code by using :prop_tgt:`AUTOMOC_DEPEND_FILTERS`. diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 735b93b..c18d8da 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -276,6 +276,7 @@ Variables that Control the Build /variable/CMAKE_ANDROID_STL_TYPE /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY /variable/CMAKE_ARCHIVE_OUTPUT_DIRECTORY_CONFIG + /variable/CMAKE_AUTOGEN_PARALLEL /variable/CMAKE_AUTOMOC /variable/CMAKE_AUTOMOC_COMPILER_PREDEFINES /variable/CMAKE_AUTOMOC_DEPEND_FILTERS @@ -414,6 +415,8 @@ Variables for Languages /variable/CMAKE_LANG_CREATE_SHARED_MODULE /variable/CMAKE_LANG_CREATE_STATIC_LIBRARY /variable/CMAKE_LANG_FLAGS + /variable/CMAKE_LANG_FLAGS_CONFIG + /variable/CMAKE_LANG_FLAGS_CONFIG_INIT /variable/CMAKE_LANG_FLAGS_DEBUG /variable/CMAKE_LANG_FLAGS_DEBUG_INIT /variable/CMAKE_LANG_FLAGS_INIT @@ -423,6 +426,7 @@ Variables for Languages /variable/CMAKE_LANG_FLAGS_RELEASE_INIT /variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO /variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO_INIT + /variable/CMAKE_LANG_GHS_KERNEL_FLAGS_CONFIG /variable/CMAKE_LANG_GHS_KERNEL_FLAGS_DEBUG /variable/CMAKE_LANG_GHS_KERNEL_FLAGS_MINSIZEREL /variable/CMAKE_LANG_GHS_KERNEL_FLAGS_RELEASE diff --git a/Help/prop_gbl/DEBUG_CONFIGURATIONS.rst b/Help/prop_gbl/DEBUG_CONFIGURATIONS.rst index 690143f..fec6fda 100644 --- a/Help/prop_gbl/DEBUG_CONFIGURATIONS.rst +++ b/Help/prop_gbl/DEBUG_CONFIGURATIONS.rst @@ -4,11 +4,10 @@ DEBUG_CONFIGURATIONS Specify which configurations are for debugging. The value must be a semi-colon separated list of configuration names. -Currently this property is used only by the target_link_libraries -command (see its documentation for details). Additional uses may be -defined in the future. +Currently this property is used only by the :command:`target_link_libraries` +command. Additional uses may be defined in the future. This property must be set at the top level of the project and before -the first target_link_libraries command invocation. If any entry in +the first :command:`target_link_libraries` command invocation. If any entry in the list does not match a valid configuration for the project the behavior is undefined. diff --git a/Help/prop_tgt/AUTOGEN_PARALLEL.rst b/Help/prop_tgt/AUTOGEN_PARALLEL.rst new file mode 100644 index 0000000..07fbc5a --- /dev/null +++ b/Help/prop_tgt/AUTOGEN_PARALLEL.rst @@ -0,0 +1,21 @@ +AUTOGEN_PARALLEL +---------------- + +Number of parallel ``moc`` or ``uic`` processes to start when using +:prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`. + +The custom `<origin>_autogen` target starts a number of threads of which +each one parses a source file and on demand starts a ``moc`` or ``uic`` +process. :prop_tgt:`AUTOGEN_PARALLEL` controls how many parallel threads +(and therefore ``moc`` or ``uic`` processes) are started. + +- An empty (or unset) value or the string ``AUTO`` sets the number of + threads/processes to the number of physical CPUs on the host system. +- A positive non zero integer value sets the exact thread/process count. +- Otherwise a single thread/process is started. + +By default :prop_tgt:`AUTOGEN_PARALLEL` is initialized from +:variable:`CMAKE_AUTOGEN_PARALLEL`. + +See the :manual:`cmake-qt(7)` manual for more information on using CMake +with Qt. diff --git a/Help/prop_tgt/AUTOMOC.rst b/Help/prop_tgt/AUTOMOC.rst index 9e5261f..3bd693a 100644 --- a/Help/prop_tgt/AUTOMOC.rst +++ b/Help/prop_tgt/AUTOMOC.rst @@ -71,7 +71,8 @@ automoc targets together in an IDE, e.g. in MSVS. The global property :prop_gbl:`AUTOGEN_SOURCE_GROUP` can be used to group files generated by :prop_tgt:`AUTOMOC` together in an IDE, e.g. in MSVS. -Additional macro names to search for can be added to :prop_tgt:`AUTOMOC_MACRO_NAMES`. +Additional macro names to search for can be added to +:prop_tgt:`AUTOMOC_MACRO_NAMES`. Additional ``moc`` dependency file names can be extracted from source code by using :prop_tgt:`AUTOMOC_DEPEND_FILTERS`. @@ -82,5 +83,8 @@ which is controlled by :prop_tgt:`AUTOMOC_COMPILER_PREDEFINES`. Source C++ files can be excluded from :prop_tgt:`AUTOMOC` processing by enabling :prop_sf:`SKIP_AUTOMOC` or the broader :prop_sf:`SKIP_AUTOGEN`. +The number of parallel ``moc`` processes to start can be modified by +setting :prop_tgt:`AUTOGEN_PARALLEL`. + See the :manual:`cmake-qt(7)` manual for more information on using CMake with Qt. diff --git a/Help/prop_tgt/AUTOUIC.rst b/Help/prop_tgt/AUTOUIC.rst index 1791384..4fc603f 100644 --- a/Help/prop_tgt/AUTOUIC.rst +++ b/Help/prop_tgt/AUTOUIC.rst @@ -33,5 +33,8 @@ autouic targets together in an IDE, e.g. in MSVS. Source files can be excluded from :prop_tgt:`AUTOUIC` processing by enabling :prop_sf:`SKIP_AUTOUIC` or the broader :prop_sf:`SKIP_AUTOGEN`. +The number of parallel ``uic`` processes to start can be modified by +setting :prop_tgt:`AUTOGEN_PARALLEL`. + See the :manual:`cmake-qt(7)` manual for more information on using CMake with Qt. diff --git a/Help/prop_tgt/LINK_FLAGS.rst b/Help/prop_tgt/LINK_FLAGS.rst index 409d00a..b09e7c1 100644 --- a/Help/prop_tgt/LINK_FLAGS.rst +++ b/Help/prop_tgt/LINK_FLAGS.rst @@ -4,5 +4,6 @@ LINK_FLAGS Additional flags to use when linking this target. The LINK_FLAGS property can be used to add extra flags to the link -step of a target. LINK_FLAGS_<CONFIG> will add to the configuration -<CONFIG>, for example, DEBUG, RELEASE, MINSIZEREL, RELWITHDEBINFO. +step of a target. :prop_tgt:`LINK_FLAGS_<CONFIG>` will add to the +configuration ``<CONFIG>``, for example, ``DEBUG``, ``RELEASE``, +``MINSIZEREL``, ``RELWITHDEBINFO``, ... diff --git a/Help/release/dev/CheckIncludeFile-required-libs.rst b/Help/release/dev/CheckIncludeFile-required-libs.rst new file mode 100644 index 0000000..14c43d1 --- /dev/null +++ b/Help/release/dev/CheckIncludeFile-required-libs.rst @@ -0,0 +1,11 @@ +CheckIncludeFile-required-libs +------------------------------ + +* The :module:`CheckIncludeFile` module ``check_include_file`` macro + learned to honor the ``CMAKE_REQUIRED_LIBRARIES`` variable. + +* The :module:`CheckIncludeFileCXX` module ``check_include_file_cxx`` macro + learned to honor the ``CMAKE_REQUIRED_LIBRARIES`` variable. + +* The :module:`CheckIncludeFiles` module ``check_include_files`` macro + learned to honor the ``CMAKE_REQUIRED_LIBRARIES`` variable. diff --git a/Help/release/dev/autogen-parallel.rst b/Help/release/dev/autogen-parallel.rst new file mode 100644 index 0000000..50ae9a6 --- /dev/null +++ b/Help/release/dev/autogen-parallel.rst @@ -0,0 +1,10 @@ +autogen-parallel +---------------- + +* When using :prop_tgt:`AUTOMOC` or :prop_tgt:`AUTOUIC`, CMake starts multiple + parallel ``moc`` or ``uic`` processes to reduce the build time. + The new :variable:`CMAKE_AUTOGEN_PARALLEL` variable and + :prop_tgt:`AUTOGEN_PARALLEL` target property allow to modify the number of + parallel ``moc`` or ``uic`` processes to start. + By default CMake starts a single ``moc`` or ``uic`` process for each physical + CPU on the host system. diff --git a/Help/release/dev/extend-compile-language-genex.rst b/Help/release/dev/extend-compile-language-genex.rst index 0a0a669..7c09376 100644 --- a/Help/release/dev/extend-compile-language-genex.rst +++ b/Help/release/dev/extend-compile-language-genex.rst @@ -1,7 +1,14 @@ extend-compile-language-genex ----------------------------- -* The ``COMPILE_LANGUAGE`` :manual:`generator expression - <cmake-generator-expressions(7)>` may now be used with - :ref:`Visual Studio Generators` in :prop_tgt:`COMPILE_OPTIONS` - and :command:`file(GENERATE)`. +* :ref:`Visual Studio Generators` learned to support the ``COMPILE_LANGUAGE`` + :manual:`generator expression <cmake-generator-expressions(7)>` in + target-wide :prop_tgt:`COMPILE_DEFINITIONS`, + :prop_tgt:`INCLUDE_DIRECTORIES`, :prop_tgt:`COMPILE_OPTIONS`, and + :command:`file(GENERATE)`. + +* The :generator:`Xcode` generator learned to support the ``COMPILE_LANGUAGE`` + :manual:`generator expression <cmake-generator-expressions(7)>` in + target-wide :prop_tgt:`COMPILE_DEFINITIONS` and + :prop_tgt:`INCLUDE_DIRECTORIES`. It previously supported only + :prop_tgt:`COMPILE_OPTIONS` and :command:`file(GENERATE)`. diff --git a/Help/release/dev/iphone-deployment-target.rst b/Help/release/dev/iphone-deployment-target.rst index e586df6..7a20a6d 100644 --- a/Help/release/dev/iphone-deployment-target.rst +++ b/Help/release/dev/iphone-deployment-target.rst @@ -7,5 +7,5 @@ iphone-deployment-target set for the target platform selected by :variable:`CMAKE_OSX_SYSROOT`. If for example the sysroot variable specifies an iOS SDK then the - value in ``CMAKE_OSX_DEPLOYMENT_TARGET`` is interpreted as minium + value in ``CMAKE_OSX_DEPLOYMENT_TARGET`` is interpreted as minimum iOS version. diff --git a/Help/variable/CMAKE_AUTOGEN_PARALLEL.rst b/Help/variable/CMAKE_AUTOGEN_PARALLEL.rst new file mode 100644 index 0000000..dd9499a --- /dev/null +++ b/Help/variable/CMAKE_AUTOGEN_PARALLEL.rst @@ -0,0 +1,10 @@ +CMAKE_AUTOGEN_PARALLEL +---------------------- + +Number of parallel ``moc`` or ``uic`` processes to start when using +:prop_tgt:`AUTOMOC` and :prop_tgt:`AUTOUIC`. + +This variable is used to initialize the :prop_tgt:`AUTOGEN_PARALLEL` property +on all the targets. See that target property for additional information. + +By default :variable:`CMAKE_AUTOGEN_PARALLEL` is unset. diff --git a/Help/variable/CMAKE_BUILD_TYPE.rst b/Help/variable/CMAKE_BUILD_TYPE.rst index 2d54d60..2d35635 100644 --- a/Help/variable/CMAKE_BUILD_TYPE.rst +++ b/Help/variable/CMAKE_BUILD_TYPE.rst @@ -5,7 +5,7 @@ Specifies the build type on single-configuration generators. This statically specifies what build type (configuration) will be built in this build tree. Possible values are empty, ``Debug``, ``Release``, -``RelWithDebInfo`` and ``MinSizeRel``. This variable is only meaningful to +``RelWithDebInfo``, ``MinSizeRel``, ... This variable is only meaningful to single-configuration generators (such as :ref:`Makefile Generators` and :generator:`Ninja`) i.e. those which choose a single configuration when CMake runs to generate a build tree as opposed to multi-configuration generators @@ -13,7 +13,7 @@ which offer selection of the build configuration within the generated build environment. There are many per-config properties and variables (usually following clean ``SOME_VAR_<CONFIG>`` order conventions), such as ``CMAKE_C_FLAGS_<CONFIG>``, specified as uppercase: -``CMAKE_C_FLAGS_[DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL]``. For example, +``CMAKE_C_FLAGS_[DEBUG|RELEASE|RELWITHDEBINFO|MINSIZEREL|...]``. For example, in a build tree configured to build type ``Debug``, CMake will see to having :variable:`CMAKE_C_FLAGS_DEBUG <CMAKE_<LANG>_FLAGS_DEBUG>` settings get added to the :variable:`CMAKE_C_FLAGS <CMAKE_<LANG>_FLAGS>` settings. See diff --git a/Help/variable/CMAKE_LANG_FLAGS_CONFIG.rst b/Help/variable/CMAKE_LANG_FLAGS_CONFIG.rst new file mode 100644 index 0000000..1dbd036 --- /dev/null +++ b/Help/variable/CMAKE_LANG_FLAGS_CONFIG.rst @@ -0,0 +1,4 @@ +CMAKE_<LANG>_FLAGS_<CONFIG> +--------------------------- + +Flags for language ``<LANG>`` when building for the ``<CONFIG>`` configuration. diff --git a/Help/variable/CMAKE_LANG_FLAGS_CONFIG_INIT.rst b/Help/variable/CMAKE_LANG_FLAGS_CONFIG_INIT.rst new file mode 100644 index 0000000..1eb5b3f --- /dev/null +++ b/Help/variable/CMAKE_LANG_FLAGS_CONFIG_INIT.rst @@ -0,0 +1,10 @@ +CMAKE_<LANG>_FLAGS_<CONFIG>_INIT +-------------------------------- + +Value used to initialize the :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache +entry the first time a build tree is configured for language ``<LANG>``. +This variable is meant to be set by a :variable:`toolchain file +<CMAKE_TOOLCHAIN_FILE>`. CMake may prepend or append content to +the value based on the environment and target platform. + +See also :variable:`CMAKE_<LANG>_FLAGS_INIT`. diff --git a/Help/variable/CMAKE_LANG_FLAGS_DEBUG.rst b/Help/variable/CMAKE_LANG_FLAGS_DEBUG.rst index a233d4a..6be424a 100644 --- a/Help/variable/CMAKE_LANG_FLAGS_DEBUG.rst +++ b/Help/variable/CMAKE_LANG_FLAGS_DEBUG.rst @@ -1,6 +1,5 @@ CMAKE_<LANG>_FLAGS_DEBUG ------------------------ -Flags for ``Debug`` build type or configuration. - -``<LANG>`` flags used when :variable:`CMAKE_BUILD_TYPE` is ``Debug``. +This variable is the ``Debug`` variant of the +:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variable. diff --git a/Help/variable/CMAKE_LANG_FLAGS_DEBUG_INIT.rst b/Help/variable/CMAKE_LANG_FLAGS_DEBUG_INIT.rst index dcddb2e..de7fcfc 100644 --- a/Help/variable/CMAKE_LANG_FLAGS_DEBUG_INIT.rst +++ b/Help/variable/CMAKE_LANG_FLAGS_DEBUG_INIT.rst @@ -1,10 +1,5 @@ CMAKE_<LANG>_FLAGS_DEBUG_INIT ----------------------------- -Value used to initialize the :variable:`CMAKE_<LANG>_FLAGS_DEBUG` cache -entry the first time a build tree is configured for language ``<LANG>``. -This variable is meant to be set by a :variable:`toolchain file -<CMAKE_TOOLCHAIN_FILE>`. CMake may prepend or append content to -the value based on the environment and target platform. - -See also :variable:`CMAKE_<LANG>_FLAGS_INIT`. +This variable is the ``Debug`` variant of the +:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>_INIT` variable. diff --git a/Help/variable/CMAKE_LANG_FLAGS_INIT.rst b/Help/variable/CMAKE_LANG_FLAGS_INIT.rst index 1d32cc3..a88d122 100644 --- a/Help/variable/CMAKE_LANG_FLAGS_INIT.rst +++ b/Help/variable/CMAKE_LANG_FLAGS_INIT.rst @@ -7,9 +7,5 @@ This variable is meant to be set by a :variable:`toolchain file <CMAKE_TOOLCHAIN_FILE>`. CMake may prepend or append content to the value based on the environment and target platform. -See also the configuration-specific variables: - -* :variable:`CMAKE_<LANG>_FLAGS_DEBUG_INIT` -* :variable:`CMAKE_<LANG>_FLAGS_RELEASE_INIT` -* :variable:`CMAKE_<LANG>_FLAGS_MINSIZEREL_INIT` -* :variable:`CMAKE_<LANG>_FLAGS_RELWITHDEBINFO_INIT` +See also the configuration-specific +:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>_INIT` variable. diff --git a/Help/variable/CMAKE_LANG_FLAGS_MINSIZEREL.rst b/Help/variable/CMAKE_LANG_FLAGS_MINSIZEREL.rst index a9436c1..634fab9 100644 --- a/Help/variable/CMAKE_LANG_FLAGS_MINSIZEREL.rst +++ b/Help/variable/CMAKE_LANG_FLAGS_MINSIZEREL.rst @@ -1,7 +1,5 @@ CMAKE_<LANG>_FLAGS_MINSIZEREL ----------------------------- -Flags for ``MinSizeRel`` build type or configuration. - -``<LANG>`` flags used when :variable:`CMAKE_BUILD_TYPE` is ``MinSizeRel`` -(short for minimum size release). +This variable is the ``MinSizeRel`` variant of the +:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variable. diff --git a/Help/variable/CMAKE_LANG_FLAGS_MINSIZEREL_INIT.rst b/Help/variable/CMAKE_LANG_FLAGS_MINSIZEREL_INIT.rst index c0aedf4..1e7003c 100644 --- a/Help/variable/CMAKE_LANG_FLAGS_MINSIZEREL_INIT.rst +++ b/Help/variable/CMAKE_LANG_FLAGS_MINSIZEREL_INIT.rst @@ -1,10 +1,5 @@ CMAKE_<LANG>_FLAGS_MINSIZEREL_INIT ---------------------------------- -Value used to initialize the :variable:`CMAKE_<LANG>_FLAGS_MINSIZEREL` -cache entry the first time a build tree is configured for language ``<LANG>``. -This variable is meant to be set by a :variable:`toolchain file -<CMAKE_TOOLCHAIN_FILE>`. CMake may prepend or append content to -the value based on the environment and target platform. - -See also :variable:`CMAKE_<LANG>_FLAGS_INIT`. +This variable is the ``MinSizeRel`` variant of the +:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>_INIT` variable. diff --git a/Help/variable/CMAKE_LANG_FLAGS_RELEASE.rst b/Help/variable/CMAKE_LANG_FLAGS_RELEASE.rst index ffc5d79..3baeab0 100644 --- a/Help/variable/CMAKE_LANG_FLAGS_RELEASE.rst +++ b/Help/variable/CMAKE_LANG_FLAGS_RELEASE.rst @@ -1,6 +1,5 @@ CMAKE_<LANG>_FLAGS_RELEASE -------------------------- -Flags for ``Release`` build type or configuration. - -``<LANG>`` flags used when :variable:`CMAKE_BUILD_TYPE` is ``Release``. +This variable is the ``Release`` variant of the +:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variable. diff --git a/Help/variable/CMAKE_LANG_FLAGS_RELEASE_INIT.rst b/Help/variable/CMAKE_LANG_FLAGS_RELEASE_INIT.rst index 59f92ff..e7c73fe 100644 --- a/Help/variable/CMAKE_LANG_FLAGS_RELEASE_INIT.rst +++ b/Help/variable/CMAKE_LANG_FLAGS_RELEASE_INIT.rst @@ -1,10 +1,5 @@ CMAKE_<LANG>_FLAGS_RELEASE_INIT ------------------------------- -Value used to initialize the :variable:`CMAKE_<LANG>_FLAGS_RELEASE` -cache entry the first time a build tree is configured for language ``<LANG>``. -This variable is meant to be set by a :variable:`toolchain file -<CMAKE_TOOLCHAIN_FILE>`. CMake may prepend or append content to -the value based on the environment and target platform. - -See also :variable:`CMAKE_<LANG>_FLAGS_INIT`. +This variable is the ``Release`` variant of the +:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>_INIT` variable. diff --git a/Help/variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO.rst b/Help/variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO.rst index 962768e..67a5073 100644 --- a/Help/variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO.rst +++ b/Help/variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO.rst @@ -1,7 +1,5 @@ CMAKE_<LANG>_FLAGS_RELWITHDEBINFO --------------------------------- -Flags for ``RelWithDebInfo`` type or configuration. - -``<LANG>`` flags used when :variable:`CMAKE_BUILD_TYPE` is ``RelWithDebInfo`` -(short for Release With Debug Information). +This variable is the ``RelWithDebInfo`` variant of the +:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variable. diff --git a/Help/variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO_INIT.rst b/Help/variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO_INIT.rst index 915f023..3ab3975 100644 --- a/Help/variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO_INIT.rst +++ b/Help/variable/CMAKE_LANG_FLAGS_RELWITHDEBINFO_INIT.rst @@ -1,10 +1,5 @@ CMAKE_<LANG>_FLAGS_RELWITHDEBINFO_INIT -------------------------------------- -Value used to initialize the :variable:`CMAKE_<LANG>_FLAGS_RELWITHDEBINFO` -cache entry the first time a build tree is configured for language ``<LANG>``. -This variable is meant to be set by a :variable:`toolchain file -<CMAKE_TOOLCHAIN_FILE>`. CMake may prepend or append content to -the value based on the environment and target platform. - -See also :variable:`CMAKE_<LANG>_FLAGS_INIT`. +This variable is the ``RelWithDebInfo`` variant of the +:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>_INIT` variable. diff --git a/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_CONFIG.rst b/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_CONFIG.rst new file mode 100644 index 0000000..8ed1c02 --- /dev/null +++ b/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_CONFIG.rst @@ -0,0 +1,5 @@ +CMAKE_<LANG>_GHS_KERNEL_FLAGS_<CONFIG> +-------------------------------------- + +GHS kernel flags for language ``<LANG>`` when building for the ``<CONFIG>`` +configuration. diff --git a/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_DEBUG.rst b/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_DEBUG.rst index 1f639a3..4fea67a 100644 --- a/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_DEBUG.rst +++ b/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_DEBUG.rst @@ -1,6 +1,5 @@ CMAKE_<LANG>_GHS_KERNEL_FLAGS_DEBUG ----------------------------------- -GHS kernel flags for ``Debug`` build type or configuration. - -``<LANG>`` flags used when :variable:`CMAKE_BUILD_TYPE` is ``Debug``. +This variable is the ``Debug`` variant of the +:variable:`CMAKE_<LANG>_GHS_KERNEL_FLAGS_<CONFIG>` variable. diff --git a/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_MINSIZEREL.rst b/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_MINSIZEREL.rst index 94e2115..31f87f2 100644 --- a/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_MINSIZEREL.rst +++ b/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_MINSIZEREL.rst @@ -1,7 +1,5 @@ CMAKE_<LANG>_GHS_KERNEL_FLAGS_MINSIZEREL ---------------------------------------- -GHS kernel flags for ``MinSizeRel`` build type or configuration. - -``<LANG>`` flags used when :variable:`CMAKE_BUILD_TYPE` is ``MinSizeRel`` -(short for minimum size release). +This variable is the ``MinSizeRel`` variant of the +:variable:`CMAKE_<LANG>_GHS_KERNEL_FLAGS_<CONFIG>` variable. diff --git a/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_RELEASE.rst b/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_RELEASE.rst index 74566ef..1acd198 100644 --- a/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_RELEASE.rst +++ b/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_RELEASE.rst @@ -1,6 +1,5 @@ CMAKE_<LANG>_GHS_KERNEL_FLAGS_RELEASE ------------------------------------- -GHS kernel flags for ``Release`` build type or configuration. - -``<LANG>`` flags used when :variable:`CMAKE_BUILD_TYPE` is ``Release``. +This variable is the ``Release`` variant of the +:variable:`CMAKE_<LANG>_GHS_KERNEL_FLAGS_<CONFIG>` variable. diff --git a/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_RELWITHDEBINFO.rst b/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_RELWITHDEBINFO.rst index d148193..ac1b6bc 100644 --- a/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_RELWITHDEBINFO.rst +++ b/Help/variable/CMAKE_LANG_GHS_KERNEL_FLAGS_RELWITHDEBINFO.rst @@ -1,7 +1,5 @@ CMAKE_<LANG>_GHS_KERNEL_FLAGS_RELWITHDEBINFO -------------------------------------------- -GHS kernel flags for ``RelWithDebInfo`` type or configuration. - -``<LANG>`` flags used when :variable:`CMAKE_BUILD_TYPE` is ``RelWithDebInfo`` -(short for Release With Debug Information). +This variable is the ``RelWithDebInfo`` variant of the +:variable:`CMAKE_<LANG>_GHS_KERNEL_FLAGS_<CONFIG>` variable. diff --git a/Modules/AutoRccInfo.cmake.in b/Modules/AutoRccInfo.cmake.in index 5457a6f..cbab4a7 100644 --- a/Modules/AutoRccInfo.cmake.in +++ b/Modules/AutoRccInfo.cmake.in @@ -1,10 +1,6 @@ # Meta set(ARCC_MULTI_CONFIG @_multi_config@) # Directories and files -set(ARCC_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/") -set(ARCC_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/") -set(ARCC_CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@/") -set(ARCC_CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@/") set(ARCC_BUILD_DIR @_build_dir@) # Qt environment set(ARCC_RCC_EXECUTABLE @_qt_rcc_executable@) diff --git a/Modules/AutogenInfo.cmake.in b/Modules/AutogenInfo.cmake.in index 9a4a06d..7320c0a 100644 --- a/Modules/AutogenInfo.cmake.in +++ b/Modules/AutogenInfo.cmake.in @@ -1,5 +1,6 @@ # Meta set(AM_MULTI_CONFIG @_multi_config@) +set(AM_PARALLEL @_parallel@) # Directories and files set(AM_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/") set(AM_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/") diff --git a/Modules/CMakeASMInformation.cmake b/Modules/CMakeASMInformation.cmake index f7cf900..125c4e3 100644 --- a/Modules/CMakeASMInformation.cmake +++ b/Modules/CMakeASMInformation.cmake @@ -67,38 +67,7 @@ endif() # Support for CMAKE_ASM${ASM_DIALECT}_FLAGS_INIT and friends: set(CMAKE_ASM${ASM_DIALECT}_FLAGS_INIT "$ENV{ASM${ASM_DIALECT}FLAGS} ${CMAKE_ASM${ASM_DIALECT}_FLAGS_INIT}") -foreach(c "" _DEBUG _RELEASE _MINSIZEREL _RELWITHDEBINFO) - string(STRIP "${CMAKE_ASM${ASM_DIALECT}_FLAGS${c}_INIT}" CMAKE_ASM${ASM_DIALECT}_FLAGS${c}_INIT) -endforeach() - -set (CMAKE_ASM${ASM_DIALECT}_FLAGS "${CMAKE_ASM${ASM_DIALECT}_FLAGS_INIT}" CACHE STRING - "Flags used by the assembler during all build types.") - -if(NOT CMAKE_NOT_USING_CONFIG_FLAGS) - get_property(_GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - # default build type is none - if(NOT _GENERATOR_IS_MULTI_CONFIG AND NOT CMAKE_NO_BUILD_TYPE) - set (CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE_INIT} CACHE STRING - "Choose the type of build, options are: None, Debug Release RelWithDebInfo MinSizeRel.") - endif() - unset(_GENERATOR_IS_MULTI_CONFIG) - set (CMAKE_ASM${ASM_DIALECT}_FLAGS_DEBUG "${CMAKE_ASM${ASM_DIALECT}_FLAGS_DEBUG_INIT}" CACHE STRING - "Flags used by the assembler during debug builds.") - set (CMAKE_ASM${ASM_DIALECT}_FLAGS_MINSIZEREL "${CMAKE_ASM${ASM_DIALECT}_FLAGS_MINSIZEREL_INIT}" CACHE STRING - "Flags used by the assembler during release minsize builds.") - set (CMAKE_ASM${ASM_DIALECT}_FLAGS_RELEASE "${CMAKE_ASM${ASM_DIALECT}_FLAGS_RELEASE_INIT}" CACHE STRING - "Flags used by the assembler during release builds.") - set (CMAKE_ASM${ASM_DIALECT}_FLAGS_RELWITHDEBINFO "${CMAKE_ASM${ASM_DIALECT}_FLAGS_RELWITHDEBINFO_INIT}" CACHE STRING - "Flags used by the assembler during Release with Debug Info builds.") -endif() - -mark_as_advanced(CMAKE_ASM${ASM_DIALECT}_FLAGS - CMAKE_ASM${ASM_DIALECT}_FLAGS_DEBUG - CMAKE_ASM${ASM_DIALECT}_FLAGS_MINSIZEREL - CMAKE_ASM${ASM_DIALECT}_FLAGS_RELEASE - CMAKE_ASM${ASM_DIALECT}_FLAGS_RELWITHDEBINFO - ) - +cmake_initialize_per_config_variable(CMAKE_ASM${ASM_DIALECT}_FLAGS "Flags used by the ASM${ASM_DIALECT} compiler") if(NOT CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT) set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT "<CMAKE_ASM${ASM_DIALECT}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>") diff --git a/Modules/CMakeCInformation.cmake b/Modules/CMakeCInformation.cmake index 71aadb4..1e46cac 100644 --- a/Modules/CMakeCInformation.cmake +++ b/Modules/CMakeCInformation.cmake @@ -102,30 +102,7 @@ endif() set(CMAKE_C_FLAGS_INIT "$ENV{CFLAGS} ${CMAKE_C_FLAGS_INIT}") -foreach(c "" _DEBUG _RELEASE _MINSIZEREL _RELWITHDEBINFO) - string(STRIP "${CMAKE_C_FLAGS${c}_INIT}" CMAKE_C_FLAGS${c}_INIT) -endforeach() - -set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS_INIT}" CACHE STRING - "Flags used by the compiler during all build types.") - -if(NOT CMAKE_NOT_USING_CONFIG_FLAGS) - get_property(_GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - # default build type is none - if(NOT _GENERATOR_IS_MULTI_CONFIG AND NOT CMAKE_NO_BUILD_TYPE) - set (CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE_INIT} CACHE STRING - "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") - endif() - unset(_GENERATOR_IS_MULTI_CONFIG) - set (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG_INIT}" CACHE STRING - "Flags used by the compiler during debug builds.") - set (CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL_INIT}" CACHE STRING - "Flags used by the compiler during release builds for minimum size.") - set (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE_INIT}" CACHE STRING - "Flags used by the compiler during release builds.") - set (CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO_INIT}" CACHE STRING - "Flags used by the compiler during release builds with debug info.") -endif() +cmake_initialize_per_config_variable(CMAKE_C_FLAGS "Flags used by the C compiler") if(CMAKE_C_STANDARD_LIBRARIES_INIT) set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES_INIT}" @@ -208,13 +185,6 @@ if(NOT CMAKE_EXECUTABLE_RPATH_LINK_C_FLAG) set(CMAKE_EXECUTABLE_RPATH_LINK_C_FLAG ${CMAKE_SHARED_LIBRARY_RPATH_LINK_C_FLAG}) endif() -mark_as_advanced( -CMAKE_C_FLAGS -CMAKE_C_FLAGS_DEBUG -CMAKE_C_FLAGS_MINSIZEREL -CMAKE_C_FLAGS_RELEASE -CMAKE_C_FLAGS_RELWITHDEBINFO -) set(CMAKE_C_INFORMATION_LOADED 1) diff --git a/Modules/CMakeCSharpInformation.cmake b/Modules/CMakeCSharpInformation.cmake index 25f869c..48e1a1e 100644 --- a/Modules/CMakeCSharpInformation.cmake +++ b/Modules/CMakeCSharpInformation.cmake @@ -44,23 +44,8 @@ endif() # use _INIT variables so that this only happens the first time # and you can set these flags in the cmake cache set(CMAKE_CSharp_FLAGS_INIT "$ENV{CSFLAGS} ${CMAKE_CSharp_FLAGS_INIT}") -# avoid just having a space as the initial value for the cache -if(CMAKE_CSharp_FLAGS_INIT STREQUAL " ") - set(CMAKE_CSharp_FLAGS_INIT) -endif() -set (CMAKE_CSharp_FLAGS "${CMAKE_CSharp_FLAGS_INIT}" CACHE STRING - "Flags used by the C# compiler during all build types.") -if(NOT CMAKE_NOT_USING_CONFIG_FLAGS) - set (CMAKE_CSharp_FLAGS_DEBUG "${CMAKE_CSharp_FLAGS_DEBUG_INIT}" CACHE STRING - "Flags used by the C# compiler during debug builds.") - set (CMAKE_CSharp_FLAGS_MINSIZEREL "${CMAKE_CSharp_FLAGS_MINSIZEREL_INIT}" CACHE STRING - "Flags used by the C# compiler during release builds for minimum size.") - set (CMAKE_CSharp_FLAGS_RELEASE "${CMAKE_CSharp_FLAGS_RELEASE_INIT}" CACHE STRING - "Flags used by the C# compiler during release builds.") - set (CMAKE_CSharp_FLAGS_RELWITHDEBINFO "${CMAKE_CSharp_FLAGS_RELWITHDEBINFO_INIT}" CACHE STRING - "Flags used by the C# compiler during release builds with debug info.") -endif() +cmake_initialize_per_config_variable(CMAKE_CSharp_FLAGS "Flags used by the C# compiler") if(CMAKE_CSharp_STANDARD_LIBRARIES_INIT) set(CMAKE_CSharp_STANDARD_LIBRARIES "${CMAKE_CSharp_STANDARD_LIBRARIES_INIT}" @@ -71,49 +56,12 @@ endif() # set missing flags (if they are not defined). This is needed in the # unlikely case that you have only C# and no C/C++ targets in your # project. -if(NOT DEFINED CMAKE_SHARED_LINKER_FLAGS) - set(CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "" FORCE) -endif() -if(NOT DEFINED CMAKE_SHARED_LINKER_FLAGS_DEBUG) - set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "" CACHE STRING "" FORCE) -endif() -if(NOT DEFINED CMAKE_SHARED_LINKER_FLAGS_RELEASE) - set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "" CACHE STRING "" FORCE) -endif() -if(NOT DEFINED CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL) - set(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "" CACHE STRING "" FORCE) -endif() -if(NOT DEFINED CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO) - set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "" CACHE STRING "" FORCE) -endif() - -if(NOT DEFINED CMAKE_EXE_LINKER_FLAGS) - set(CMAKE_EXE_LINKER_FLAGS "" CACHE STRING "" FORCE) -endif() -if(NOT DEFINED CMAKE_EXE_LINKER_FLAGS_DEBUG) - set(CMAKE_EXE_LINKER_FLAGS_DEBUG "" CACHE STRING "" FORCE) -endif() -if(NOT DEFINED CMAKE_EXE_LINKER_FLAGS_RELEASE) - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "" CACHE STRING "" FORCE) -endif() -if(NOT DEFINED CMAKE_EXE_LINKER_FLAGS_MINSIZEREL) - set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "" CACHE STRING "" FORCE) -endif() -if(NOT DEFINED CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO) - set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "" CACHE STRING "" FORCE) -endif() +cmake_initialize_per_config_variable(CMAKE_EXE_LINKER_FLAGS "Flags used by the linker") +cmake_initialize_per_config_variable(CMAKE_SHARED_LINKER_FLAGS "Flags used by the linker during the creation of shared libraries") set(CMAKE_CSharp_CREATE_SHARED_LIBRARY "CSharp_NO_CREATE_SHARED_LIBRARY") set(CMAKE_CSharp_CREATE_SHARED_MODULE "CSharp_NO_CREATE_SHARED_MODULE") set(CMAKE_CSharp_LINK_EXECUTABLE "CSharp_NO_LINK_EXECUTABLE") -mark_as_advanced( - CMAKE_CSharp_FLAGS - CMAKE_CSharp_FLAGS_RELEASE - CMAKE_CSharp_FLAGS_RELWITHDEBINFO - CMAKE_CSharp_FLAGS_MINSIZEREL - CMAKE_CSharp_FLAGS_DEBUG - ) - set(CMAKE_CSharp_USE_RESPONSE_FILE_FOR_OBJECTS 1) set(CMAKE_CSharp_INFORMATION_LOADED 1) diff --git a/Modules/CMakeCUDAInformation.cmake b/Modules/CMakeCUDAInformation.cmake index f4609cd..167e177 100644 --- a/Modules/CMakeCUDAInformation.cmake +++ b/Modules/CMakeCUDAInformation.cmake @@ -74,24 +74,7 @@ endif() # and you can set these flags in the cmake cache set(CMAKE_CUDA_FLAGS_INIT "$ENV{CUDAFLAGS} ${CMAKE_CUDA_FLAGS_INIT}") -foreach(c "" _DEBUG _RELEASE _MINSIZEREL _RELWITHDEBINFO) - string(STRIP "${CMAKE_CUDA_FLAGS${c}_INIT}" CMAKE_CUDA_FLAGS${c}_INIT) -endforeach() - -set (CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS_INIT}" CACHE STRING - "Flags used by the compiler during all build types.") - -if(NOT CMAKE_NOT_USING_CONFIG_FLAGS) - set (CMAKE_CUDA_FLAGS_DEBUG "${CMAKE_CUDA_FLAGS_DEBUG_INIT}" CACHE STRING - "Flags used by the compiler during debug builds.") - set (CMAKE_CUDA_FLAGS_MINSIZEREL "${CMAKE_CUDA_FLAGS_MINSIZEREL_INIT}" CACHE STRING - "Flags used by the compiler during release builds for minimum size.") - set (CMAKE_CUDA_FLAGS_RELEASE "${CMAKE_CUDA_FLAGS_RELEASE_INIT}" CACHE STRING - "Flags used by the compiler during release builds.") - set (CMAKE_CUDA_FLAGS_RELWITHDEBINFO "${CMAKE_CUDA_FLAGS_RELWITHDEBINFO_INIT}" CACHE STRING - "Flags used by the compiler during release builds with debug info.") - -endif() +cmake_initialize_per_config_variable(CMAKE_CUDA_FLAGS "Flags used by the CUDA compiler") if(CMAKE_CUDA_STANDARD_LIBRARIES_INIT) set(CMAKE_CUDA_STANDARD_LIBRARIES "${CMAKE_CUDA_STANDARD_LIBRARIES_INIT}" @@ -207,11 +190,4 @@ endif() unset(_CMAKE_CUDA_EXTRA_DEVICE_LINK_FLAGS) -mark_as_advanced( -CMAKE_CUDA_FLAGS -CMAKE_CUDA_FLAGS_RELEASE -CMAKE_CUDA_FLAGS_RELWITHDEBINFO -CMAKE_CUDA_FLAGS_MINSIZEREL -CMAKE_CUDA_FLAGS_DEBUG) - set(CMAKE_CUDA_INFORMATION_LOADED 1) diff --git a/Modules/CMakeCXXInformation.cmake b/Modules/CMakeCXXInformation.cmake index ec731fa..31ccef7 100644 --- a/Modules/CMakeCXXInformation.cmake +++ b/Modules/CMakeCXXInformation.cmake @@ -197,24 +197,7 @@ endforeach() # and you can set these flags in the cmake cache set(CMAKE_CXX_FLAGS_INIT "$ENV{CXXFLAGS} ${CMAKE_CXX_FLAGS_INIT}") -foreach(c "" _DEBUG _RELEASE _MINSIZEREL _RELWITHDEBINFO) - string(STRIP "${CMAKE_CXX_FLAGS${c}_INIT}" CMAKE_CXX_FLAGS${c}_INIT) -endforeach() - -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_INIT}" CACHE STRING - "Flags used by the compiler during all build types.") - -if(NOT CMAKE_NOT_USING_CONFIG_FLAGS) - set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG_INIT}" CACHE STRING - "Flags used by the compiler during debug builds.") - set (CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL_INIT}" CACHE STRING - "Flags used by the compiler during release builds for minimum size.") - set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE_INIT}" CACHE STRING - "Flags used by the compiler during release builds.") - set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO_INIT}" CACHE STRING - "Flags used by the compiler during release builds with debug info.") - -endif() +cmake_initialize_per_config_variable(CMAKE_CXX_FLAGS "Flags used by the CXX compiler") if(CMAKE_CXX_STANDARD_LIBRARIES_INIT) set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES_INIT}" @@ -287,11 +270,7 @@ endif() mark_as_advanced( CMAKE_VERBOSE_MAKEFILE -CMAKE_CXX_FLAGS -CMAKE_CXX_FLAGS_RELEASE -CMAKE_CXX_FLAGS_RELWITHDEBINFO -CMAKE_CXX_FLAGS_MINSIZEREL -CMAKE_CXX_FLAGS_DEBUG) +) set(CMAKE_CXX_INFORMATION_LOADED 1) diff --git a/Modules/CMakeCommonLanguageInclude.cmake b/Modules/CMakeCommonLanguageInclude.cmake index 43b5da0..b043e18 100644 --- a/Modules/CMakeCommonLanguageInclude.cmake +++ b/Modules/CMakeCommonLanguageInclude.cmake @@ -10,120 +10,14 @@ string(APPEND CMAKE_EXE_LINKER_FLAGS_INIT " $ENV{LDFLAGS}") string(APPEND CMAKE_SHARED_LINKER_FLAGS_INIT " $ENV{LDFLAGS}") string(APPEND CMAKE_MODULE_LINKER_FLAGS_INIT " $ENV{LDFLAGS}") -foreach(t EXE SHARED MODULE STATIC) - foreach(c "" _DEBUG _RELEASE _MINSIZEREL _RELWITHDEBINFO) - string(STRIP "${CMAKE_${t}_LINKER_FLAGS${c}_INIT}" CMAKE_${t}_LINKER_FLAGS${c}_INIT) - endforeach() -endforeach() - -if(NOT CMAKE_NOT_USING_CONFIG_FLAGS) - get_property(_GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - # default build type is none - if(NOT _GENERATOR_IS_MULTI_CONFIG AND NOT CMAKE_NO_BUILD_TYPE) - set (CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE_INIT} CACHE STRING - "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.") - endif() - unset(_GENERATOR_IS_MULTI_CONFIG) - - set (CMAKE_EXE_LINKER_FLAGS_DEBUG ${CMAKE_EXE_LINKER_FLAGS_DEBUG_INIT} CACHE STRING - "Flags used by the linker during debug builds.") - - set (CMAKE_EXE_LINKER_FLAGS_MINSIZEREL ${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL_INIT} CACHE STRING - "Flags used by the linker during release minsize builds.") - - set (CMAKE_EXE_LINKER_FLAGS_RELEASE ${CMAKE_EXE_LINKER_FLAGS_RELEASE_INIT} CACHE STRING - "Flags used by the linker during release builds.") - - set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO - ${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO_INIT} CACHE STRING - "Flags used by the linker during Release with Debug Info builds.") - - set (CMAKE_SHARED_LINKER_FLAGS_DEBUG ${CMAKE_SHARED_LINKER_FLAGS_DEBUG_INIT} CACHE STRING - "Flags used by the linker during debug builds.") - - set (CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL ${CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL_INIT} - CACHE STRING - "Flags used by the linker during release minsize builds.") - - set (CMAKE_SHARED_LINKER_FLAGS_RELEASE ${CMAKE_SHARED_LINKER_FLAGS_RELEASE_INIT} CACHE STRING - "Flags used by the linker during release builds.") - - set (CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO - ${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO_INIT} CACHE STRING - "Flags used by the linker during Release with Debug Info builds.") - - set (CMAKE_MODULE_LINKER_FLAGS_DEBUG ${CMAKE_MODULE_LINKER_FLAGS_DEBUG_INIT} CACHE STRING - "Flags used by the linker during debug builds.") - - set (CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL ${CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL_INIT} - CACHE STRING - "Flags used by the linker during release minsize builds.") - - set (CMAKE_MODULE_LINKER_FLAGS_RELEASE ${CMAKE_MODULE_LINKER_FLAGS_RELEASE_INIT} CACHE STRING - "Flags used by the linker during release builds.") - - set (CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO - ${CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO_INIT} CACHE STRING - "Flags used by the linker during Release with Debug Info builds.") - - set (CMAKE_STATIC_LINKER_FLAGS_DEBUG ${CMAKE_STATIC_LINKER_FLAGS_DEBUG_INIT} CACHE STRING - "Flags used by the linker during debug builds.") - - set (CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL ${CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL_INIT} - CACHE STRING - "Flags used by the linker during release minsize builds.") - - set (CMAKE_STATIC_LINKER_FLAGS_RELEASE ${CMAKE_STATIC_LINKER_FLAGS_RELEASE_INIT} CACHE STRING - "Flags used by the linker during release builds.") - - set (CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO - ${CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO_INIT} CACHE STRING - "Flags used by the linker during Release with Debug Info builds.") -endif() - -# executable linker flags -set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS_INIT}" - CACHE STRING "Flags used by the linker.") - -# shared linker flags -set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_INIT}" - CACHE STRING "Flags used by the linker during the creation of dll's.") - -# module linker flags -set (CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS_INIT}" - CACHE STRING "Flags used by the linker during the creation of modules.") - -# static linker flags -set (CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS_INIT}" - CACHE STRING "Flags used by the linker during the creation of static libraries.") +cmake_initialize_per_config_variable(CMAKE_EXE_LINKER_FLAGS "Flags used by the linker") +cmake_initialize_per_config_variable(CMAKE_SHARED_LINKER_FLAGS "Flags used by the linker during the creation of shared libraries") +cmake_initialize_per_config_variable(CMAKE_MODULE_LINKER_FLAGS "Flags used by the linker during the creation of modules") +cmake_initialize_per_config_variable(CMAKE_STATIC_LINKER_FLAGS "Flags used by the linker during the creation of static libraries") # Alias the build tool variable for backward compatibility. set(CMAKE_BUILD_TOOL ${CMAKE_MAKE_PROGRAM}) mark_as_advanced( CMAKE_VERBOSE_MAKEFILE - -CMAKE_EXE_LINKER_FLAGS -CMAKE_EXE_LINKER_FLAGS_DEBUG -CMAKE_EXE_LINKER_FLAGS_MINSIZEREL -CMAKE_EXE_LINKER_FLAGS_RELEASE -CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO - -CMAKE_SHARED_LINKER_FLAGS -CMAKE_SHARED_LINKER_FLAGS_DEBUG -CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL -CMAKE_SHARED_LINKER_FLAGS_RELEASE -CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO - -CMAKE_MODULE_LINKER_FLAGS -CMAKE_MODULE_LINKER_FLAGS_DEBUG -CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL -CMAKE_MODULE_LINKER_FLAGS_RELEASE -CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO - -CMAKE_STATIC_LINKER_FLAGS -CMAKE_STATIC_LINKER_FLAGS_DEBUG -CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL -CMAKE_STATIC_LINKER_FLAGS_RELEASE -CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO ) diff --git a/Modules/CMakeFortranInformation.cmake b/Modules/CMakeFortranInformation.cmake index d422578..8e5c027 100644 --- a/Modules/CMakeFortranInformation.cmake +++ b/Modules/CMakeFortranInformation.cmake @@ -159,12 +159,7 @@ set(CMAKE_VERBOSE_MAKEFILE FALSE CACHE BOOL "If this value is on, makefiles will set(CMAKE_Fortran_FLAGS_INIT "$ENV{FFLAGS} ${CMAKE_Fortran_FLAGS_INIT}") -foreach(c "" _DEBUG _RELEASE _MINSIZEREL _RELWITHDEBINFO) - string(STRIP "${CMAKE_Fortran_FLAGS${c}_INIT}" CMAKE_Fortran_FLAGS${c}_INIT) -endforeach() - -set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS_INIT}" CACHE STRING - "Flags used by the compiler during all build types.") +cmake_initialize_per_config_variable(CMAKE_Fortran_FLAGS "Flags used by the Fortran compiler") include(CMakeCommonLanguageInclude) @@ -216,24 +211,5 @@ if(CMAKE_Fortran_STANDARD_LIBRARIES_INIT) mark_as_advanced(CMAKE_Fortran_STANDARD_LIBRARIES) endif() -if(NOT CMAKE_NOT_USING_CONFIG_FLAGS) - set (CMAKE_Fortran_FLAGS_DEBUG "${CMAKE_Fortran_FLAGS_DEBUG_INIT}" CACHE STRING - "Flags used by the compiler during debug builds.") - set (CMAKE_Fortran_FLAGS_MINSIZEREL "${CMAKE_Fortran_FLAGS_MINSIZEREL_INIT}" CACHE STRING - "Flags used by the compiler during release builds for minimum size.") - set (CMAKE_Fortran_FLAGS_RELEASE "${CMAKE_Fortran_FLAGS_RELEASE_INIT}" CACHE STRING - "Flags used by the compiler during release builds.") - set (CMAKE_Fortran_FLAGS_RELWITHDEBINFO "${CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT}" CACHE STRING - "Flags used by the compiler during release builds with debug info.") - -endif() - -mark_as_advanced( -CMAKE_Fortran_FLAGS -CMAKE_Fortran_FLAGS_DEBUG -CMAKE_Fortran_FLAGS_MINSIZEREL -CMAKE_Fortran_FLAGS_RELEASE -CMAKE_Fortran_FLAGS_RELWITHDEBINFO) - # set this variable so we can avoid loading this more than once. set(CMAKE_Fortran_INFORMATION_LOADED 1) diff --git a/Modules/CMakeGenericSystem.cmake b/Modules/CMakeGenericSystem.cmake index 324a279..3eb86f9 100644 --- a/Modules/CMakeGenericSystem.cmake +++ b/Modules/CMakeGenericSystem.cmake @@ -1,6 +1,7 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. +include(CMakeInitializeConfigs) set(CMAKE_SHARED_LIBRARY_C_FLAGS "") # -pic set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-shared") # -shared diff --git a/Modules/CMakeInitializeConfigs.cmake b/Modules/CMakeInitializeConfigs.cmake new file mode 100644 index 0000000..9dfe040 --- /dev/null +++ b/Modules/CMakeInitializeConfigs.cmake @@ -0,0 +1,39 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +include_guard(GLOBAL) + +# Initializes `<_PREFIX>_<CONFIG>` variables from the corresponding +# `<_PREFIX>_<CONFIG>_INIT`, for the configurations currently used. +function(cmake_initialize_per_config_variable _PREFIX _DOCSTRING) + string(STRIP "${${_PREFIX}_INIT}" _INIT) + set("${_PREFIX}" "${_INIT}" + CACHE STRING "${_DOCSTRING} during all build types.") + mark_as_advanced("${_PREFIX}") + + if (NOT CMAKE_NOT_USING_CONFIG_FLAGS) + set(_CONFIGS Debug Release MinSizeRel RelWithDebInfo) + + get_property(_GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if (_GENERATOR_IS_MULTI_CONFIG) + list(APPEND _CONFIGS ${CMAKE_CONFIGURATION_TYPES}) + else() + if (NOT CMAKE_NO_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE_INIT}" CACHE STRING + "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel ...") + endif() + list(APPEND _CONFIGS ${CMAKE_BUILD_TYPE}) + endif() + + list(REMOVE_DUPLICATES _CONFIGS) + foreach(_BUILD_TYPE IN LISTS _CONFIGS) + if (NOT "${_BUILD_TYPE}" STREQUAL "") + string(TOUPPER "${_BUILD_TYPE}" _BUILD_TYPE) + string(STRIP "${${_PREFIX}_${_BUILD_TYPE}_INIT}" _INIT) + set("${_PREFIX}_${_BUILD_TYPE}" "${_INIT}" + CACHE STRING "${_DOCSTRING} during ${_BUILD_TYPE} builds.") + mark_as_advanced("${_PREFIX}_${_BUILD_TYPE}") + endif() + endforeach() + endif() +endfunction() diff --git a/Modules/CMakeRCInformation.cmake b/Modules/CMakeRCInformation.cmake index a340288..1227fdf 100644 --- a/Modules/CMakeRCInformation.cmake +++ b/Modules/CMakeRCInformation.cmake @@ -19,23 +19,7 @@ include(Platform/${CMAKE_SYSTEM_NAME}-${CMAKE_BASE_NAME} OPTIONAL) set(CMAKE_RC_FLAGS_INIT "$ENV{RCFLAGS} ${CMAKE_RC_FLAGS_INIT}") -foreach(c "" _DEBUG _RELEASE _MINSIZEREL _RELWITHDEBINFO) - string(STRIP "${CMAKE_RC_FLAGS${c}_INIT}" CMAKE_RC_FLAGS${c}_INIT) -endforeach() - -set (CMAKE_RC_FLAGS "${CMAKE_RC_FLAGS_INIT}" CACHE STRING - "Flags for Windows Resource Compiler.") - -if(NOT CMAKE_NOT_USING_CONFIG_FLAGS) - set (CMAKE_RC_FLAGS_DEBUG "${CMAKE_RC_FLAGS_DEBUG_INIT}" CACHE STRING - "Flags for Windows Resource Compiler during debug builds.") - set (CMAKE_RC_FLAGS_MINSIZEREL "${CMAKE_RC_FLAGS_MINSIZEREL_INIT}" CACHE STRING - "Flags for Windows Resource Compiler during release builds for minimum size.") - set (CMAKE_RC_FLAGS_RELEASE "${CMAKE_RC_FLAGS_RELEASE_INIT}" CACHE STRING - "Flags for Windows Resource Compiler during release builds.") - set (CMAKE_RC_FLAGS_RELWITHDEBINFO "${CMAKE_RC_FLAGS_RELWITHDEBINFO_INIT}" CACHE STRING - "Flags for Windows Resource Compiler during release builds with debug info.") -endif() +cmake_initialize_per_config_variable(CMAKE_RC_FLAGS "Flags for Windows Resource Compiler") # These are the only types of flags that should be passed to the rc # command, if COMPILE_FLAGS is used on a target this will be used @@ -51,12 +35,5 @@ if(NOT CMAKE_RC_COMPILE_OBJECT) "<CMAKE_RC_COMPILER> <DEFINES> <INCLUDES> <FLAGS> /fo<OBJECT> <SOURCE>") endif() -mark_as_advanced( -CMAKE_RC_FLAGS -CMAKE_RC_FLAGS_DEBUG -CMAKE_RC_FLAGS_MINSIZEREL -CMAKE_RC_FLAGS_RELEASE -CMAKE_RC_FLAGS_RELWITHDEBINFO -) # set this variable so we can avoid loading this more than once. set(CMAKE_RC_INFORMATION_LOADED 1) diff --git a/Modules/CMakeSystemSpecificInformation.cmake b/Modules/CMakeSystemSpecificInformation.cmake index 03f348d..66f1722 100644 --- a/Modules/CMakeSystemSpecificInformation.cmake +++ b/Modules/CMakeSystemSpecificInformation.cmake @@ -37,7 +37,6 @@ if(NOT _INCLUDED_SYSTEM_INFO_FILE) endif() endif() - # optionally include a file which can do extra-generator specific things, e.g. # CMakeFindEclipseCDT4.cmake asks gcc for the system include dirs for the Eclipse CDT4 generator if(CMAKE_EXTRA_GENERATOR) diff --git a/Modules/CheckIncludeFile.cmake b/Modules/CheckIncludeFile.cmake index e5554c4..501fc9a 100644 --- a/Modules/CheckIncludeFile.cmake +++ b/Modules/CheckIncludeFile.cmake @@ -27,6 +27,8 @@ # list of macros to define (-DFOO=bar) # ``CMAKE_REQUIRED_INCLUDES`` # list of include directories +# ``CMAKE_REQUIRED_LIBRARIES`` +# list of libraries to link # ``CMAKE_REQUIRED_QUIET`` # execute quietly without messages # @@ -59,6 +61,7 @@ macro(CHECK_INCLUDE_FILE INCLUDE VARIABLE) ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckIncludeFile.c COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_INCLUDE_FILE_FLAGS} "${CHECK_INCLUDE_FILE_C_INCLUDE_DIRS}" diff --git a/Modules/CheckIncludeFileCXX.cmake b/Modules/CheckIncludeFileCXX.cmake index 7948bab..cdb25fb 100644 --- a/Modules/CheckIncludeFileCXX.cmake +++ b/Modules/CheckIncludeFileCXX.cmake @@ -27,6 +27,8 @@ # list of macros to define (-DFOO=bar) # ``CMAKE_REQUIRED_INCLUDES`` # list of include directories +# ``CMAKE_REQUIRED_LIBRARIES`` +# list of libraries to link # ``CMAKE_REQUIRED_QUIET`` # execute quietly without messages # @@ -58,6 +60,7 @@ macro(CHECK_INCLUDE_FILE_CXX INCLUDE VARIABLE) ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/CheckIncludeFile.cxx COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_INCLUDE_FILE_FLAGS} "${CHECK_INCLUDE_FILE_CXX_INCLUDE_DIRS}" diff --git a/Modules/CheckIncludeFiles.cmake b/Modules/CheckIncludeFiles.cmake index 59afdab..14db68c 100644 --- a/Modules/CheckIncludeFiles.cmake +++ b/Modules/CheckIncludeFiles.cmake @@ -33,6 +33,8 @@ # list of macros to define (-DFOO=bar) # ``CMAKE_REQUIRED_INCLUDES`` # list of include directories +# ``CMAKE_REQUIRED_LIBRARIES`` +# list of libraries to link # ``CMAKE_REQUIRED_QUIET`` # execute quietly without messages # @@ -102,6 +104,7 @@ macro(CHECK_INCLUDE_FILES INCLUDE VARIABLE) ${CMAKE_BINARY_DIR} ${src} COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS} + LINK_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_INCLUDE_FILES_FLAGS} "${CHECK_INCLUDE_FILES_INCLUDE_DIRS}" diff --git a/Modules/Compiler/MSVC-ASM.cmake b/Modules/Compiler/MSVC-ASM.cmake new file mode 100644 index 0000000..45978c5 --- /dev/null +++ b/Modules/Compiler/MSVC-ASM.cmake @@ -0,0 +1 @@ +# This file is loaded when Visual Studio is used for the ASM language. diff --git a/Modules/DeployQt4.cmake b/Modules/DeployQt4.cmake index 28054f8..e758f3a 100644 --- a/Modules/DeployQt4.cmake +++ b/Modules/DeployQt4.cmake @@ -298,8 +298,14 @@ function(install_qt4_plugin plugin executable copy installed_plugin_path_var) get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(_isMultiConfig OR CMAKE_BUILD_TYPE) - install_qt4_plugin_path("${plugin_release}" "${executable}" "${copy}" "${installed_plugin_path_var}_release" "${plugins_dir}" "${component}" "Release|RelWithDebInfo|MinSizeRel") + set(_RELEASE_CONFIGS ${CMAKE_CONFIGURATION_TYPES} "${CMAKE_BUILD_TYPE}") + if (_RELEASE_CONFIGS) + list(FILTER _RELEASE_CONFIGS EXCLUDE REGEX "[Dd][Ee][Bb][Uu][Gg]") + endif() + string(REPLACE ";" "|" _RELEASE_CONFIGS "${_RELEASE_CONFIGS}") + install_qt4_plugin_path("${plugin_release}" "${executable}" "${copy}" "${installed_plugin_path_var}_release" "${plugins_dir}" "${component}" "${_RELEASE_CONFIGS}") install_qt4_plugin_path("${plugin_debug}" "${executable}" "${copy}" "${installed_plugin_path_var}_debug" "${plugins_dir}" "${component}" "Debug") + unset(_RELEASE_CONFIGS) if(CMAKE_BUILD_TYPE MATCHES "^Debug$") set(${installed_plugin_path_var} ${${installed_plugin_path_var}_debug}) diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake index a0fd90f..ca2a9c5 100644 --- a/Modules/FindBoost.cmake +++ b/Modules/FindBoost.cmake @@ -791,9 +791,8 @@ function(_Boost_COMPONENT_DEPENDENCIES component _ret) set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic) set(_Boost_WSERIALIZATION_DEPENDENCIES serialization) endif() - if(NOT Boost_VERSION VERSION_LESS 106600) + if(NOT Boost_VERSION VERSION_LESS 106700) message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets") - set(_Boost_IMPORTED_TARGETS FALSE) endif() endif() @@ -825,8 +824,8 @@ function(_Boost_COMPONENT_HEADERS component _hdrs) set(_Boost_CONTAINER_HEADERS "boost/container/container_fwd.hpp") set(_Boost_CONTEXT_HEADERS "boost/context/all.hpp") set(_Boost_COROUTINE_HEADERS "boost/coroutine/all.hpp") - set(_Boost_EXCEPTION_HEADERS "boost/exception/exception.hpp") set(_Boost_DATE_TIME_HEADERS "boost/date_time/date.hpp") + set(_Boost_EXCEPTION_HEADERS "boost/exception/exception.hpp") set(_Boost_FIBER_HEADERS "boost/fiber/all.hpp") set(_Boost_FILESYSTEM_HEADERS "boost/filesystem/path.hpp") set(_Boost_GRAPH_HEADERS "boost/graph/adjacency_list.hpp") @@ -925,8 +924,8 @@ endfunction() # `${Boost_ROOT}/libs/fiber/build/Jamfile.v2`. # function(_Boost_COMPILER_FEATURES component _ret) - # Boost >= 1.62 and < 1.65 - if(NOT Boost_VERSION VERSION_LESS 106200 AND Boost_VERSION VERSION_LESS 106500) + # Boost >= 1.62 and < 1.67 + if(NOT Boost_VERSION VERSION_LESS 106200 AND Boost_VERSION VERSION_LESS 106700) set(_Boost_FIBER_COMPILER_FEATURES cxx_alias_templates cxx_auto_type @@ -1032,7 +1031,7 @@ else() # _Boost_COMPONENT_HEADERS. See the instructions at the top of # _Boost_COMPONENT_DEPENDENCIES. set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS} - "1.65.1" "1.65.0" "1.65" + "1.66.0" "1.66" "1.65.1" "1.65.0" "1.65" "1.64.0" "1.64" "1.63.0" "1.63" "1.62.0" "1.62" "1.61.0" "1.61" "1.60.0" "1.60" "1.59.0" "1.59" "1.58.0" "1.58" "1.57.0" "1.57" "1.56.0" "1.56" "1.55.0" "1.55" "1.54.0" "1.54" "1.53.0" "1.53" "1.52.0" "1.52" "1.51.0" "1.51" @@ -1379,8 +1378,11 @@ if(Boost_DEBUG) endif() #====================== -# Systematically build up the Boost ABI tag -# http://boost.org/doc/libs/1_41_0/more/getting_started/windows.html#library-naming +# Systematically build up the Boost ABI tag for the 'tagged' and 'versioned' layouts +# http://boost.org/doc/libs/1_66_0/more/getting_started/windows.html#library-naming +# http://boost.org/doc/libs/1_66_0/boost/config/auto_link.hpp +# http://boost.org/doc/libs/1_66_0/tools/build/src/tools/common.jam +# http://boost.org/doc/libs/1_66_0/boostcpp.jam set( _boost_RELEASE_ABI_TAG "-") set( _boost_DEBUG_ABI_TAG "-") # Key Use this library when: @@ -1412,11 +1414,40 @@ if(Boost_USE_STLPORT) string(APPEND _boost_DEBUG_ABI_TAG "p") endif() # n using the STLport deprecated "native iostreams" feature +# removed from the documentation in 1.43.0 but still present in +# boost/config/auto_link.hpp if(Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS) string(APPEND _boost_RELEASE_ABI_TAG "n") string(APPEND _boost_DEBUG_ABI_TAG "n") endif() +# -x86 Architecture and address model tag +# First character is the architecture, then word-size, either 32 or 64 +# Only used in 'versioned' layout, added in Boost 1.66.0 +set(_boost_ARCHITECTURE_TAG "") +# {CMAKE_CXX_COMPILER_ARCHITECTURE_ID} is not currently set for all compilers +if(NOT "x${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "x" AND NOT Boost_VERSION VERSION_LESS 106600) + string(APPEND _boost_ARCHITECTURE_TAG "-") + # This needs to be kept in-sync with the section of CMakePlatformId.h.in + # inside 'defined(_WIN32) && defined(_MSC_VER)' + if(${CMAKE_CXX_COMPILER_ARCHITECTURE_ID} STREQUAL "IA64") + string(APPEND _boost_ARCHITECTURE_TAG "i") + elseif(${CMAKE_CXX_COMPILER_ARCHITECTURE_ID} STREQUAL "X86" + OR ${CMAKE_CXX_COMPILER_ARCHITECTURE_ID} STREQUAL "x64") + string(APPEND _boost_ARCHITECTURE_TAG "x") + elseif(${CMAKE_CXX_COMPILER_ARCHITECTURE_ID} MATCHES "^ARM") + string(APPEND _boost_ARCHITECTURE_TAG "a") + elseif(${CMAKE_CXX_COMPILER_ARCHITECTURE_ID} STREQUAL "MIPS") + string(APPEND _boost_ARCHITECTURE_TAG "m") + endif() + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + string(APPEND _boost_ARCHITECTURE_TAG "64") + else() + string(APPEND _boost_ARCHITECTURE_TAG "32") + endif() +endif() + if(Boost_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " "_boost_RELEASE_ABI_TAG = ${_boost_RELEASE_ABI_TAG}") @@ -1464,6 +1495,7 @@ foreach(c DEBUG RELEASE) ${Boost_INCLUDE_DIR}/stage/lib ) _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "${Boost_INCLUDE_DIR}/..") + _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "${Boost_INCLUDE_DIR}") if( Boost_NO_SYSTEM_PATHS ) list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH) else() @@ -1610,22 +1642,22 @@ foreach(COMPONENT ${Boost_FIND_COMPONENTS}) unset(_boost_RELEASE_NAMES) foreach(compiler IN LISTS _boost_COMPILER) list(APPEND _boost_RELEASE_NAMES - ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} ) endforeach() list(APPEND _boost_RELEASE_NAMES - ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT} ) if(_boost_STATIC_RUNTIME_WORKAROUND) set(_boost_RELEASE_STATIC_ABI_TAG "-s${_boost_RELEASE_ABI_TAG}") foreach(compiler IN LISTS _boost_COMPILER) list(APPEND _boost_RELEASE_NAMES - ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} ) endforeach() list(APPEND _boost_RELEASE_NAMES - ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} ) endif() if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread") @@ -1660,11 +1692,11 @@ foreach(COMPONENT ${Boost_FIND_COMPONENTS}) unset(_boost_DEBUG_NAMES) foreach(compiler IN LISTS _boost_COMPILER) list(APPEND _boost_DEBUG_NAMES - ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} ) endforeach() list(APPEND _boost_DEBUG_NAMES - ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED} ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT} ) @@ -1672,11 +1704,11 @@ foreach(COMPONENT ${Boost_FIND_COMPONENTS}) set(_boost_DEBUG_STATIC_ABI_TAG "-s${_boost_DEBUG_ABI_TAG}") foreach(compiler IN LISTS _boost_COMPILER) list(APPEND _boost_DEBUG_NAMES - ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} ) endforeach() list(APPEND _boost_DEBUG_NAMES - ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}-${Boost_LIB_VERSION} + ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION} ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${COMPONENT}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} ) endif() if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread") diff --git a/Modules/FindCUDA.cmake b/Modules/FindCUDA.cmake index 9176db4..0decbb5 100644 --- a/Modules/FindCUDA.cmake +++ b/Modules/FindCUDA.cmake @@ -104,7 +104,7 @@ # CUDA_NVCC_FLAGS_<CONFIG> variables. For Visual Studio targets, # the host compiler is constructed with one or more visual studio macros # such as $(VCInstallDir), that expands out to the path when -# the command is run from withing VS. +# the command is run from within VS. # # CUDA_NVCC_FLAGS # CUDA_NVCC_FLAGS_<CONFIG> @@ -525,7 +525,7 @@ set(CUDA_GENERATED_OUTPUT_DIR "" CACHE PATH "Directory to put all the output fil option(CUDA_HOST_COMPILATION_CPP "Generated file extension" ON) # Extra user settable flags -set(CUDA_NVCC_FLAGS "" CACHE STRING "Semi-colon delimit multiple arguments.") +cmake_initialize_per_config_variable(CUDA_NVCC_FLAGS "Semi-colon delimit multiple arguments.") if(CMAKE_GENERATOR MATCHES "Visual Studio") set(_CUDA_MSVC_HOST_COMPILER "$(VCInstallDir)Tools/MSVC/$(VCToolsVersion)/bin/Host$(Platform)/$(PlatformTarget)") @@ -585,23 +585,6 @@ mark_as_advanced( CUDA_SEPARABLE_COMPILATION ) -# Single config generators like Makefiles or Ninja don't usually have -# CMAKE_CONFIGURATION_TYPES defined (but note that it can be defined if set by -# projects or developers). Even CMAKE_BUILD_TYPE might not be defined for -# single config generators (and should not be defined for multi-config -# generators). To ensure we get a complete superset of all possible -# configurations, we combine CMAKE_CONFIGURATION_TYPES, CMAKE_BUILD_TYPE and -# all of the standard configurations, then weed out duplicates with -# list(REMOVE_DUPLICATES). Looping over the unique set then ensures we have -# each configuration-specific set of nvcc flags defined and marked as advanced. -set(CUDA_configuration_types ${CMAKE_CONFIGURATION_TYPES} ${CMAKE_BUILD_TYPE} Debug MinSizeRel Release RelWithDebInfo) -list(REMOVE_DUPLICATES CUDA_configuration_types) -foreach(config ${CUDA_configuration_types}) - string(TOUPPER ${config} config_upper) - set(CUDA_NVCC_FLAGS_${config_upper} "" CACHE STRING "Semi-colon delimit multiple arguments.") - mark_as_advanced(CUDA_NVCC_FLAGS_${config_upper}) -endforeach() - ############################################################################### ############################################################################### # Locate CUDA, Set Build Type, etc. diff --git a/Modules/FindDoxygen.cmake b/Modules/FindDoxygen.cmake index 8aea4e0..599d799 100644 --- a/Modules/FindDoxygen.cmake +++ b/Modules/FindDoxygen.cmake @@ -411,6 +411,8 @@ macro(_Doxygen_find_doxygen) "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\doxygen_is1;Inno Setup: App Path]/bin" /Applications/Doxygen.app/Contents/Resources /Applications/Doxygen.app/Contents/MacOS + /Applications/Utilities/Doxygen.app/Contents/Resources + /Applications/Utilities/Doxygen.app/Contents/MacOS DOC "Doxygen documentation generation tool (http://www.doxygen.org)" ) mark_as_advanced(DOXYGEN_EXECUTABLE) @@ -492,8 +494,11 @@ macro(_Doxygen_find_dot) "C:/Program Files/ATT/Graphviz/bin" [HKEY_LOCAL_MACHINE\\SOFTWARE\\ATT\\Graphviz;InstallPath]/bin /Applications/Graphviz.app/Contents/MacOS + /Applications/Utilities/Graphviz.app/Contents/MacOS /Applications/Doxygen.app/Contents/Resources /Applications/Doxygen.app/Contents/MacOS + /Applications/Utilities/Doxygen.app/Contents/Resources + /Applications/Utilities/Doxygen.app/Contents/MacOS DOC "Dot tool for use with Doxygen" ) mark_as_advanced(DOXYGEN_DOT_EXECUTABLE) diff --git a/Modules/FindMPI.cmake b/Modules/FindMPI.cmake index db14a89..c5eabbb 100644 --- a/Modules/FindMPI.cmake +++ b/Modules/FindMPI.cmake @@ -744,13 +744,22 @@ function(_MPI_guess_settings LANG) endif() mark_as_advanced(MPI_${LANG}_LIB_NAMES) set(MPI_GUESS_FOUND TRUE) + + if(_MPIEXEC_NOT_GIVEN) + unset(MPIEXEC_EXECUTABLE CACHE) + endif() + + find_program(MPIEXEC_EXECUTABLE + NAMES mpiexec + HINTS $ENV{MSMPI_BIN} "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MPI;InstallRoot]/Bin" + DOC "Executable for running MPI programs.") endif() endif() # At this point there's not many MPIs that we could still consider. # OpenMPI 1.6.x and below supported Windows, but these ship compiler wrappers that still work. # The only other relevant MPI implementation without a wrapper is MPICH2, which had Windows support in 1.4.1p1 and older. - if(NOT MPI_GUESS_LIBRARY_NAME OR "${MPI_GUESS_LIBRARY_NAME}" STREQUAL "MPICH2") + if(NOT MPI_GUESS_FOUND AND (NOT MPI_GUESS_LIBRARY_NAME OR "${MPI_GUESS_LIBRARY_NAME}" STREQUAL "MPICH2")) set(MPI_MPICH_PREFIX_PATHS "$ENV{ProgramW6432}/MPICH2/lib" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MPICH\\SMPD;binary]/../lib" @@ -809,6 +818,17 @@ function(_MPI_guess_settings LANG) unset(MPI_MPICH_ROOT_DIR) endif() set(MPI_GUESS_FOUND TRUE) + + if(_MPIEXEC_NOT_GIVEN) + unset(MPIEXEC_EXECUTABLE CACHE) + endif() + + find_program(MPIEXEC_EXECUTABLE + NAMES ${_MPIEXEC_NAMES} + HINTS "$ENV{ProgramW6432}/MPICH2/bin" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MPICH\\SMPD;binary]" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MPICH2;Path]/bin" + DOC "Executable for running MPI programs.") endif() unset(MPI_MPICH_PREFIX_PATHS) endif() @@ -1034,9 +1054,6 @@ if("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux") # SUSE Linux Enterprise Server stores its MPI implementations under /usr/lib64/mpi/gcc/<name> # We enumerate the subfolders and append each as a prefix MPI_search_mpi_prefix_folder("/usr/lib64/mpi/gcc") -elseif("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Windows") - # MSMPI stores its runtime in a special folder, this adds the possible locations to the hints. - list(APPEND MPI_HINT_DIRS $ENV{MSMPI_BIN} "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\MPI;InstallRoot]") elseif("${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "FreeBSD") # FreeBSD ships mpich under the normal system paths - but available openmpi implementations # will be found in /usr/local/mpi/<name> @@ -1046,6 +1063,15 @@ endif() # Most MPI distributions have some form of mpiexec or mpirun which gives us something we can look for. # The MPI standard does not mandate the existence of either, but instead only makes requirements if a distribution # ships an mpiexec program (mpirun executables are not regulated by the standard). + +# We defer searching for mpiexec binaries belonging to guesses until later. By doing so, mismatches between mpiexec +# and the MPI we found should be reduced. +if(NOT MPIEXEC_EXECUTABLE) + set(_MPIEXEC_NOT_GIVEN TRUE) +else() + set(_MPIEXEC_NOT_GIVEN FALSE) +endif() + find_program(MPIEXEC_EXECUTABLE NAMES ${_MPIEXEC_NAMES} PATH_SUFFIXES bin sbin @@ -1233,17 +1259,21 @@ foreach(LANG IN ITEMS C CXX Fortran) endif() endif() - if(NOT MPI_SKIP_GUESSING AND NOT MPI_${LANG}_WRAPPER_FOUND AND NOT MPI_PINNED_COMPILER) - # For C++, we may use the settings for C. Should a given compiler wrapper for C++ not exist, but one for C does, we copy over the - # settings for C. An MPI distribution that is in this situation would be IBM Platform MPI. - if("${LANG}" STREQUAL "CXX" AND MPI_C_WRAPPER_FOUND) - set(MPI_${LANG}_COMPILE_OPTIONS ${MPI_C_COMPILE_OPTIONS} CACHE STRING "MPI ${LANG} compilation options" ) - set(MPI_${LANG}_COMPILE_DEFINITIONS ${MPI_C_COMPILE_DEFINITIONS} CACHE STRING "MPI ${LANG} compilation definitions" ) - set(MPI_${LANG}_ADDITIONAL_INCLUDE_DIRS ${MPI_C_INCLUDE_DIRS} CACHE STRING "MPI ${LANG} additional include directories") - set(MPI_${LANG}_LINK_FLAGS ${MPI_C_LINK_FLAGS} CACHE STRING "MPI ${LANG} linker flags" ) - set(MPI_${LANG}_LIB_NAMES ${MPI_C_LIB_NAMES} CACHE STRING "MPI ${LANG} libraries to link against" ) - else() - _MPI_guess_settings(${LANG}) + if(NOT MPI_PINNED_COMPILER AND NOT MPI_${LANG}_WRAPPER_FOUND) + # If MPI_PINNED_COMPILER wasn't given, and the MPI compiler we potentially found didn't work, we withdraw it. + set(MPI_${LANG}_COMPILER "MPI_${LANG}_COMPILER-NOTFOUND" CACHE FILEPATH "MPI compiler for ${LANG}" FORCE) + if(NOT MPI_SKIP_GUESSING) + # For C++, we may use the settings for C. Should a given compiler wrapper for C++ not exist, but one for C does, we copy over the + # settings for C. An MPI distribution that is in this situation would be IBM Platform MPI. + if("${LANG}" STREQUAL "CXX" AND MPI_C_WRAPPER_FOUND) + set(MPI_${LANG}_COMPILE_OPTIONS ${MPI_C_COMPILE_OPTIONS} CACHE STRING "MPI ${LANG} compilation options" ) + set(MPI_${LANG}_COMPILE_DEFINITIONS ${MPI_C_COMPILE_DEFINITIONS} CACHE STRING "MPI ${LANG} compilation definitions" ) + set(MPI_${LANG}_ADDITIONAL_INCLUDE_DIRS ${MPI_C_INCLUDE_DIRS} CACHE STRING "MPI ${LANG} additional include directories") + set(MPI_${LANG}_LINK_FLAGS ${MPI_C_LINK_FLAGS} CACHE STRING "MPI ${LANG} linker flags" ) + set(MPI_${LANG}_LIB_NAMES ${MPI_C_LIB_NAMES} CACHE STRING "MPI ${LANG} libraries to link against" ) + else() + _MPI_guess_settings(${LANG}) + endif() endif() endif() endif() diff --git a/Modules/FindTCL.cmake b/Modules/FindTCL.cmake index 80779b3..19eb932 100644 --- a/Modules/FindTCL.cmake +++ b/Modules/FindTCL.cmake @@ -116,7 +116,7 @@ find_library(TCL_LIBRARY NAMES tcl tcl${TCL_LIBRARY_VERSION} tcl${TCL_TCLSH_VERSION} tcl${TK_WISH_VERSION} - tcl86 tcl8.6 + tcl86 tcl8.6 tcl86t tcl8.6t tcl85 tcl8.5 tcl84 tcl8.4 tcl83 tcl8.3 @@ -130,7 +130,7 @@ find_library(TK_LIBRARY NAMES tk tk${TK_LIBRARY_VERSION} tk${TCL_TCLSH_VERSION} tk${TK_WISH_VERSION} - tk86 tk8.6 + tk86 tk8.6 tk86t tk8.6t tk85 tk8.5 tk84 tk8.4 tk83 tk8.3 diff --git a/Modules/Platform/AIX-GNU.cmake b/Modules/Platform/AIX-GNU.cmake index 7f08c4a..0abbb61 100644 --- a/Modules/Platform/AIX-GNU.cmake +++ b/Modules/Platform/AIX-GNU.cmake @@ -24,5 +24,7 @@ macro(__aix_compiler_gnu lang) set(CMAKE_${lang}_USE_IMPLICIT_LINK_DIRECTORIES_IN_RUNTIME_PATH 1) set(CMAKE_${lang}_LINK_FLAGS "-Wl,-bnoipath") - unset(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY) + if(CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 7 OR CMAKE_SYSTEM_VERSION VERSION_LESS 7.1) + unset(CMAKE_${lang}_COMPILE_OPTIONS_VISIBILITY) + endif() endmacro() diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 7031d5a..cd1287c 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -313,8 +313,8 @@ set(SRCS cmQtAutoGen.h cmQtAutoGenerator.cxx cmQtAutoGenerator.h - cmQtAutoGeneratorInitializer.cxx - cmQtAutoGeneratorInitializer.h + cmQtAutoGenInitializer.cxx + cmQtAutoGenInitializer.h cmQtAutoGeneratorMocUic.cxx cmQtAutoGeneratorMocUic.h cmQtAutoGeneratorRcc.cxx @@ -327,6 +327,7 @@ set(SRCS cmSourceFile.h cmSourceFileLocation.cxx cmSourceFileLocation.h + cmSourceFileLocationKind.h cmSourceGroup.cxx cmSourceGroup.h cmState.cxx diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 6fb5057..353ee61 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 10) -set(CMake_VERSION_PATCH 20180109) +set(CMake_VERSION_PATCH 20180119) #set(CMake_VERSION_RC 1) diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index ae07feb..53c47a2 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx @@ -9,10 +9,14 @@ #include "cmSystemTools.h" #include "cmWorkingDirectory.h" +#include "cm_uv.h" + #include "cmsys/FStream.hxx" #include "cmsys/String.hxx" #include "cmsys/SystemInformation.hxx" + #include <algorithm> +#include <chrono> #include <iomanip> #include <list> #include <math.h> @@ -21,6 +25,43 @@ #include <stdlib.h> #include <utility> +#if defined(CMAKE_USE_SYSTEM_LIBUV) && !defined(_WIN32) && \ + UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR < 19 +#define CMAKE_UV_SIGNAL_HACK +/* + libuv does not use SA_RESTART on its signal handler, but C++ streams + depend on it for reliable i/o operations. This RAII helper convinces + libuv to install its handler, and then revises the handler to add the + SA_RESTART flag. We use a distinct uv loop that never runs to avoid + ever really getting a callback. libuv may fill the hack loop's signal + pipe and then stop writing, but that won't break any real loops. + */ +class cmUVSignalHackRAII +{ + uv_loop_t HackLoop; + cm::uv_signal_ptr HackSignal; + static void HackCB(uv_signal_t*, int) {} +public: + cmUVSignalHackRAII() + { + uv_loop_init(&this->HackLoop); + this->HackSignal.init(this->HackLoop); + this->HackSignal.start(HackCB, SIGCHLD); + struct sigaction hack_sa; + sigaction(SIGCHLD, NULL, &hack_sa); + if (!(hack_sa.sa_flags & SA_RESTART)) { + hack_sa.sa_flags |= SA_RESTART; + sigaction(SIGCHLD, &hack_sa, NULL); + } + } + ~cmUVSignalHackRAII() + { + this->HackSignal.stop(); + uv_loop_close(&this->HackLoop); + } +}; +#endif + class TestComparator { public: @@ -95,24 +136,32 @@ void cmCTestMultiProcessHandler::RunTests() if (this->HasCycles) { return; } +#ifdef CMAKE_UV_SIGNAL_HACK + cmUVSignalHackRAII hackRAII; +#endif this->TestHandler->SetMaxIndex(this->FindMaxIndex()); + + uv_loop_init(&this->Loop); this->StartNextTests(); - while (!this->Tests.empty()) { - if (this->StopTimePassed) { - return; - } - this->CheckOutput(); - this->StartNextTests(); - } - // let all running tests finish - while (this->CheckOutput()) { - } + uv_run(&this->Loop, UV_RUN_DEFAULT); + uv_loop_close(&this->Loop); + this->MarkFinished(); this->UpdateCostData(); } -void cmCTestMultiProcessHandler::StartTestProcess(int test) +bool cmCTestMultiProcessHandler::StartTestProcess(int test) { + std::chrono::system_clock::time_point stop_time = this->CTest->GetStopTime(); + if (stop_time != std::chrono::system_clock::time_point() && + stop_time <= std::chrono::system_clock::now()) { + cmCTestLog(this->CTest, ERROR_MESSAGE, "The stop time has been passed. " + "Stopping all tests." + << std::endl); + this->StopTimePassed = true; + return false; + } + cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "test " << test << "\n", this->Quiet); this->TestRunningMap[test] = true; // mark the test as running @@ -120,7 +169,7 @@ void cmCTestMultiProcessHandler::StartTestProcess(int test) this->EraseTest(test); this->RunningCount += GetProcessorsUsed(test); - cmCTestRunTest* testRun = new cmCTestRunTest(this->TestHandler); + cmCTestRunTest* testRun = new cmCTestRunTest(*this); if (this->CTest->GetRepeatUntilFail()) { testRun->SetRunUntilFailOn(); testRun->SetNumberOfRuns(this->CTest->GetTestRepeat()); @@ -143,28 +192,11 @@ void cmCTestMultiProcessHandler::StartTestProcess(int test) this->LockResources(test); if (testRun->StartTest(this->Total)) { - this->RunningTests.insert(testRun); - } else if (testRun->IsStopTimePassed()) { - this->StopTimePassed = true; - delete testRun; - return; - } else { - - for (auto& j : this->Tests) { - j.second.erase(test); - } - - this->UnlockResources(test); - this->Completed++; - this->TestFinishMap[test] = true; - this->TestRunningMap[test] = false; - this->RunningCount -= GetProcessorsUsed(test); - testRun->EndTest(this->Completed, this->Total, false); - if (!this->Properties[test]->Disabled) { - this->Failed->push_back(this->Properties[test]->Name); - } - delete testRun; + return true; } + + this->FinishTestProcess(testRun, false); + return false; } void cmCTestMultiProcessHandler::LockResources(int index) @@ -222,8 +254,7 @@ bool cmCTestMultiProcessHandler::StartTest(int test) // if there are no depends left then run this test if (this->Tests[test].empty()) { - this->StartTestProcess(test); - return true; + return this->StartTestProcess(test); } // This test was not able to start because it is waiting // on depends to run @@ -233,6 +264,11 @@ bool cmCTestMultiProcessHandler::StartTest(int test) void cmCTestMultiProcessHandler::StartNextTests() { size_t numToStart = 0; + + if (this->Tests.empty()) { + return; + } + if (this->RunningCount < this->ParallelLevel) { numToStart = this->ParallelLevel - this->RunningCount; } @@ -365,45 +401,42 @@ void cmCTestMultiProcessHandler::StartNextTests() } } -bool cmCTestMultiProcessHandler::CheckOutput() +void cmCTestMultiProcessHandler::FinishTestProcess(cmCTestRunTest* runner, + bool started) { - // no more output we are done - if (this->RunningTests.empty()) { - return false; - } - std::vector<cmCTestRunTest*> finished; - std::string out, err; - for (cmCTestRunTest* p : this->RunningTests) { - if (!p->CheckOutput()) { - finished.push_back(p); - } - } - for (cmCTestRunTest* p : finished) { - this->Completed++; - int test = p->GetIndex(); + this->Completed++; + + int test = runner->GetIndex(); + auto properties = runner->GetTestProperties(); - bool testResult = p->EndTest(this->Completed, this->Total, true); - if (p->StartAgain()) { + bool testResult = runner->EndTest(this->Completed, this->Total, started); + if (started) { + if (runner->StartAgain()) { this->Completed--; // remove the completed test because run again - continue; - } - if (testResult) { - this->Passed->push_back(p->GetTestProperties()->Name); - } else { - this->Failed->push_back(p->GetTestProperties()->Name); - } - for (auto& t : this->Tests) { - t.second.erase(test); + return; } - this->TestFinishMap[test] = true; - this->TestRunningMap[test] = false; - this->RunningTests.erase(p); - this->WriteCheckpoint(test); - this->UnlockResources(test); - this->RunningCount -= GetProcessorsUsed(test); - delete p; } - return true; + + if (testResult) { + this->Passed->push_back(properties->Name); + } else if (!properties->Disabled) { + this->Failed->push_back(properties->Name); + } + + for (auto& t : this->Tests) { + t.second.erase(test); + } + + this->TestFinishMap[test] = true; + this->TestRunningMap[test] = false; + this->WriteCheckpoint(test); + this->UnlockResources(test); + this->RunningCount -= GetProcessorsUsed(test); + + delete runner; + if (started) { + this->StartNextTests(); + } } void cmCTestMultiProcessHandler::UpdateCostData() @@ -670,7 +703,7 @@ void cmCTestMultiProcessHandler::PrintTestList() cmWorkingDirectory workdir(p.Directory); - cmCTestRunTest testRun(this->TestHandler); + cmCTestRunTest testRun(*this); testRun.SetIndex(p.Index); testRun.SetTestProperties(&p); testRun.ComputeArguments(); // logs the command in verbose mode diff --git a/Source/CTest/cmCTestMultiProcessHandler.h b/Source/CTest/cmCTestMultiProcessHandler.h index dccc2c8..7837ff9 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.h +++ b/Source/CTest/cmCTestMultiProcessHandler.h @@ -12,6 +12,8 @@ #include <string> #include <vector> +#include "cm_uv.h" + class cmCTest; class cmCTestRunTest; @@ -23,6 +25,7 @@ class cmCTestRunTest; class cmCTestMultiProcessHandler { friend class TestComparator; + friend class cmCTestRunTest; public: struct TestSet : public std::set<int> @@ -75,7 +78,7 @@ protected: // Start the next test or tests as many as are allowed by // ParallelLevel void StartNextTests(); - void StartTestProcess(int test); + bool StartTestProcess(int test); bool StartTest(int test); // Mark the checkpoint for the given test void WriteCheckpoint(int index); @@ -95,9 +98,8 @@ protected: // Removes the checkpoint file void MarkFinished(); void EraseTest(int index); - // Return true if there are still tests running - // check all running processes for output and exit case - bool CheckOutput(); + void FinishTestProcess(cmCTestRunTest* runner, bool started); + void RemoveTest(int index); // Check if we need to resume an interrupted test set void CheckResume(); @@ -130,7 +132,7 @@ protected: std::vector<cmCTestTestHandler::cmCTestTestResult>* TestResults; size_t ParallelLevel; // max number of process that can be run at once unsigned long TestLoad; - std::set<cmCTestRunTest*> RunningTests; // current running tests + uv_loop_t Loop; cmCTestTestHandler* TestHandler; cmCTest* CTest; bool HasCycles; diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index dbdefae..baf894e 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -4,28 +4,27 @@ #include "cmCTest.h" #include "cmCTestMemCheckHandler.h" -#include "cmCTestTestHandler.h" +#include "cmCTestMultiProcessHandler.h" #include "cmProcess.h" #include "cmSystemTools.h" #include "cmWorkingDirectory.h" -#include "cm_curl.h" #include "cm_zlib.h" #include "cmsys/Base64.h" -#include "cmsys/Process.h" #include "cmsys/RegularExpression.hxx" #include <chrono> +#include <cmAlgorithms.h> #include <iomanip> +#include <ratio> #include <sstream> #include <stdio.h> -#include <time.h> #include <utility> -cmCTestRunTest::cmCTestRunTest(cmCTestTestHandler* handler) +cmCTestRunTest::cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler) + : MultiTestHandler(multiHandler) { - this->CTest = handler->CTest; - this->TestHandler = handler; - this->TestProcess = nullptr; + this->CTest = multiHandler.CTest; + this->TestHandler = multiHandler.TestHandler; this->TestResult.ExecutionTime = std::chrono::duration<double>::zero(); this->TestResult.ReturnValue = 0; this->TestResult.Status = cmCTestTestHandler::NOT_RUN; @@ -34,60 +33,37 @@ cmCTestRunTest::cmCTestRunTest(cmCTestTestHandler* handler) this->ProcessOutput.clear(); this->CompressedOutput.clear(); this->CompressionRatio = 2; - this->StopTimePassed = false; this->NumberOfRunsLeft = 1; // default to 1 run of the test this->RunUntilFail = false; // default to run the test once this->RunAgain = false; // default to not having to run again } -cmCTestRunTest::~cmCTestRunTest() +void cmCTestRunTest::CheckOutput(std::string const& line) { -} - -bool cmCTestRunTest::CheckOutput() -{ - // Read lines for up to 0.1 seconds of total time. - std::chrono::duration<double> timeout = std::chrono::milliseconds(100); - auto timeEnd = std::chrono::steady_clock::now() + timeout; - std::string line; - while ((timeout = timeEnd - std::chrono::steady_clock::now(), - timeout > std::chrono::seconds(0))) { - int p = this->TestProcess->GetNextOutputLine(line, timeout); - if (p == cmsysProcess_Pipe_None) { - // Process has terminated and all output read. - return false; - } - if (p == cmsysProcess_Pipe_STDOUT) { - // Store this line of output. - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, this->GetIndex() - << ": " << line << std::endl); - this->ProcessOutput += line; - this->ProcessOutput += "\n"; - - // Check for TIMEOUT_AFTER_MATCH property. - if (!this->TestProperties->TimeoutRegularExpressions.empty()) { - for (auto& reg : this->TestProperties->TimeoutRegularExpressions) { - if (reg.first.find(this->ProcessOutput.c_str())) { - cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, this->GetIndex() - << ": " - << "Test timeout changed to " - << std::chrono::duration_cast<std::chrono::seconds>( - this->TestProperties->AlternateTimeout) - .count() - << std::endl); - this->TestProcess->ResetStartTime(); - this->TestProcess->ChangeTimeout( - this->TestProperties->AlternateTimeout); - this->TestProperties->TimeoutRegularExpressions.clear(); - break; - } - } + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, this->GetIndex() + << ": " << line << std::endl); + this->ProcessOutput += line; + this->ProcessOutput += "\n"; + + // Check for TIMEOUT_AFTER_MATCH property. + if (!this->TestProperties->TimeoutRegularExpressions.empty()) { + for (auto& reg : this->TestProperties->TimeoutRegularExpressions) { + if (reg.first.find(this->ProcessOutput.c_str())) { + cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, this->GetIndex() + << ": " + << "Test timeout changed to " + << std::chrono::duration_cast<std::chrono::seconds>( + this->TestProperties->AlternateTimeout) + .count() + << std::endl); + this->TestProcess->ResetStartTime(); + this->TestProcess->ChangeTimeout( + this->TestProperties->AlternateTimeout); + this->TestProperties->TimeoutRegularExpressions.clear(); + break; } - } else { // if(p == cmsysProcess_Pipe_Timeout) - break; } } - return true; } // Streamed compression of test output. The compressed data @@ -160,8 +136,8 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) this->WriteLogOutputTop(completed, total); std::string reason; bool passed = true; - int res = - started ? this->TestProcess->GetProcessStatus() : cmsysProcess_State_Error; + cmProcess::State res = + started ? this->TestProcess->GetProcessStatus() : cmProcess::State::Error; int retVal = this->TestProcess->GetExitValue(); bool forceFail = false; bool skipped = false; @@ -200,7 +176,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) } } } - if (res == cmsysProcess_State_Exited) { + if (res == cmProcess::State::Exited) { bool success = !forceFail && (retVal == 0 || !this->TestProperties->RequiredRegularExpressions.empty()); @@ -221,29 +197,29 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Failed " << reason); outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure; } - } else if (res == cmsysProcess_State_Expired) { + } else if (res == cmProcess::State::Expired) { cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Timeout "); this->TestResult.Status = cmCTestTestHandler::TIMEOUT; outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure; - } else if (res == cmsysProcess_State_Exception) { + } else if (res == cmProcess::State::Exception) { outputTestErrorsToConsole = this->CTest->OutputTestOutputOnTestFailure; cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Exception: "); this->TestResult.ExceptionStatus = this->TestProcess->GetExitExceptionString(); switch (this->TestProcess->GetExitException()) { - case cmsysProcess_Exception_Fault: + case cmProcess::Exception::Fault: cmCTestLog(this->CTest, HANDLER_OUTPUT, "SegFault"); this->TestResult.Status = cmCTestTestHandler::SEGFAULT; break; - case cmsysProcess_Exception_Illegal: + case cmProcess::Exception::Illegal: cmCTestLog(this->CTest, HANDLER_OUTPUT, "Illegal"); this->TestResult.Status = cmCTestTestHandler::ILLEGAL; break; - case cmsysProcess_Exception_Interrupt: + case cmProcess::Exception::Interrupt: cmCTestLog(this->CTest, HANDLER_OUTPUT, "Interrupt"); this->TestResult.Status = cmCTestTestHandler::INTERRUPT; break; - case cmsysProcess_Exception_Numerical: + case cmProcess::Exception::Numerical: cmCTestLog(this->CTest, HANDLER_OUTPUT, "Numerical"); this->TestResult.Status = cmCTestTestHandler::NUMERICAL; break; @@ -254,7 +230,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) } } else if ("Disabled" == this->TestResult.CompletionStatus) { cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Not Run (Disabled) "); - } else // cmsysProcess_State_Error + } else // cmProcess::State::Error { cmCTestLog(this->CTest, HANDLER_OUTPUT, "***Not Run "); } @@ -350,7 +326,7 @@ bool cmCTestRunTest::EndTest(size_t completed, size_t total, bool started) if (!this->NeedsToRerun()) { this->TestHandler->TestResults.push_back(this->TestResult); } - delete this->TestProcess; + this->TestProcess.reset(); return passed || skipped; } @@ -432,7 +408,7 @@ bool cmCTestRunTest::StartTest(size_t total) this->TestResult.TestCount = this->TestProperties->Index; this->TestResult.Name = this->TestProperties->Name; this->TestResult.Path = this->TestProperties->Directory; - this->TestProcess = new cmProcess; + this->TestProcess = cm::make_unique<cmProcess>(*this); this->TestResult.Output = "Disabled"; this->TestResult.FullCommandLine.clear(); return false; @@ -453,7 +429,7 @@ bool cmCTestRunTest::StartTest(size_t total) // its arguments are irrelevant. This matters for the case where a fixture // dependency might be creating the executable we want to run. if (!this->FailedDependencies.empty()) { - this->TestProcess = new cmProcess; + this->TestProcess = cm::make_unique<cmProcess>(*this); std::string msg = "Failed test dependencies:"; for (std::string const& failedDep : this->FailedDependencies) { msg += " " + failedDep; @@ -470,7 +446,7 @@ bool cmCTestRunTest::StartTest(size_t total) this->ComputeArguments(); std::vector<std::string>& args = this->TestProperties->Args; if (args.size() >= 2 && args[1] == "NOT_AVAILABLE") { - this->TestProcess = new cmProcess; + this->TestProcess = cm::make_unique<cmProcess>(*this); std::string msg; if (this->CTest->GetConfigType().empty()) { msg = "Test not available without configuration."; @@ -493,7 +469,7 @@ bool cmCTestRunTest::StartTest(size_t total) for (std::string const& file : this->TestProperties->RequiredFiles) { if (!cmSystemTools::FileExists(file.c_str())) { // Required file was not found - this->TestProcess = new cmProcess; + this->TestProcess = cm::make_unique<cmProcess>(*this); *this->TestHandler->LogFile << "Unable to find required file: " << file << std::endl; cmCTestLog(this->CTest, ERROR_MESSAGE, @@ -509,7 +485,7 @@ bool cmCTestRunTest::StartTest(size_t total) if (this->ActualCommand.empty()) { // if the command was not found create a TestResult object // that has that information - this->TestProcess = new cmProcess; + this->TestProcess = cm::make_unique<cmProcess>(*this); *this->TestHandler->LogFile << "Unable to find executable: " << args[1] << std::endl; cmCTestLog(this->CTest, ERROR_MESSAGE, @@ -522,11 +498,22 @@ bool cmCTestRunTest::StartTest(size_t total) } this->StartTime = this->CTest->CurrentTime(); - auto timeout = this->ResolveTimeout(); + auto timeout = this->TestProperties->Timeout; - if (this->StopTimePassed) { - return false; + std::chrono::system_clock::time_point stop_time = this->CTest->GetStopTime(); + if (stop_time != std::chrono::system_clock::time_point()) { + std::chrono::duration<double> stop_timeout = + (stop_time - std::chrono::system_clock::now()) % std::chrono::hours(24); + + if (stop_timeout <= std::chrono::duration<double>::zero()) { + stop_timeout = std::chrono::duration<double>::zero(); + } + if (timeout == std::chrono::duration<double>::zero() || + stop_timeout < timeout) { + timeout = stop_timeout; + } } + return this->ForkProcess(timeout, this->TestProperties->ExplicitTimeout, &this->TestProperties->Environment); } @@ -603,72 +590,11 @@ void cmCTestRunTest::DartProcessing() } } -std::chrono::duration<double> cmCTestRunTest::ResolveTimeout() -{ - auto timeout = this->TestProperties->Timeout; - - if (this->CTest->GetStopTime().empty()) { - return timeout; - } - struct tm* lctime; - time_t current_time = time(nullptr); - lctime = gmtime(¤t_time); - int gm_hour = lctime->tm_hour; - time_t gm_time = mktime(lctime); - lctime = localtime(¤t_time); - int local_hour = lctime->tm_hour; - - int tzone_offset = local_hour - gm_hour; - if (gm_time > current_time && gm_hour < local_hour) { - // this means gm_time is on the next day - tzone_offset -= 24; - } else if (gm_time < current_time && gm_hour > local_hour) { - // this means gm_time is on the previous day - tzone_offset += 24; - } - - tzone_offset *= 100; - char buf[1024]; - // add todays year day and month to the time in str because - // curl_getdate no longer assumes the day is today - sprintf(buf, "%d%02d%02d %s %+05i", lctime->tm_year + 1900, - lctime->tm_mon + 1, lctime->tm_mday, - this->CTest->GetStopTime().c_str(), tzone_offset); - - time_t stop_time_t = curl_getdate(buf, ¤t_time); - if (stop_time_t == -1) { - return timeout; - } - - auto stop_time = std::chrono::system_clock::from_time_t(stop_time_t); - - // the stop time refers to the next day - if (this->CTest->NextDayStopTime) { - stop_time += std::chrono::hours(24); - } - auto stop_timeout = - (stop_time - std::chrono::system_clock::from_time_t(current_time)) % - std::chrono::hours(24); - this->CTest->LastStopTimeout = stop_timeout; - - if (stop_timeout <= std::chrono::duration<double>::zero() || - stop_timeout > this->CTest->LastStopTimeout) { - cmCTestLog(this->CTest, ERROR_MESSAGE, "The stop time has been passed. " - "Stopping all tests." - << std::endl); - this->StopTimePassed = true; - return std::chrono::duration<double>::zero(); - } - return timeout == std::chrono::duration<double>::zero() - ? stop_timeout - : (timeout < stop_timeout ? timeout : stop_timeout); -} - bool cmCTestRunTest::ForkProcess(std::chrono::duration<double> testTimeOut, bool explicitTimeout, std::vector<std::string>* environment) { - this->TestProcess = new cmProcess; + this->TestProcess = cm::make_unique<cmProcess>(*this); this->TestProcess->SetId(this->Index); this->TestProcess->SetWorkingDirectory( this->TestProperties->Directory.c_str()); @@ -720,7 +646,7 @@ bool cmCTestRunTest::ForkProcess(std::chrono::duration<double> testTimeOut, cmSystemTools::AppendEnv(*environment); } - return this->TestProcess->StartProcess(); + return this->TestProcess->StartProcess(this->MultiTestHandler.Loop); } void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total) @@ -794,3 +720,8 @@ void cmCTestRunTest::WriteLogOutputTop(size_t completed, size_t total) cmCTestLog(this->CTest, DEBUG, "Testing " << this->TestProperties->Name << " ... "); } + +void cmCTestRunTest::FinalizeTest() +{ + this->MultiTestHandler.FinishTestProcess(this, true); +} diff --git a/Source/CTest/cmCTestRunTest.h b/Source/CTest/cmCTestRunTest.h index cd380ca..fbc202f 100644 --- a/Source/CTest/cmCTestRunTest.h +++ b/Source/CTest/cmCTestRunTest.h @@ -12,9 +12,10 @@ #include <vector> #include "cmCTestTestHandler.h" +#include "cmProcess.h" // IWYU pragma: keep (for unique_ptr) class cmCTest; -class cmProcess; +class cmCTestMultiProcessHandler; /** \class cmRunTest * \brief represents a single test to be run @@ -24,8 +25,9 @@ class cmProcess; class cmCTestRunTest { public: - cmCTestRunTest(cmCTestTestHandler* handler); - ~cmCTestRunTest(); + explicit cmCTestRunTest(cmCTestMultiProcessHandler& multiHandler); + + ~cmCTestRunTest() = default; void SetNumberOfRuns(int n) { this->NumberOfRunsLeft = n; } void SetRunUntilFailOn() { this->RunUntilFail = true; } @@ -50,15 +52,13 @@ public: std::string GetProcessOutput() { return this->ProcessOutput; } - bool IsStopTimePassed() { return this->StopTimePassed; } - cmCTestTestHandler::cmCTestTestResult GetTestResults() { return this->TestResult; } // Read and store output. Returns true if it must be called again. - bool CheckOutput(); + void CheckOutput(std::string const& line); // Compresses the output, writing to CompressedOutput void CompressOutput(); @@ -74,12 +74,14 @@ public: bool StartAgain(); + cmCTest* GetCTest() const { return this->CTest; } + + void FinalizeTest(); + private: bool NeedsToRerun(); void DartProcessing(); void ExeNotFound(std::string exe); - // Figures out a final timeout which is min(STOP_TIME, NOW+TIMEOUT) - std::chrono::duration<double> ResolveTimeout(); bool ForkProcess(std::chrono::duration<double> testTimeOut, bool explicitTimeout, std::vector<std::string>* environment); @@ -91,26 +93,18 @@ private: // Pointer back to the "parent"; the handler that invoked this test run cmCTestTestHandler* TestHandler; cmCTest* CTest; - cmProcess* TestProcess; - // If the executable to run is ctest, don't create a new process; - // just instantiate a new cmTest. (Can be disabled for a single test - // if this option is set to false.) - // bool OptimizeForCTest; - - bool UsePrefixCommand; - std::string PrefixCommand; - + std::unique_ptr<cmProcess> TestProcess; std::string ProcessOutput; std::string CompressedOutput; double CompressionRatio; // The test results cmCTestTestHandler::cmCTestTestResult TestResult; + cmCTestMultiProcessHandler& MultiTestHandler; int Index; std::set<std::string> FailedDependencies; std::string StartTime; std::string ActualCommand; std::vector<std::string> Arguments; - bool StopTimePassed; bool RunUntilFail; int NumberOfRunsLeft; bool RunAgain; diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx index 0db66c3..e332a77 100644 --- a/Source/CTest/cmProcess.cxx +++ b/Source/CTest/cmProcess.cxx @@ -2,12 +2,66 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmProcess.h" -#include "cmProcessOutput.h" +#include "cmCTest.h" +#include "cmCTestRunTest.h" +#include "cmCTestTestHandler.h" +#include "cmsys/Process.h" + +#include <algorithm> +#include <fcntl.h> #include <iostream> +#include <signal.h> +#include <string> +#if !defined(_WIN32) +#include <unistd.h> +#endif + +#define CM_PROCESS_BUF_SIZE 65536 + +#if defined(_WIN32) && !defined(__CYGWIN__) +#include <io.h> + +static int cmProcessGetPipes(int* fds) +{ + SECURITY_ATTRIBUTES attr; + HANDLE readh, writeh; + attr.nLength = sizeof(attr); + attr.lpSecurityDescriptor = nullptr; + attr.bInheritHandle = FALSE; + if (!CreatePipe(&readh, &writeh, &attr, 0)) + return uv_translate_sys_error(GetLastError()); + fds[0] = _open_osfhandle((intptr_t)readh, 0); + fds[1] = _open_osfhandle((intptr_t)writeh, 0); + if (fds[0] == -1 || fds[1] == -1) { + CloseHandle(readh); + CloseHandle(writeh); + return uv_translate_sys_error(GetLastError()); + } + return 0; +} +#else +#include <errno.h> -cmProcess::cmProcess() +static int cmProcessGetPipes(int* fds) +{ + if (pipe(fds) == -1) { + return uv_translate_sys_error(errno); + } + + if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 || + fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) { + close(fds[0]); + close(fds[1]); + return uv_translate_sys_error(errno); + } + return 0; +} +#endif + +cmProcess::cmProcess(cmCTestRunTest& runner) + : Runner(runner) + , Conv(cmProcessOutput::UTF8, CM_PROCESS_BUF_SIZE) { - this->Process = nullptr; this->Timeout = std::chrono::duration<double>::zero(); this->TotalTime = std::chrono::duration<double>::zero(); this->ExitValue = 0; @@ -17,8 +71,8 @@ cmProcess::cmProcess() cmProcess::~cmProcess() { - cmsysProcess_Delete(this->Process); } + void cmProcess::SetCommand(const char* command) { this->Command = command; @@ -29,8 +83,9 @@ void cmProcess::SetCommandArguments(std::vector<std::string> const& args) this->Arguments = args; } -bool cmProcess::StartProcess() +bool cmProcess::StartProcess(uv_loop_t& loop) { + this->ProcessState = cmProcess::State::Error; if (this->Command.empty()) { return false; } @@ -43,17 +98,83 @@ bool cmProcess::StartProcess() this->ProcessArgs.push_back(arg.c_str()); } this->ProcessArgs.push_back(nullptr); // null terminate the list - this->Process = cmsysProcess_New(); - cmsysProcess_SetCommand(this->Process, &*this->ProcessArgs.begin()); - if (!this->WorkingDirectory.empty()) { - cmsysProcess_SetWorkingDirectory(this->Process, - this->WorkingDirectory.c_str()); + + cm::uv_timer_ptr timer; + int status = timer.init(loop, this); + if (status != 0) { + cmCTestLog(this->Runner.GetCTest(), ERROR_MESSAGE, + "Error initializing timer: " << uv_strerror(status) + << std::endl); + return false; + } + + cm::uv_pipe_ptr pipe_writer; + cm::uv_pipe_ptr pipe_reader; + + pipe_writer.init(loop, 0); + pipe_reader.init(loop, 0, this); + + int fds[2] = { -1, -1 }; + status = cmProcessGetPipes(fds); + if (status != 0) { + cmCTestLog(this->Runner.GetCTest(), ERROR_MESSAGE, + "Error initializing pipe: " << uv_strerror(status) + << std::endl); + return false; + } + + uv_pipe_open(pipe_reader, fds[0]); + uv_pipe_open(pipe_writer, fds[1]); + + uv_stdio_container_t stdio[3]; + stdio[0].flags = UV_IGNORE; + stdio[1].flags = UV_INHERIT_STREAM; + stdio[1].data.stream = pipe_writer; + stdio[2] = stdio[1]; + + uv_process_options_t options = uv_process_options_t(); + options.file = this->Command.data(); + options.args = const_cast<char**>(this->ProcessArgs.data()); + options.stdio_count = 3; // in, out and err + options.exit_cb = &cmProcess::OnExitCB; + options.stdio = stdio; + + status = + uv_read_start(pipe_reader, &cmProcess::OnAllocateCB, &cmProcess::OnReadCB); + + if (status != 0) { + cmCTestLog(this->Runner.GetCTest(), ERROR_MESSAGE, + "Error starting read events: " << uv_strerror(status) + << std::endl); + return false; + } + + status = this->Process.spawn(loop, options, this); + if (status != 0) { + cmCTestLog(this->Runner.GetCTest(), ERROR_MESSAGE, "Process not started\n " + << this->Command << "\n[" << uv_strerror(status) << "]\n"); + return false; + } + + this->PipeReader = std::move(pipe_reader); + this->Timer = std::move(timer); + + this->StartTimer(); + + this->ProcessState = cmProcess::State::Executing; + return true; +} + +void cmProcess::StartTimer() +{ + auto properties = this->Runner.GetTestProperties(); + auto msec = + std::chrono::duration_cast<std::chrono::milliseconds>(this->Timeout); + + if (msec != std::chrono::milliseconds(0) || !properties->ExplicitTimeout) { + this->Timer.start(&cmProcess::OnTimeoutCB, + static_cast<uint64_t>(msec.count()), 0); } - cmsysProcess_SetTimeout(this->Process, this->Timeout.count()); - cmsysProcess_SetOption(this->Process, cmsysProcess_Option_MergeOutput, 1); - cmsysProcess_Execute(this->Process); - return (cmsysProcess_GetState(this->Process) == - cmsysProcess_State_Executing); } bool cmProcess::Buffer::GetLine(std::string& line) @@ -100,51 +221,124 @@ bool cmProcess::Buffer::GetLast(std::string& line) return false; } -int cmProcess::GetNextOutputLine(std::string& line, - std::chrono::duration<double> timeout) +void cmProcess::OnReadCB(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf) { - cmProcessOutput processOutput(cmProcessOutput::UTF8); - std::string strdata; - double waitTimeout = timeout.count(); - for (;;) { - // Look for lines already buffered. - if (this->Output.GetLine(line)) { - return cmsysProcess_Pipe_STDOUT; - } + auto self = static_cast<cmProcess*>(stream->data); + self->OnRead(nread, buf); +} - // Check for more data from the process. - char* data; - int length; - int p = - cmsysProcess_WaitForData(this->Process, &data, &length, &waitTimeout); - if (p == cmsysProcess_Pipe_Timeout) { - return cmsysProcess_Pipe_Timeout; - } - if (p == cmsysProcess_Pipe_STDOUT) { - processOutput.DecodeText(data, length, strdata); - this->Output.insert(this->Output.end(), strdata.begin(), strdata.end()); - } else { // p == cmsysProcess_Pipe_None - // The process will provide no more data. - break; +void cmProcess::OnRead(ssize_t nread, const uv_buf_t* buf) +{ + std::string line; + if (nread > 0) { + std::string strdata; + this->Conv.DecodeText(buf->base, static_cast<size_t>(nread), strdata); + this->Output.insert(this->Output.end(), strdata.begin(), strdata.end()); + + while (this->Output.GetLine(line)) { + this->Runner.CheckOutput(line); + line.clear(); } + + return; } - processOutput.DecodeText(std::string(), strdata); - if (!strdata.empty()) { - this->Output.insert(this->Output.end(), strdata.begin(), strdata.end()); + + if (nread == 0) { + return; + } + + // The process will provide no more data. + if (nread != UV_EOF) { + auto error = static_cast<int>(nread); + cmCTestLog(this->Runner.GetCTest(), ERROR_MESSAGE, + "Error reading stream: " << uv_strerror(error) << std::endl); } // Look for partial last lines. if (this->Output.GetLast(line)) { - return cmsysProcess_Pipe_STDOUT; + this->Runner.CheckOutput(line); } - // No more data. Wait for process exit. - if (!cmsysProcess_WaitForExit(this->Process, &waitTimeout)) { - return cmsysProcess_Pipe_Timeout; + this->ReadHandleClosed = true; + this->PipeReader.reset(); + if (this->ProcessHandleClosed) { + uv_timer_stop(this->Timer); + this->Runner.FinalizeTest(); + } +} + +void cmProcess::OnAllocateCB(uv_handle_t* handle, size_t suggested_size, + uv_buf_t* buf) +{ + auto self = static_cast<cmProcess*>(handle->data); + self->OnAllocate(suggested_size, buf); +} + +void cmProcess::OnAllocate(size_t /*suggested_size*/, uv_buf_t* buf) +{ + if (this->Buf.size() != CM_PROCESS_BUF_SIZE) { + this->Buf.resize(CM_PROCESS_BUF_SIZE); + } + + *buf = + uv_buf_init(this->Buf.data(), static_cast<unsigned int>(this->Buf.size())); +} + +void cmProcess::OnTimeoutCB(uv_timer_t* timer) +{ + auto self = static_cast<cmProcess*>(timer->data); + self->OnTimeout(); +} + +void cmProcess::OnTimeout() +{ + if (this->ProcessState != cmProcess::State::Executing) { + return; + } + this->ProcessState = cmProcess::State::Expired; + bool const was_still_reading = !this->ReadHandleClosed; + if (!this->ReadHandleClosed) { + this->ReadHandleClosed = true; + this->PipeReader.reset(); + } + if (!this->ProcessHandleClosed) { + // Kill the child and let our on-exit handler finish the test. + cmsysProcess_KillPID(static_cast<unsigned long>(this->Process->pid)); + } else if (was_still_reading) { + // Our on-exit handler already ran but did not finish the test + // because we were still reading output. We've just dropped + // our read handler, so we need to finish the test now. + this->Runner.FinalizeTest(); + } +} + +void cmProcess::OnExitCB(uv_process_t* process, int64_t exit_status, + int term_signal) +{ + auto self = static_cast<cmProcess*>(process->data); + self->OnExit(exit_status, term_signal); +} + +void cmProcess::OnExit(int64_t exit_status, int term_signal) +{ + if (this->ProcessState != cmProcess::State::Expired) { + if ( +#if defined(_WIN32) + ((DWORD)exit_status & 0xF0000000) == 0xC0000000 +#else + term_signal != 0 +#endif + ) { + this->ProcessState = cmProcess::State::Exception; + } else { + this->ProcessState = cmProcess::State::Exited; + } } // Record exit information. - this->ExitValue = cmsysProcess_GetExitValue(this->Process); + this->ExitValue = static_cast<int>(exit_status); + this->Signal = term_signal; this->TotalTime = std::chrono::steady_clock::now() - this->StartTime; // Because of a processor clock scew the runtime may become slightly // negative. If someone changed the system clock while the process was @@ -153,95 +347,373 @@ int cmProcess::GetNextOutputLine(std::string& line, if (this->TotalTime <= std::chrono::duration<double>::zero()) { this->TotalTime = std::chrono::duration<double>::zero(); } - // std::cerr << "Time to run: " << this->TotalTime << "\n"; - return cmsysProcess_Pipe_None; -} - -// return the process status -int cmProcess::GetProcessStatus() -{ - if (!this->Process) { - return cmsysProcess_State_Exited; - } - return cmsysProcess_GetState(this->Process); -} - -int cmProcess::ReportStatus() -{ - int result = 1; - switch (cmsysProcess_GetState(this->Process)) { - case cmsysProcess_State_Starting: { - std::cerr << "cmProcess: Never started " << this->Command - << " process.\n"; - } break; - case cmsysProcess_State_Error: { - std::cerr << "cmProcess: Error executing " << this->Command - << " process: " << cmsysProcess_GetErrorString(this->Process) - << "\n"; - } break; - case cmsysProcess_State_Exception: { - std::cerr << "cmProcess: " << this->Command - << " process exited with an exception: "; - switch (cmsysProcess_GetExitException(this->Process)) { - case cmsysProcess_Exception_None: { - std::cerr << "None"; - } break; - case cmsysProcess_Exception_Fault: { - std::cerr << "Segmentation fault"; - } break; - case cmsysProcess_Exception_Illegal: { - std::cerr << "Illegal instruction"; - } break; - case cmsysProcess_Exception_Interrupt: { - std::cerr << "Interrupted by user"; - } break; - case cmsysProcess_Exception_Numerical: { - std::cerr << "Numerical exception"; - } break; - case cmsysProcess_Exception_Other: { - std::cerr << "Unknown"; - } break; - } - std::cerr << "\n"; - } break; - case cmsysProcess_State_Executing: { - std::cerr << "cmProcess: Never terminated " << this->Command - << " process.\n"; - } break; - case cmsysProcess_State_Exited: { - result = cmsysProcess_GetExitValue(this->Process); - std::cerr << "cmProcess: " << this->Command - << " process exited with code " << result << "\n"; - } break; - case cmsysProcess_State_Expired: { - std::cerr << "cmProcess: killed " << this->Command - << " process due to timeout.\n"; - } break; - case cmsysProcess_State_Killed: { - std::cerr << "cmProcess: killed " << this->Command << " process.\n"; - } break; - } - return result; + + this->ProcessHandleClosed = true; + if (this->ReadHandleClosed) { + uv_timer_stop(this->Timer); + this->Runner.FinalizeTest(); + } +} + +cmProcess::State cmProcess::GetProcessStatus() +{ + return this->ProcessState; } void cmProcess::ChangeTimeout(std::chrono::duration<double> t) { this->Timeout = t; - cmsysProcess_SetTimeout(this->Process, this->Timeout.count()); + this->StartTimer(); } void cmProcess::ResetStartTime() { - cmsysProcess_ResetStartTime(this->Process); this->StartTime = std::chrono::steady_clock::now(); } -int cmProcess::GetExitException() +cmProcess::Exception cmProcess::GetExitException() { - return cmsysProcess_GetExitException(this->Process); + auto exception = Exception::None; +#if defined(_WIN32) && !defined(__CYGWIN__) + auto exit_code = (DWORD) this->ExitValue; + if ((exit_code & 0xF0000000) != 0xC0000000) { + return exception; + } + + if (exit_code) { + switch (exit_code) { + case STATUS_DATATYPE_MISALIGNMENT: + case STATUS_ACCESS_VIOLATION: + case STATUS_IN_PAGE_ERROR: + case STATUS_INVALID_HANDLE: + case STATUS_NONCONTINUABLE_EXCEPTION: + case STATUS_INVALID_DISPOSITION: + case STATUS_ARRAY_BOUNDS_EXCEEDED: + case STATUS_STACK_OVERFLOW: + exception = Exception::Fault; + break; + case STATUS_FLOAT_DENORMAL_OPERAND: + case STATUS_FLOAT_DIVIDE_BY_ZERO: + case STATUS_FLOAT_INEXACT_RESULT: + case STATUS_FLOAT_INVALID_OPERATION: + case STATUS_FLOAT_OVERFLOW: + case STATUS_FLOAT_STACK_CHECK: + case STATUS_FLOAT_UNDERFLOW: +#ifdef STATUS_FLOAT_MULTIPLE_FAULTS + case STATUS_FLOAT_MULTIPLE_FAULTS: +#endif +#ifdef STATUS_FLOAT_MULTIPLE_TRAPS + case STATUS_FLOAT_MULTIPLE_TRAPS: +#endif + case STATUS_INTEGER_DIVIDE_BY_ZERO: + case STATUS_INTEGER_OVERFLOW: + exception = Exception::Numerical; + break; + case STATUS_CONTROL_C_EXIT: + exception = Exception::Interrupt; + break; + case STATUS_ILLEGAL_INSTRUCTION: + case STATUS_PRIVILEGED_INSTRUCTION: + exception = Exception::Illegal; + break; + default: + exception = Exception::Other; + } + } +#else + if (this->Signal) { + switch (this->Signal) { + case SIGSEGV: + exception = Exception::Fault; + break; + case SIGFPE: + exception = Exception::Numerical; + break; + case SIGINT: + exception = Exception::Interrupt; + break; + case SIGILL: + exception = Exception::Illegal; + break; + default: + exception = Exception::Other; + } + } +#endif + return exception; } std::string cmProcess::GetExitExceptionString() { - return cmsysProcess_GetExceptionString(this->Process); + std::string exception_str; +#if defined(_WIN32) + switch (this->ExitValue) { + case STATUS_CONTROL_C_EXIT: + exception_str = "User interrupt"; + break; + case STATUS_FLOAT_DENORMAL_OPERAND: + exception_str = "Floating-point exception (denormal operand)"; + break; + case STATUS_FLOAT_DIVIDE_BY_ZERO: + exception_str = "Divide-by-zero"; + break; + case STATUS_FLOAT_INEXACT_RESULT: + exception_str = "Floating-point exception (inexact result)"; + break; + case STATUS_FLOAT_INVALID_OPERATION: + exception_str = "Invalid floating-point operation"; + break; + case STATUS_FLOAT_OVERFLOW: + exception_str = "Floating-point overflow"; + break; + case STATUS_FLOAT_STACK_CHECK: + exception_str = "Floating-point stack check failed"; + break; + case STATUS_FLOAT_UNDERFLOW: + exception_str = "Floating-point underflow"; + break; +#ifdef STATUS_FLOAT_MULTIPLE_FAULTS + case STATUS_FLOAT_MULTIPLE_FAULTS: + exception_str = "Floating-point exception (multiple faults)"; + break; +#endif +#ifdef STATUS_FLOAT_MULTIPLE_TRAPS + case STATUS_FLOAT_MULTIPLE_TRAPS: + exception_str = "Floating-point exception (multiple traps)"; + break; +#endif + case STATUS_INTEGER_DIVIDE_BY_ZERO: + exception_str = "Integer divide-by-zero"; + break; + case STATUS_INTEGER_OVERFLOW: + exception_str = "Integer overflow"; + break; + + case STATUS_DATATYPE_MISALIGNMENT: + exception_str = "Datatype misalignment"; + break; + case STATUS_ACCESS_VIOLATION: + exception_str = "Access violation"; + break; + case STATUS_IN_PAGE_ERROR: + exception_str = "In-page error"; + break; + case STATUS_INVALID_HANDLE: + exception_str = "Invalid handle"; + break; + case STATUS_NONCONTINUABLE_EXCEPTION: + exception_str = "Noncontinuable exception"; + break; + case STATUS_INVALID_DISPOSITION: + exception_str = "Invalid disposition"; + break; + case STATUS_ARRAY_BOUNDS_EXCEEDED: + exception_str = "Array bounds exceeded"; + break; + case STATUS_STACK_OVERFLOW: + exception_str = "Stack overflow"; + break; + + case STATUS_ILLEGAL_INSTRUCTION: + exception_str = "Illegal instruction"; + break; + case STATUS_PRIVILEGED_INSTRUCTION: + exception_str = "Privileged instruction"; + break; + case STATUS_NO_MEMORY: + default: + char buf[1024]; + _snprintf(buf, 1024, "Exit code 0x%x\n", this->ExitValue); + exception_str.assign(buf); + } +#else + switch (this->Signal) { +#ifdef SIGSEGV + case SIGSEGV: + exception_str = "Segmentation fault"; + break; +#endif +#ifdef SIGBUS +#if !defined(SIGSEGV) || SIGBUS != SIGSEGV + case SIGBUS: + exception_str = "Bus error"; + break; +#endif +#endif +#ifdef SIGFPE + case SIGFPE: + exception_str = "Floating-point exception"; + break; +#endif +#ifdef SIGILL + case SIGILL: + exception_str = "Illegal instruction"; + break; +#endif +#ifdef SIGINT + case SIGINT: + exception_str = "User interrupt"; + break; +#endif +#ifdef SIGABRT + case SIGABRT: + exception_str = "Child aborted"; + break; +#endif +#ifdef SIGKILL + case SIGKILL: + exception_str = "Child killed"; + break; +#endif +#ifdef SIGTERM + case SIGTERM: + exception_str = "Child terminated"; + break; +#endif +#ifdef SIGHUP + case SIGHUP: + exception_str = "SIGHUP"; + break; +#endif +#ifdef SIGQUIT + case SIGQUIT: + exception_str = "SIGQUIT"; + break; +#endif +#ifdef SIGTRAP + case SIGTRAP: + exception_str = "SIGTRAP"; + break; +#endif +#ifdef SIGIOT +#if !defined(SIGABRT) || SIGIOT != SIGABRT + case SIGIOT: + exception_str = "SIGIOT"; + break; +#endif +#endif +#ifdef SIGUSR1 + case SIGUSR1: + exception_str = "SIGUSR1"; + break; +#endif +#ifdef SIGUSR2 + case SIGUSR2: + exception_str = "SIGUSR2"; + break; +#endif +#ifdef SIGPIPE + case SIGPIPE: + exception_str = "SIGPIPE"; + break; +#endif +#ifdef SIGALRM + case SIGALRM: + exception_str = "SIGALRM"; + break; +#endif +#ifdef SIGSTKFLT + case SIGSTKFLT: + exception_str = "SIGSTKFLT"; + break; +#endif +#ifdef SIGCHLD + case SIGCHLD: + exception_str = "SIGCHLD"; + break; +#elif defined(SIGCLD) + case SIGCLD: + exception_str = "SIGCLD"; + break; +#endif +#ifdef SIGCONT + case SIGCONT: + exception_str = "SIGCONT"; + break; +#endif +#ifdef SIGSTOP + case SIGSTOP: + exception_str = "SIGSTOP"; + break; +#endif +#ifdef SIGTSTP + case SIGTSTP: + exception_str = "SIGTSTP"; + break; +#endif +#ifdef SIGTTIN + case SIGTTIN: + exception_str = "SIGTTIN"; + break; +#endif +#ifdef SIGTTOU + case SIGTTOU: + exception_str = "SIGTTOU"; + break; +#endif +#ifdef SIGURG + case SIGURG: + exception_str = "SIGURG"; + break; +#endif +#ifdef SIGXCPU + case SIGXCPU: + exception_str = "SIGXCPU"; + break; +#endif +#ifdef SIGXFSZ + case SIGXFSZ: + exception_str = "SIGXFSZ"; + break; +#endif +#ifdef SIGVTALRM + case SIGVTALRM: + exception_str = "SIGVTALRM"; + break; +#endif +#ifdef SIGPROF + case SIGPROF: + exception_str = "SIGPROF"; + break; +#endif +#ifdef SIGWINCH + case SIGWINCH: + exception_str = "SIGWINCH"; + break; +#endif +#ifdef SIGPOLL + case SIGPOLL: + exception_str = "SIGPOLL"; + break; +#endif +#ifdef SIGIO +#if !defined(SIGPOLL) || SIGIO != SIGPOLL + case SIGIO: + exception_str = "SIGIO"; + break; +#endif +#endif +#ifdef SIGPWR + case SIGPWR: + exception_str = "SIGPWR"; + break; +#endif +#ifdef SIGSYS + case SIGSYS: + exception_str = "SIGSYS"; + break; +#endif +#ifdef SIGUNUSED +#if !defined(SIGSYS) || SIGUNUSED != SIGSYS + case SIGUNUSED: + exception_str = "SIGUNUSED"; + break; +#endif +#endif + default: + exception_str = "Signal "; + exception_str += std::to_string(this->Signal); + } +#endif + return exception_str; } diff --git a/Source/CTest/cmProcess.h b/Source/CTest/cmProcess.h index f3b0bd7..633be24 100644 --- a/Source/CTest/cmProcess.h +++ b/Source/CTest/cmProcess.h @@ -5,11 +5,18 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include "cmsys/Process.h" +#include "cmProcessOutput.h" +#include "cmUVHandlePtr.h" +#include "cm_uv.h" + #include <chrono> +#include <stddef.h> +#include <stdint.h> #include <string> #include <vector> +class cmCTestRunTest; + /** \class cmProcess * \brief run a process with c++ * @@ -18,7 +25,7 @@ class cmProcess { public: - cmProcess(); + explicit cmProcess(cmCTestRunTest& runner); ~cmProcess(); const char* GetCommand() { return this->Command.c_str(); } void SetCommand(const char* command); @@ -28,33 +35,71 @@ public: void ChangeTimeout(std::chrono::duration<double> t); void ResetStartTime(); // Return true if the process starts - bool StartProcess(); + bool StartProcess(uv_loop_t& loop); + + enum class State + { + Starting, + Error, + Exception, + Executing, + Exited, + Expired, + Killed, + Disowned + }; - // return the process status - int GetProcessStatus(); - // Report the status of the program - int ReportStatus(); + State GetProcessStatus(); int GetId() { return this->Id; } void SetId(int id) { this->Id = id; } int GetExitValue() { return this->ExitValue; } std::chrono::duration<double> GetTotalTime() { return this->TotalTime; } - int GetExitException(); + + enum class Exception + { + None, + Fault, + Illegal, + Interrupt, + Numerical, + Other + }; + + Exception GetExitException(); std::string GetExitExceptionString(); - /** - * Read one line of output but block for no more than timeout. - * Returns: - * cmsysProcess_Pipe_None = Process terminated and all output read - * cmsysProcess_Pipe_STDOUT = Line came from stdout or stderr - * cmsysProcess_Pipe_Timeout = Timeout expired while waiting - */ - int GetNextOutputLine(std::string& line, - std::chrono::duration<double> timeout); private: std::chrono::duration<double> Timeout; std::chrono::steady_clock::time_point StartTime; std::chrono::duration<double> TotalTime; - cmsysProcess* Process; + bool ReadHandleClosed = false; + bool ProcessHandleClosed = false; + + cm::uv_process_ptr Process; + cm::uv_pipe_ptr PipeReader; + cm::uv_timer_ptr Timer; + std::vector<char> Buf; + + cmCTestRunTest& Runner; + cmProcessOutput Conv; + int Signal = 0; + cmProcess::State ProcessState = cmProcess::State::Starting; + + static void OnExitCB(uv_process_t* process, int64_t exit_status, + int term_signal); + static void OnTimeoutCB(uv_timer_t* timer); + static void OnReadCB(uv_stream_t* stream, ssize_t nread, + const uv_buf_t* buf); + static void OnAllocateCB(uv_handle_t* handle, size_t suggested_size, + uv_buf_t* buf); + + void OnExit(int64_t exit_status, int term_signal); + void OnTimeout(); + void OnRead(ssize_t nread, const uv_buf_t* buf); + void OnAllocate(size_t suggested_size, uv_buf_t* buf); + + void StartTimer(); + class Buffer : public std::vector<char> { // Half-open index range of partial line already scanned. diff --git a/Source/CursesDialog/CMakeLists.txt b/Source/CursesDialog/CMakeLists.txt index 6023c83..c51b0dd 100644 --- a/Source/CursesDialog/CMakeLists.txt +++ b/Source/CursesDialog/CMakeLists.txt @@ -2,19 +2,19 @@ # file Copyright.txt or https://cmake.org/licensing for details. set( CURSES_SRCS - CursesDialog/cmCursesOptionsWidget - CursesDialog/cmCursesBoolWidget - CursesDialog/cmCursesCacheEntryComposite - CursesDialog/cmCursesDummyWidget - CursesDialog/cmCursesFilePathWidget - CursesDialog/cmCursesForm - CursesDialog/cmCursesLabelWidget - CursesDialog/cmCursesLongMessageForm - CursesDialog/cmCursesMainForm - CursesDialog/cmCursesPathWidget - CursesDialog/cmCursesStringWidget - CursesDialog/cmCursesWidget - CursesDialog/ccmake + CursesDialog/cmCursesOptionsWidget.cxx + CursesDialog/cmCursesBoolWidget.cxx + CursesDialog/cmCursesCacheEntryComposite.cxx + CursesDialog/cmCursesDummyWidget.cxx + CursesDialog/cmCursesFilePathWidget.cxx + CursesDialog/cmCursesForm.cxx + CursesDialog/cmCursesLabelWidget.cxx + CursesDialog/cmCursesLongMessageForm.cxx + CursesDialog/cmCursesMainForm.cxx + CursesDialog/cmCursesPathWidget.cxx + CursesDialog/cmCursesStringWidget.cxx + CursesDialog/cmCursesWidget.cxx + CursesDialog/ccmake.cxx ) include_directories(${CURSES_INCLUDE_PATH}) diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index 26e1dcb..fd7c5e8 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -279,11 +279,8 @@ cmCTest::cmCTest() this->InteractiveDebugMode = true; this->TimeOut = std::chrono::duration<double>::zero(); this->GlobalTimeout = std::chrono::duration<double>::zero(); - this->LastStopTimeout = std::chrono::hours(24); this->CompressXMLFiles = false; this->ScheduleType.clear(); - this->StopTime.clear(); - this->NextDayStopTime = false; this->OutputLogFile = nullptr; this->OutputLogFileLastTag = -1; this->SuppressUpdatingCTestConfiguration = false; @@ -2269,10 +2266,41 @@ void cmCTest::SetNotesFiles(const char* notes) this->NotesFiles = notes; } -void cmCTest::SetStopTime(std::string const& time) +void cmCTest::SetStopTime(std::string const& time_str) { - this->StopTime = time; - this->DetermineNextDayStop(); + + struct tm* lctime; + time_t current_time = time(nullptr); + lctime = gmtime(¤t_time); + int gm_hour = lctime->tm_hour; + time_t gm_time = mktime(lctime); + lctime = localtime(¤t_time); + int local_hour = lctime->tm_hour; + + int tzone_offset = local_hour - gm_hour; + if (gm_time > current_time && gm_hour < local_hour) { + // this means gm_time is on the next day + tzone_offset -= 24; + } else if (gm_time < current_time && gm_hour > local_hour) { + // this means gm_time is on the previous day + tzone_offset += 24; + } + + tzone_offset *= 100; + char buf[1024]; + sprintf(buf, "%d%02d%02d %s %+05i", lctime->tm_year + 1900, + lctime->tm_mon + 1, lctime->tm_mday, time_str.c_str(), tzone_offset); + + time_t stop_time = curl_getdate(buf, ¤t_time); + if (stop_time == -1) { + this->StopTime = std::chrono::system_clock::time_point(); + return; + } + this->StopTime = std::chrono::system_clock::from_time_t(stop_time); + + if (stop_time < current_time) { + this->StopTime += std::chrono::hours(24); + } } int cmCTest::ReadCustomConfigurationFileTree(const char* dir, cmMakefile* mf) @@ -2430,38 +2458,6 @@ void cmCTest::EmptyCTestConfiguration() this->CTestConfiguration.clear(); } -void cmCTest::DetermineNextDayStop() -{ - struct tm* lctime; - time_t current_time = time(nullptr); - lctime = gmtime(¤t_time); - int gm_hour = lctime->tm_hour; - time_t gm_time = mktime(lctime); - lctime = localtime(¤t_time); - int local_hour = lctime->tm_hour; - - int tzone_offset = local_hour - gm_hour; - if (gm_time > current_time && gm_hour < local_hour) { - // this means gm_time is on the next day - tzone_offset -= 24; - } else if (gm_time < current_time && gm_hour > local_hour) { - // this means gm_time is on the previous day - tzone_offset += 24; - } - - tzone_offset *= 100; - char buf[1024]; - sprintf(buf, "%d%02d%02d %s %+05i", lctime->tm_year + 1900, - lctime->tm_mon + 1, lctime->tm_mday, this->StopTime.c_str(), - tzone_offset); - - time_t stop_time = curl_getdate(buf, ¤t_time); - - if (stop_time < current_time) { - this->NextDayStopTime = true; - } -} - void cmCTest::SetCTestConfiguration(const char* name, const char* value, bool suppress) { diff --git a/Source/cmCTest.h b/Source/cmCTest.h index 23d71cb..61487f1 100644 --- a/Source/cmCTest.h +++ b/Source/cmCTest.h @@ -226,7 +226,10 @@ public: bool ShouldCompressTestOutput(); bool CompressString(std::string& str); - std::string GetStopTime() { return this->StopTime; } + std::chrono::system_clock::time_point GetStopTime() + { + return this->StopTime; + } void SetStopTime(std::string const& time); /** Used for parallel ctest job scheduling */ @@ -464,8 +467,7 @@ private: bool RepeatUntilFail; std::string ConfigType; std::string ScheduleType; - std::string StopTime; - bool NextDayStopTime; + std::chrono::system_clock::time_point StopTime; bool Verbose; bool ExtraVerbose; bool ProduceXML; @@ -481,8 +483,6 @@ private: int GenerateNotesFile(const char* files); - void DetermineNextDayStop(); - // these are helper classes typedef std::map<std::string, cmCTestGenericHandler*> t_TestingHandlers; t_TestingHandlers TestingHandlers; @@ -512,8 +512,6 @@ private: std::chrono::duration<double> GlobalTimeout; - std::chrono::duration<double> LastStopTimeout; - int MaxTestNameWidth; int ParallelLevel; diff --git a/Source/cmExtraCodeBlocksGenerator.cxx b/Source/cmExtraCodeBlocksGenerator.cxx index edce330..31c8bca 100644 --- a/Source/cmExtraCodeBlocksGenerator.cxx +++ b/Source/cmExtraCodeBlocksGenerator.cxx @@ -371,10 +371,10 @@ void cmExtraCodeBlocksGenerator::CreateNewProjectFile( continue; } - // check whether it is a C/C++ implementation file + // check whether it is a C/C++/CUDA implementation file bool isCFile = false; std::string lang = s->GetLanguage(); - if (lang == "C" || lang == "CXX") { + if (lang == "C" || lang == "CXX" || lang == "CUDA") { std::string const& srcext = s->GetExtension(); isCFile = cm->IsSourceExtension(srcext); } diff --git a/Source/cmExtraCodeLiteGenerator.cxx b/Source/cmExtraCodeLiteGenerator.cxx index 383942b..4958007 100644 --- a/Source/cmExtraCodeLiteGenerator.cxx +++ b/Source/cmExtraCodeLiteGenerator.cxx @@ -227,10 +227,10 @@ std::string cmExtraCodeLiteGenerator::CollectSourceFiles( gt->GetSourceFiles(sources, makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); for (cmSourceFile* s : sources) { - // check whether it is a C/C++ implementation file + // check whether it is a C/C++/CUDA implementation file bool isCFile = false; std::string lang = s->GetLanguage(); - if (lang == "C" || lang == "CXX") { + if (lang == "C" || lang == "CXX" || lang == "CUDA") { std::string const& srcext = s->GetExtension(); isCFile = cm->IsSourceExtension(srcext); } diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 3d311d6..34ef45f 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -805,7 +805,7 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode const std::vector<std::string>& parameters, cmGeneratorExpressionContext* context, const GeneratorExpressionContent* content, - cmGeneratorExpressionDAGChecker* dagChecker) const override + cmGeneratorExpressionDAGChecker* /*dagChecker*/) const override { if (context->Language.empty()) { reportError( @@ -827,33 +827,14 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode return std::string(); } std::string genName = gg->GetName(); - if (genName.find("Visual Studio") != std::string::npos) { - if (dagChecker && (dagChecker->EvaluatingCompileDefinitions() || - dagChecker->EvaluatingIncludeDirectories())) { - reportError( - context, content->GetOriginalExpression(), - "$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS " - "and file(GENERATE) with the Visual Studio generator."); - return std::string(); - } - } else if (genName.find("Xcode") != std::string::npos) { - if (dagChecker && (dagChecker->EvaluatingCompileDefinitions() || - dagChecker->EvaluatingIncludeDirectories())) { - reportError( - context, content->GetOriginalExpression(), - "$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS " - "and file(GENERATE) with the Xcode generator."); - return std::string(); - } - } else { - if (genName.find("Makefiles") == std::string::npos && - genName.find("Ninja") == std::string::npos && - genName.find("Watcom WMake") == std::string::npos) { - reportError( - context, content->GetOriginalExpression(), - "$<COMPILE_LANGUAGE:...> not supported for this generator."); - return std::string(); - } + if (genName.find("Makefiles") == std::string::npos && + genName.find("Ninja") == std::string::npos && + genName.find("Visual Studio") == std::string::npos && + genName.find("Xcode") == std::string::npos && + genName.find("Watcom WMake") == std::string::npos) { + reportError(context, content->GetOriginalExpression(), + "$<COMPILE_LANGUAGE:...> not supported for this generator."); + return std::string(); } if (parameters.empty()) { return context->Language; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index fd9b488..47a9390 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -33,7 +33,7 @@ #include "cmMakefile.h" #include "cmOutputConverter.h" #include "cmPolicies.h" -#include "cmQtAutoGeneratorInitializer.h" +#include "cmQtAutoGenInitializer.h" #include "cmSourceFile.h" #include "cmState.h" #include "cmStateDirectory.h" @@ -1473,10 +1473,10 @@ bool cmGlobalGenerator::ComputeTargetDepends() return true; } -std::vector<std::unique_ptr<cmQtAutoGeneratorInitializer>> +std::vector<std::unique_ptr<cmQtAutoGenInitializer>> cmGlobalGenerator::CreateQtAutoGenInitializers() { - std::vector<std::unique_ptr<cmQtAutoGeneratorInitializer>> autogenInits; + std::vector<std::unique_ptr<cmQtAutoGenInitializer>> autogenInits; #ifdef CMAKE_BUILD_WITH_CMAKE for (cmLocalGenerator* localGen : this->LocalGenerators) { @@ -1506,13 +1506,13 @@ cmGlobalGenerator::CreateQtAutoGenInitializers() } std::string qtVersionMajor = - cmQtAutoGeneratorInitializer::GetQtMajorVersion(target); + cmQtAutoGenInitializer::GetQtMajorVersion(target); // don't do anything if there is no Qt4 or Qt5Core (which contains moc) if (qtVersionMajor != "4" && qtVersionMajor != "5") { continue; } - autogenInits.emplace_back(new cmQtAutoGeneratorInitializer( + autogenInits.emplace_back(new cmQtAutoGenInitializer( target, mocEnabled, uicEnabled, rccEnabled, qtVersionMajor)); } } diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 92e6a29..3ebff82 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -32,7 +32,7 @@ class cmLinkLineComputer; class cmLocalGenerator; class cmMakefile; class cmOutputConverter; -class cmQtAutoGeneratorInitializer; +class cmQtAutoGenInitializer; class cmSourceFile; class cmStateDirectory; class cmake; @@ -437,7 +437,7 @@ protected: virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const; // Qt auto generators - std::vector<std::unique_ptr<cmQtAutoGeneratorInitializer>> + std::vector<std::unique_ptr<cmQtAutoGenInitializer>> CreateQtAutoGenInitializers(); std::string SelectMakeProgram(const std::string& makeProgram, diff --git a/Source/cmGlobalXCodeGenerator.cxx b/Source/cmGlobalXCodeGenerator.cxx index 338c2b4..bd51f60 100644 --- a/Source/cmGlobalXCodeGenerator.cxx +++ b/Source/cmGlobalXCodeGenerator.cxx @@ -1703,6 +1703,7 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, gtgt->GetName().c_str()); return; } + std::string const& langForPreprocessor = llang; if (gtgt->IsIPOEnabled(llang, configName)) { const char* ltoValue = @@ -1723,7 +1724,10 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, this->AppendDefines(ppDefs, exportMacro); } std::vector<std::string> targetDefines; - gtgt->GetCompileDefinitions(targetDefines, configName, "C"); + if (!langForPreprocessor.empty()) { + gtgt->GetCompileDefinitions(targetDefines, configName, + langForPreprocessor); + } this->AppendDefines(ppDefs, targetDefines); buildSettings->AddAttribute("GCC_PREPROCESSOR_DEFINITIONS", ppDefs.CreateList()); @@ -1996,8 +2000,10 @@ void cmGlobalXCodeGenerator::CreateBuildSettings(cmGeneratorTarget* gtgt, const bool emitSystemIncludes = this->XcodeVersion >= 83; std::vector<std::string> includes; - this->CurrentLocalGenerator->GetIncludeDirectories(includes, gtgt, "C", - configName); + if (!langForPreprocessor.empty()) { + this->CurrentLocalGenerator->GetIncludeDirectories( + includes, gtgt, langForPreprocessor, configName); + } std::set<std::string> emitted; emitted.insert("/System/Library/Frameworks"); diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index eccd4d0..59c20a9 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -637,7 +637,8 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( configType = projectType; } std::string flags; - if (strcmp(configType, "10") != 0) { + std::string langForClCompile; + if (target->GetType() <= cmStateEnums::OBJECT_LIBRARY) { const std::string& linkLanguage = (this->FortranProject ? std::string("Fortran") : target->GetLinkerLanguage(configName)); @@ -647,10 +648,11 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( target->GetName().c_str()); return; } - if (linkLanguage == "C" || linkLanguage == "CXX" || - linkLanguage == "Fortran") { + langForClCompile = linkLanguage; + if (langForClCompile == "C" || langForClCompile == "CXX" || + langForClCompile == "Fortran") { std::string baseFlagVar = "CMAKE_"; - baseFlagVar += linkLanguage; + baseFlagVar += langForClCompile; baseFlagVar += "_FLAGS"; flags = this->Makefile->GetRequiredDefinition(baseFlagVar.c_str()); std::string flagVar = @@ -667,7 +669,7 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( } // Add the target-specific flags. - this->AddCompileOptions(flags, target, linkLanguage, configName); + this->AddCompileOptions(flags, target, langForClCompile, configName); // Check IPO related warning/error. target->IsIPOEnabled(linkLanguage, configName); @@ -703,7 +705,9 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( targetOptions.Parse(defineFlags.c_str()); targetOptions.ParseFinish(); std::vector<std::string> targetDefines; - target->GetCompileDefinitions(targetDefines, configName, "CXX"); + if (!langForClCompile.empty()) { + target->GetCompileDefinitions(targetDefines, configName, langForClCompile); + } targetOptions.AddDefines(targetDefines); targetOptions.SetVerboseMakefile( this->Makefile->IsOn("CMAKE_VERBOSE_MAKEFILE")); @@ -792,10 +796,13 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( << "\\$(ConfigurationName)\"\n"; } fout << "\t\t\t\tAdditionalIncludeDirectories=\""; - std::vector<std::string> includes; - this->GetIncludeDirectories(includes, target, "C", configName); - std::vector<std::string>::iterator i = includes.begin(); - for (; i != includes.end(); ++i) { + std::vector<std::string> includes_cl; + if (!langForClCompile.empty()) { + this->GetIncludeDirectories(includes_cl, target, langForClCompile, + configName); + } + std::vector<std::string>::iterator i = includes_cl.begin(); + for (; i != includes_cl.end(); ++i) { // output the include path std::string ipath = this->ConvertToXMLOutputPath(i->c_str()); fout << ipath << ";"; @@ -810,7 +817,8 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( } fout << "\"\n"; targetOptions.OutputFlagMap(fout, "\t\t\t\t"); - targetOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t", "\n", "CXX"); + targetOptions.OutputPreprocessorDefinitions(fout, "\t\t\t\t", "\n", + langForClCompile); fout << "\t\t\t\tObjectFile=\"$(IntDir)\\\"\n"; if (target->GetType() <= cmStateEnums::OBJECT_LIBRARY) { // Specify the compiler program database file if configured. @@ -829,9 +837,12 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( "\t\t\t\tName=\"MASM\"\n" "\t\t\t\tIncludePaths=\"" ; + std::vector<std::string> includes_masm; + this->GetIncludeDirectories(includes_masm, target, "ASM_MASM", + configName); /* clang-format on */ const char* sep = ""; - for (i = includes.begin(); i != includes.end(); ++i) { + for (i = includes_masm.begin(); i != includes_masm.end(); ++i) { std::string inc = *i; cmConvertToWindowsSlash(inc); fout << sep << this->EscapeForXML(inc); @@ -859,7 +870,9 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( } fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"\n" << "\t\t\t\tAdditionalIncludeDirectories=\""; - for (i = includes.begin(); i != includes.end(); ++i) { + std::vector<std::string> includes_rc; + this->GetIncludeDirectories(includes_rc, target, "RC", configName); + for (i = includes_rc.begin(); i != includes_rc.end(); ++i) { std::string ipath = this->ConvertToXMLOutputPath(i->c_str()); fout << ipath << ";"; } @@ -873,7 +886,9 @@ void cmLocalVisualStudio7Generator::WriteConfiguration( } fout << "\t\t\t<Tool\n\t\t\t\tName=\"" << tool << "\"\n"; fout << "\t\t\t\tAdditionalIncludeDirectories=\""; - for (i = includes.begin(); i != includes.end(); ++i) { + std::vector<std::string> includes_midl; + this->GetIncludeDirectories(includes_midl, target, "MIDL", configName); + for (i = includes_midl.begin(); i != includes_midl.end(); ++i) { std::string ipath = this->ConvertToXMLOutputPath(i->c_str()); fout << ipath << ";"; } diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 5d6029c..eeeb54f 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -855,7 +855,7 @@ cmSourceFile* cmMakefile::AddCustomCommandToOutput( std::string outName = gg->GenerateRuleFile(outputs[0]); // Check if the rule file already exists. - file = this->GetSource(outName); + file = this->GetSource(outName, cmSourceFileLocationKind::Known); if (file && file->GetCustomCommand() && !replace) { // The rule file already exists. if (commandLines != file->GetCustomCommand()->GetCommandLines()) { @@ -868,19 +868,22 @@ cmSourceFile* cmMakefile::AddCustomCommandToOutput( // Create a cmSourceFile for the rule file. if (!file) { - file = this->CreateSource(outName, true); + file = + this->CreateSource(outName, true, cmSourceFileLocationKind::Known); } file->SetProperty("__CMAKE_RULE", "1"); } // Always create the output sources and mark them generated. for (std::string const& o : outputs) { - if (cmSourceFile* out = this->GetOrCreateSource(o, true)) { + if (cmSourceFile* out = + this->GetOrCreateSource(o, true, cmSourceFileLocationKind::Known)) { out->SetProperty("GENERATED", "1"); } } for (std::string const& o : byproducts) { - if (cmSourceFile* out = this->GetOrCreateSource(o, true)) { + if (cmSourceFile* out = + this->GetOrCreateSource(o, true, cmSourceFileLocationKind::Known)) { out->SetProperty("GENERATED", "1"); } } @@ -967,7 +970,7 @@ void cmMakefile::AddCustomCommandOldStyle( } // Each output must get its own copy of this rule. - cmsys::RegularExpression sourceFiles("\\.(C|M|c|c\\+\\+|cc|cpp|cxx|m|mm|" + cmsys::RegularExpression sourceFiles("\\.(C|M|c|c\\+\\+|cc|cpp|cxx|cu|m|mm|" "rc|def|r|odl|idl|hpj|bat|h|h\\+\\+|" "hm|hpp|hxx|in|txx|inl)$"); for (std::string const& oi : outputs) { @@ -1092,7 +1095,8 @@ cmTarget* cmMakefile::AddUtilityCommand( // Always create the byproduct sources and mark them generated. for (std::string const& byproduct : byproducts) { - if (cmSourceFile* out = this->GetOrCreateSource(byproduct, true)) { + if (cmSourceFile* out = this->GetOrCreateSource( + byproduct, true, cmSourceFileLocationKind::Known)) { out->SetProperty("GENERATED", "1"); } } @@ -1961,7 +1965,7 @@ cmSourceGroup* cmMakefile::GetSourceGroup( if (sg != nullptr) { // iterate through its children to find match source group for (unsigned int i = 1; i < name.size(); ++i) { - sg = sg->LookupChild(name[i].c_str()); + sg = sg->LookupChild(name[i]); if (sg == nullptr) { break; } @@ -2005,7 +2009,7 @@ void cmMakefile::AddSourceGroup(const std::vector<std::string>& name, if (i == -1) { // group does not exist nor belong to any existing group // add its first component - this->SourceGroups.push_back(cmSourceGroup(name[0].c_str(), regex)); + this->SourceGroups.push_back(cmSourceGroup(name[0], regex)); sg = this->GetSourceGroup(currentName); i = 0; // last component found } @@ -2015,9 +2019,8 @@ void cmMakefile::AddSourceGroup(const std::vector<std::string>& name, } // build the whole source group path for (++i; i <= lastElement; ++i) { - sg->AddChild( - cmSourceGroup(name[i].c_str(), nullptr, sg->GetFullName().c_str())); - sg = sg->LookupChild(name[i].c_str()); + sg->AddChild(cmSourceGroup(name[i], nullptr, sg->GetFullName().c_str())); + sg = sg->LookupChild(name[i]); } sg->SetGroupRegex(regex); @@ -3132,9 +3135,10 @@ void cmMakefile::SetArgcArgv(const std::vector<std::string>& args) } } -cmSourceFile* cmMakefile::GetSource(const std::string& sourceName) const +cmSourceFile* cmMakefile::GetSource(const std::string& sourceName, + cmSourceFileLocationKind kind) const { - cmSourceFileLocation sfl(this, sourceName); + cmSourceFileLocation sfl(this, sourceName, kind); auto name = this->GetCMakeInstance()->StripExtension(sfl.GetName()); #if defined(_WIN32) || defined(__APPLE__) name = cmSystemTools::LowerCase(name); @@ -3151,9 +3155,10 @@ cmSourceFile* cmMakefile::GetSource(const std::string& sourceName) const } cmSourceFile* cmMakefile::CreateSource(const std::string& sourceName, - bool generated) + bool generated, + cmSourceFileLocationKind kind) { - cmSourceFile* sf = new cmSourceFile(this, sourceName); + cmSourceFile* sf = new cmSourceFile(this, sourceName, kind); if (generated) { sf->SetProperty("GENERATED", "1"); } @@ -3170,12 +3175,13 @@ cmSourceFile* cmMakefile::CreateSource(const std::string& sourceName, } cmSourceFile* cmMakefile::GetOrCreateSource(const std::string& sourceName, - bool generated) + bool generated, + cmSourceFileLocationKind kind) { - if (cmSourceFile* esf = this->GetSource(sourceName)) { + if (cmSourceFile* esf = this->GetSource(sourceName, kind)) { return esf; } - return this->CreateSource(sourceName, generated); + return this->CreateSource(sourceName, generated, kind); } void cmMakefile::AddTargetObject(std::string const& tgtName, diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index c92424b..f06e2ff 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -20,6 +20,7 @@ #include "cmListFileCache.h" #include "cmNewLineStyle.h" #include "cmPolicies.h" +#include "cmSourceFileLocationKind.h" #include "cmStateSnapshot.h" #include "cmStateTypes.h" #include "cmTarget.h" @@ -387,22 +388,26 @@ public: /** Get a cmSourceFile pointer for a given source name, if the name is * not found, then a null pointer is returned. */ - cmSourceFile* GetSource(const std::string& sourceName) const; + cmSourceFile* GetSource( + const std::string& sourceName, + cmSourceFileLocationKind kind = cmSourceFileLocationKind::Ambiguous) const; /** Create the source file and return it. generated * indicates if it is a generated file, this is used in determining * how to create the source file instance e.g. name */ - cmSourceFile* CreateSource(const std::string& sourceName, - bool generated = false); + cmSourceFile* CreateSource( + const std::string& sourceName, bool generated = false, + cmSourceFileLocationKind kind = cmSourceFileLocationKind::Ambiguous); /** Get a cmSourceFile pointer for a given source name, if the name is * not found, then create the source file and return it. generated * indicates if it is a generated file, this is used in determining * how to create the source file instance e.g. name */ - cmSourceFile* GetOrCreateSource(const std::string& sourceName, - bool generated = false); + cmSourceFile* GetOrCreateSource( + const std::string& sourceName, bool generated = false, + cmSourceFileLocationKind kind = cmSourceFileLocationKind::Ambiguous); void AddTargetObject(std::string const& tgtName, std::string const& objFile); diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx index e0cc35a..594e0f5 100644 --- a/Source/cmNinjaNormalTargetGenerator.cxx +++ b/Source/cmNinjaNormalTargetGenerator.cxx @@ -760,7 +760,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement() /*implicitOuts=*/cmNinjaDeps(), explicitDeps, implicitDeps, orderOnlyDeps, vars, rspfile, commandLineLengthLimit, &usedResponseFile); - this->WriteDeviceLinkRule(usedResponseFile); + this->WriteDeviceLinkRule(false); } void cmNinjaNormalTargetGenerator::WriteLinkStatement() diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index 255a532..18ecbe7 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx @@ -2,16 +2,13 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmQtAutoGen.h" #include "cmAlgorithms.h" -#include "cmProcessOutput.h" #include "cmSystemTools.h" -#include "cmsys/FStream.hxx" #include "cmsys/RegularExpression.hxx" #include <algorithm> #include <iterator> #include <sstream> -#include <stddef.h> // - Static variables @@ -21,8 +18,8 @@ std::string const genNameUic = "AutoUic"; std::string const genNameRcc = "AutoRcc"; std::string const mcNameSingle = "SINGLE"; -std::string const mcNameWrap = "WRAP"; -std::string const mcNameFull = "FULL"; +std::string const mcNameWrapper = "WRAPPER"; +std::string const mcNameMulti = "MULTI"; // - Static functions @@ -80,203 +77,53 @@ void MergeOptions(std::vector<std::string>& baseOpts, baseOpts.insert(baseOpts.end(), extraOpts.begin(), extraOpts.end()); } -/// @brief Reads the resource files list from from a .qrc file - Qt4 version -/// @return True if the .qrc file was successfully parsed -static bool RccListInputsQt4(std::string const& fileName, - std::vector<std::string>& files, - std::string* errorMessage) -{ - bool allGood = true; - // Read qrc file content into string - std::string qrcContents; - { - cmsys::ifstream ifs(fileName.c_str()); - if (ifs) { - std::ostringstream osst; - osst << ifs.rdbuf(); - qrcContents = osst.str(); - } else { - if (errorMessage != nullptr) { - std::string& err = *errorMessage; - err = "rcc file not readable:\n "; - err += cmQtAutoGen::Quoted(fileName); - err += "\n"; - } - allGood = false; - } - } - if (allGood) { - // qrc file directory - std::string qrcDir(cmSystemTools::GetFilenamePath(fileName)); - if (!qrcDir.empty()) { - qrcDir += '/'; - } - - cmsys::RegularExpression fileMatchRegex("(<file[^<]+)"); - cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)"); - - size_t offset = 0; - while (fileMatchRegex.find(qrcContents.c_str() + offset)) { - std::string qrcEntry = fileMatchRegex.match(1); - offset += qrcEntry.size(); - { - fileReplaceRegex.find(qrcEntry); - std::string tag = fileReplaceRegex.match(1); - qrcEntry = qrcEntry.substr(tag.size()); - } - if (!cmSystemTools::FileIsFullPath(qrcEntry.c_str())) { - qrcEntry = qrcDir + qrcEntry; - } - files.push_back(qrcEntry); - } - } - return allGood; -} - -/// @brief Reads the resource files list from from a .qrc file - Qt5 version -/// @return True if the .qrc file was successfully parsed -static bool RccListInputsQt5(std::string const& rccCommand, - std::vector<std::string> const& rccListOptions, - std::string const& fileName, - std::vector<std::string>& files, - std::string* errorMessage) -{ - if (rccCommand.empty()) { - cmSystemTools::Error("rcc executable not available"); - return false; - } - - std::string const fileDir = cmSystemTools::GetFilenamePath(fileName); - std::string const fileNameName = cmSystemTools::GetFilenameName(fileName); - - // Run rcc list command - bool result = false; - int retVal = 0; - std::string rccStdOut; - std::string rccStdErr; - { - std::vector<std::string> command; - command.push_back(rccCommand); - command.insert(command.end(), rccListOptions.begin(), - rccListOptions.end()); - command.push_back(fileNameName); - result = cmSystemTools::RunSingleCommand( - command, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(), - cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto); - } - if (!result || retVal) { - if (errorMessage != nullptr) { - std::string& err = *errorMessage; - err = "rcc list process failed for:\n "; - err += cmQtAutoGen::Quoted(fileName); - err += "\n"; - err += rccStdOut; - err += "\n"; - err += rccStdErr; - err += "\n"; - } - return false; - } - - // Lambda to strip CR characters - auto StripCR = [](std::string& line) { - std::string::size_type cr = line.find('\r'); - if (cr != std::string::npos) { - line = line.substr(0, cr); - } - }; - - // Parse rcc std output - { - std::istringstream ostr(rccStdOut); - std::string oline; - while (std::getline(ostr, oline)) { - StripCR(oline); - if (!oline.empty()) { - files.push_back(oline); - } - } - } - // Parse rcc error output - { - std::istringstream estr(rccStdErr); - std::string eline; - while (std::getline(estr, eline)) { - StripCR(eline); - if (cmHasLiteralPrefix(eline, "RCC: Error in")) { - static std::string searchString = "Cannot find file '"; - - std::string::size_type pos = eline.find(searchString); - if (pos == std::string::npos) { - if (errorMessage != nullptr) { - std::string& err = *errorMessage; - err = "rcc lists unparsable output:\n"; - err += cmQtAutoGen::Quoted(eline); - err += "\n"; - } - return false; - } - pos += searchString.length(); - std::string::size_type sz = eline.size() - pos - 1; - files.push_back(eline.substr(pos, sz)); - } - } - } - - // Convert relative paths to absolute paths - for (std::string& resFile : files) { - resFile = cmSystemTools::CollapseCombinedPath(fileDir, resFile); - } - - return true; -} - // - Class definitions -std::string const cmQtAutoGen::listSep = "<<<S>>>"; +std::string const cmQtAutoGen::ListSep = "<<<S>>>"; +unsigned int const cmQtAutoGen::ParallelMax = 64; -std::string const& cmQtAutoGen::GeneratorName(Generator type) +std::string const& cmQtAutoGen::GeneratorName(GeneratorT type) { switch (type) { - case Generator::GEN: + case GeneratorT::GEN: return genNameGen; - case Generator::MOC: + case GeneratorT::MOC: return genNameMoc; - case Generator::UIC: + case GeneratorT::UIC: return genNameUic; - case Generator::RCC: + case GeneratorT::RCC: return genNameRcc; } return genNameGen; } -std::string cmQtAutoGen::GeneratorNameUpper(Generator genType) +std::string cmQtAutoGen::GeneratorNameUpper(GeneratorT genType) { return cmSystemTools::UpperCase(cmQtAutoGen::GeneratorName(genType)); } -std::string const& cmQtAutoGen::MultiConfigName(MultiConfig config) +std::string const& cmQtAutoGen::MultiConfigName(MultiConfigT config) { switch (config) { - case MultiConfig::SINGLE: + case MultiConfigT::SINGLE: return mcNameSingle; - case MultiConfig::WRAP: - return mcNameWrap; - case MultiConfig::FULL: - return mcNameFull; + case MultiConfigT::WRAPPER: + return mcNameWrapper; + case MultiConfigT::MULTI: + return mcNameMulti; } - return mcNameWrap; + return mcNameWrapper; } -cmQtAutoGen::MultiConfig cmQtAutoGen::MultiConfigType(std::string const& name) +cmQtAutoGen::MultiConfigT cmQtAutoGen::MultiConfigType(std::string const& name) { if (name == mcNameSingle) { - return MultiConfig::SINGLE; + return MultiConfigT::SINGLE; } - if (name == mcNameFull) { - return MultiConfig::FULL; + if (name == mcNameMulti) { + return MultiConfigT::MULTI; } - return MultiConfig::WRAP; + return MultiConfigT::WRAPPER; } std::string cmQtAutoGen::Quoted(std::string const& text) @@ -294,6 +141,33 @@ std::string cmQtAutoGen::Quoted(std::string const& text) return res; } +std::string cmQtAutoGen::QuotedCommand(std::vector<std::string> const& command) +{ + std::string res; + for (std::string const& item : command) { + if (!res.empty()) { + res.push_back(' '); + } + std::string const cesc = cmQtAutoGen::Quoted(item); + if (item.empty() || (cesc.size() > (item.size() + 2)) || + (cesc.find(' ') != std::string::npos)) { + res += cesc; + } else { + res += item; + } + } + return res; +} + +std::string cmQtAutoGen::SubDirPrefix(std::string const& filename) +{ + std::string res(cmSystemTools::GetFilenamePath(filename)); + if (!res.empty()) { + res += '/'; + } + return res; +} + std::string cmQtAutoGen::AppendFilenameSuffix(std::string const& filename, std::string const& suffix) { @@ -333,27 +207,79 @@ void cmQtAutoGen::RccMergeOptions(std::vector<std::string>& baseOpts, MergeOptions(baseOpts, newOpts, valueOpts, isQt5); } -bool cmQtAutoGen::RccListInputs(std::string const& rccCommand, - std::vector<std::string> const& rccListOptions, - std::string const& fileName, - std::vector<std::string>& files, - std::string* errorMessage) +void cmQtAutoGen::RccListParseContent(std::string const& content, + std::vector<std::string>& files) { - bool allGood = false; - if (cmSystemTools::FileExists(fileName.c_str())) { - if (rccListOptions.empty()) { - allGood = RccListInputsQt4(fileName, files, errorMessage); - } else { - allGood = RccListInputsQt5(rccCommand, rccListOptions, fileName, files, - errorMessage); + cmsys::RegularExpression fileMatchRegex("(<file[^<]+)"); + cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)"); + + const char* contentChars = content.c_str(); + while (fileMatchRegex.find(contentChars)) { + std::string const qrcEntry = fileMatchRegex.match(1); + contentChars += qrcEntry.size(); + { + fileReplaceRegex.find(qrcEntry); + std::string const tag = fileReplaceRegex.match(1); + files.push_back(qrcEntry.substr(tag.size())); } - } else { - if (errorMessage != nullptr) { - std::string& err = *errorMessage; - err = "rcc resource file does not exist:\n "; - err += cmQtAutoGen::Quoted(fileName); - err += "\n"; + } +} + +bool cmQtAutoGen::RccListParseOutput(std::string const& rccStdOut, + std::string const& rccStdErr, + std::vector<std::string>& files, + std::string& error) +{ + // Lambda to strip CR characters + auto StripCR = [](std::string& line) { + std::string::size_type cr = line.find('\r'); + if (cr != std::string::npos) { + line = line.substr(0, cr); } + }; + + // Parse rcc std output + { + std::istringstream ostr(rccStdOut); + std::string oline; + while (std::getline(ostr, oline)) { + StripCR(oline); + if (!oline.empty()) { + files.push_back(oline); + } + } + } + // Parse rcc error output + { + std::istringstream estr(rccStdErr); + std::string eline; + while (std::getline(estr, eline)) { + StripCR(eline); + if (cmHasLiteralPrefix(eline, "RCC: Error in")) { + static std::string const searchString = "Cannot find file '"; + + std::string::size_type pos = eline.find(searchString); + if (pos == std::string::npos) { + error = "rcc lists unparsable output:\n"; + error += cmQtAutoGen::Quoted(eline); + error += "\n"; + return false; + } + pos += searchString.length(); + std::string::size_type sz = eline.size() - pos - 1; + files.push_back(eline.substr(pos, sz)); + } + } + } + + return true; +} + +void cmQtAutoGen::RccListConvertFullPath(std::string const& qrcFileDir, + std::vector<std::string>& files) +{ + for (std::string& entry : files) { + std::string tmp = cmSystemTools::CollapseCombinedPath(qrcFileDir, entry); + entry = std::move(tmp); } - return allGood; } diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index e769e93..30ce0f6 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -9,14 +9,18 @@ #include <vector> /** \class cmQtAutoGen - * \brief Class used as namespace for QtAutogen related types and functions + * \brief Common base class for QtAutoGen classes */ class cmQtAutoGen { public: - static std::string const listSep; + /// @brief Nested lists separator + static std::string const ListSep; + /// @brief Maximum number of parallel threads/processes in a generator + static unsigned int const ParallelMax; - enum Generator + /// @brief AutoGen generator type + enum class GeneratorT { GEN, // General MOC, @@ -24,27 +28,33 @@ public: RCC }; - enum MultiConfig + /// @brief Multiconfiguration type + enum class MultiConfigT { - SINGLE, // Single configuration - WRAP, // Multi configuration using wrapper files - FULL // Full multi configuration using per config sources + SINGLE, // Single configuration + WRAPPER, // Multi configuration using wrapper files + MULTI // Multi configuration using per config sources }; public: /// @brief Returns the generator name - static std::string const& GeneratorName(Generator genType); + static std::string const& GeneratorName(GeneratorT genType); /// @brief Returns the generator name in upper case - static std::string GeneratorNameUpper(Generator genType); + static std::string GeneratorNameUpper(GeneratorT genType); /// @brief Returns the multi configuration name string - static std::string const& MultiConfigName(MultiConfig config); + static std::string const& MultiConfigName(MultiConfigT config); /// @brief Returns the multi configuration type - static MultiConfig MultiConfigType(std::string const& name); + static MultiConfigT MultiConfigType(std::string const& name); /// @brief Returns a the string escaped and enclosed in quotes static std::string Quoted(std::string const& text); + static std::string QuotedCommand(std::vector<std::string> const& command); + + /// @brief Returns the parent directory of the file with a "/" suffix + static std::string SubDirPrefix(std::string const& filename); + /// @brief Appends the suffix to the filename before the last dot static std::string AppendFilenameSuffix(std::string const& filename, std::string const& suffix); @@ -59,14 +69,21 @@ public: std::vector<std::string> const& newOpts, bool isQt5); - /// @brief Reads the resource files list from from a .qrc file - /// @arg fileName Must be the absolute path of the .qrc file - /// @return True if the rcc file was successfully read - static bool RccListInputs(std::string const& rccCommand, - std::vector<std::string> const& rccListOptions, - std::string const& fileName, - std::vector<std::string>& files, - std::string* errorMessage = nullptr); + /// @brief Parses the content of a qrc file + /// + /// Use when rcc does not support the "--list" option + static void RccListParseContent(std::string const& content, + std::vector<std::string>& files); + + /// @brief Parses the output of the "rcc --list ..." command + static bool RccListParseOutput(std::string const& rccStdOut, + std::string const& rccStdErr, + std::vector<std::string>& files, + std::string& error); + + /// @brief Converts relative qrc entry paths to full paths + static void RccListConvertFullPath(std::string const& qrcFileDir, + std::vector<std::string>& files); }; #endif diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 14743de..0b67981 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -1,7 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmQtAutoGen.h" -#include "cmQtAutoGeneratorInitializer.h" +#include "cmQtAutoGenInitializer.h" #include "cmAlgorithms.h" #include "cmCustomCommand.h" @@ -24,6 +24,7 @@ #include "cm_sys_stat.h" #include "cmake.h" #include "cmsys/FStream.hxx" +#include "cmsys/SystemInformation.hxx" #include <algorithm> #include <array> @@ -52,6 +53,20 @@ inline static std::string GetSafeProperty(cmSourceFile const* sf, return std::string(SafeString(sf->GetProperty(key))); } +static std::size_t GetParallelCPUCount() +{ + static std::size_t count = 0; + // Detect only on the first call + if (count == 0) { + cmsys::SystemInformation info; + info.RunCPUCheck(); + count = info.GetNumberOfPhysicalCPU(); + count = std::max<std::size_t>(count, 1); + count = std::min<std::size_t>(count, cmQtAutoGen::ParallelMax); + } + return count; +} + static void AddDefinitionEscaped(cmMakefile* makefile, const char* key, std::string const& value) { @@ -85,12 +100,12 @@ static void AddDefinitionEscaped( seplist.push_back(std::move(blist)); } makefile->AddDefinition(key, cmOutputConverter::EscapeForCMake( - cmJoin(seplist, cmQtAutoGen::listSep)) + cmJoin(seplist, cmQtAutoGen::ListSep)) .c_str()); } static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName, - cmQtAutoGen::Generator genType) + cmQtAutoGen::GeneratorT genType) { cmSourceGroup* sourceGroup = nullptr; // Acquire source group @@ -101,10 +116,10 @@ static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName, std::array<std::string, 2> props; // Use generator specific group name switch (genType) { - case cmQtAutoGen::MOC: + case cmQtAutoGen::GeneratorT::MOC: props[0] = "AUTOMOC_SOURCE_GROUP"; break; - case cmQtAutoGen::RCC: + case cmQtAutoGen::GeneratorT::RCC: props[0] = "AUTORCC_SOURCE_GROUP"; break; default: @@ -211,7 +226,7 @@ static bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin, return cycle; } -cmQtAutoGeneratorInitializer::cmQtAutoGeneratorInitializer( +cmQtAutoGenInitializer::cmQtAutoGenInitializer( cmGeneratorTarget* target, bool mocEnabled, bool uicEnabled, bool rccEnabled, std::string const& qtVersionMajor) : Target(target) @@ -219,13 +234,13 @@ cmQtAutoGeneratorInitializer::cmQtAutoGeneratorInitializer( , UicEnabled(uicEnabled) , RccEnabled(rccEnabled) , QtVersionMajor(qtVersionMajor) - , MultiConfig(cmQtAutoGen::WRAP) + , MultiConfig(MultiConfigT::WRAPPER) { - this->QtVersionMinor = cmQtAutoGeneratorInitializer::GetQtMinorVersion( - target, this->QtVersionMajor); + this->QtVersionMinor = + cmQtAutoGenInitializer::GetQtMinorVersion(target, this->QtVersionMajor); } -void cmQtAutoGeneratorInitializer::InitCustomTargets() +void cmQtAutoGenInitializer::InitCustomTargets() { cmMakefile* makefile = this->Target->Target->GetMakefile(); cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); @@ -240,19 +255,19 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() // Multi configuration { if (!globalGen->IsMultiConfig()) { - this->MultiConfig = cmQtAutoGen::SINGLE; + this->MultiConfig = MultiConfigT::SINGLE; } // FIXME: Xcode does not support per-config sources, yet. // (EXCLUDED_SOURCE_FILE_NAMES) // if (globalGen->GetName().find("Xcode") != std::string::npos) { - // return cmQtAutoGen::FULL; + // return MultiConfigT::MULTI; //} // FIXME: Visual Studio does not support per-config sources, yet. // (EXCLUDED_SOURCE_FILE_NAMES) // if (globalGen->GetName().find("Visual Studio") != std::string::npos) { - // return cmQtAutoGen::FULL; + // return MultiConfigT::MULTI; //} } @@ -294,7 +309,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() this->AutogenInfoFile += "/AutogenInfo.cmake"; this->AutogenSettingsFile = this->DirInfo; - this->AutogenSettingsFile += "/AutogenOldSettings.cmake"; + this->AutogenSettingsFile += "/AutogenOldSettings.txt"; } // Autogen target FOLDER property @@ -324,7 +339,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() { std::string base = this->DirInfo; base += "/AutogenOldSettings"; - if (this->MultiConfig == cmQtAutoGen::SINGLE) { + if (this->MultiConfig == MultiConfigT::SINGLE) { AddCleanFile(makefile, base.append(".cmake")); } else { for (std::string const& cfg : this->ConfigsList) { @@ -340,7 +355,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() // Add moc compilation to generated files list if (this->MocEnabled) { std::string const mocsComp = this->DirBuild + "/mocs_compilation.cpp"; - auto files = this->AddGeneratedSource(mocsComp, cmQtAutoGen::MOC); + auto files = this->AddGeneratedSource(mocsComp, GeneratorT::MOC); for (std::string& file : files) { autogenProvides.push_back(std::move(file)); } @@ -349,7 +364,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES if (this->MocEnabled || this->UicEnabled) { std::string includeDir = this->DirBuild + "/include"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { + if (this->MultiConfig != MultiConfigT::SINGLE) { includeDir += "_$<CONFIG>"; } this->Target->AddIncludeDirectory(includeDir, true); @@ -475,10 +490,16 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() } // Read skip files from makefile sources if (this->MocEnabled || this->UicEnabled) { - const std::vector<cmSourceFile*>& allSources = makefile->GetSourceFiles(); - for (cmSourceFile* sf : allSources) { + std::string pathError; + for (cmSourceFile* sf : makefile->GetSourceFiles()) { // sf->GetExtension() is only valid after sf->GetFullPath() ... - std::string const& fPath = sf->GetFullPath(); + // Since we're iterating over source files that might be not in the + // target we need to check for path errors (not existing files). + std::string const& fPath = sf->GetFullPath(&pathError); + if (!pathError.empty()) { + pathError.clear(); + continue; + } cmSystemTools::FileFormat const fileType = cmSystemTools::GetFileFormat(sf->GetExtension().c_str()); if (!(fileType == cmSystemTools::CXX_FILE_FORMAT) && @@ -554,10 +575,10 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() msg += "For compatibility, CMake is excluding the GENERATED source " "file(s):\n"; for (const std::string& absFile : generatedHeaders) { - msg.append(" ").append(cmQtAutoGen::Quoted(absFile)).append("\n"); + msg.append(" ").append(Quoted(absFile)).append("\n"); } for (const std::string& absFile : generatedSources) { - msg.append(" ").append(cmQtAutoGen::Quoted(absFile)).append("\n"); + msg.append(" ").append(Quoted(absFile)).append("\n"); } msg += "from processing by "; msg += tools; @@ -624,7 +645,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() qrc.InfoFile = base; qrc.InfoFile += "Info.cmake"; qrc.SettingsFile = base; - qrc.SettingsFile += "Settings.cmake"; + qrc.SettingsFile += "Settings.txt"; } } } @@ -644,16 +665,16 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() std::vector<std::string> nameOpts; nameOpts.emplace_back("-name"); nameOpts.emplace_back(std::move(name)); - cmQtAutoGen::RccMergeOptions(opts, nameOpts, QtV5); + RccMergeOptions(opts, nameOpts, QtV5); } // Merge file option - cmQtAutoGen::RccMergeOptions(opts, qrc.Options, QtV5); + RccMergeOptions(opts, qrc.Options, QtV5); qrc.Options = std::move(opts); } for (Qrc& qrc : this->Qrcs) { // Register file at target std::vector<std::string> const ccOutput = - this->AddGeneratedSource(qrc.RccFile, cmQtAutoGen::RCC); + this->AddGeneratedSource(qrc.RccFile, GeneratorT::RCC); cmCustomCommandLines commandLines; { @@ -680,8 +701,9 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() ccName += qrc.PathChecksum; } std::vector<std::string> ccDepends; - // Add the .qrc file to the custom target dependencies + // Add the .qrc and info file to the custom target dependencies ccDepends.push_back(qrc.QrcFile); + ccDepends.push_back(qrc.InfoFile); cmTarget* autoRccTarget = makefile->AddUtilityCommand( ccName, cmMakefile::TargetOrigin::Generator, true, @@ -703,15 +725,14 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() { std::vector<std::string> ccByproducts; std::vector<std::string> ccDepends; - // Add the .qrc file to the custom command dependencies + // Add the .qrc and info file to the custom command dependencies ccDepends.push_back(qrc.QrcFile); + ccDepends.push_back(qrc.InfoFile); // Add the resource files to the dependencies { std::string error; - if (cmQtAutoGen::RccListInputs(this->RccExecutable, - this->RccListOptions, qrc.QrcFile, - qrc.Resources, &error)) { + if (RccListInputs(qrc.QrcFile, qrc.Resources, error)) { for (std::string const& fileName : qrc.Resources) { // Add resource file to the custom command dependencies ccDepends.push_back(fileName); @@ -879,7 +900,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() } } -void cmQtAutoGeneratorInitializer::SetupCustomTargets() +void cmQtAutoGenInitializer::SetupCustomTargets() { cmMakefile* makefile = this->Target->Target->GetMakefile(); @@ -897,8 +918,16 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() // Basic setup AddDefinitionEscaped(makefile, "_multi_config", - cmQtAutoGen::MultiConfigName(this->MultiConfig)); + MultiConfigName(this->MultiConfig)); AddDefinitionEscaped(makefile, "_build_dir", this->DirBuild); + { + std::string parallel = GetSafeProperty(this->Target, "AUTOGEN_PARALLEL"); + // Autodetect number of CPUs + if (parallel.empty() || (parallel == "AUTO")) { + parallel = std::to_string(GetParallelCPUCount()); + } + AddDefinitionEscaped(makefile, "_parallel", parallel); + } if (this->MocEnabled || this->UicEnabled) { AddDefinitionEscaped(makefile, "_qt_version_major", this->QtVersionMajor); @@ -923,7 +952,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() // Create info directory on demand if (!cmSystemTools::MakeDirectory(this->DirInfo)) { std::string emsg = ("Could not create directory: "); - emsg += cmQtAutoGen::Quoted(this->DirInfo); + emsg += Quoted(this->DirInfo); cmSystemTools::Error(emsg.c_str()); } @@ -945,7 +974,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() if (!ofs) { // File open error std::string error = "Internal CMake error when trying to open file: "; - error += cmQtAutoGen::Quoted(fileName); + error += Quoted(fileName); error += " for writing."; cmSystemTools::Error(error.c_str()); } @@ -978,11 +1007,11 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() OfsWriteMap("AM_MOC_INCLUDES", this->ConfigMocIncludes); OfsWriteMap("AM_UIC_TARGET_OPTIONS", this->ConfigUicOptions); // Settings files (only require for multi configuration generators) - if (this->MultiConfig != cmQtAutoGen::SINGLE) { + if (this->MultiConfig != MultiConfigT::SINGLE) { std::map<std::string, std::string> settingsFiles; for (std::string const& cfg : this->ConfigsList) { - settingsFiles[cfg] = cmQtAutoGen::AppendFilenameSuffix( - this->AutogenSettingsFile, "_" + cfg); + settingsFiles[cfg] = + AppendFilenameSuffix(this->AutogenSettingsFile, "_" + cfg); } OfsWriteMap("AM_SETTINGS_FILE", settingsFiles); } @@ -1027,11 +1056,11 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() OfsWriteMap("ARCC_CONFIG_SUFFIX", configSuffixes); // Settings files (only require for multi configuration generators) - if (this->MultiConfig != cmQtAutoGen::SINGLE) { + if (this->MultiConfig != MultiConfigT::SINGLE) { std::map<std::string, std::string> settingsFiles; for (std::string const& cfg : this->ConfigsList) { settingsFiles[cfg] = - cmQtAutoGen::AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg); + AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg); } OfsWriteMap("ARCC_SETTINGS_FILE", settingsFiles); } @@ -1043,7 +1072,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() } } -void cmQtAutoGeneratorInitializer::SetupCustomTargetsMoc() +void cmQtAutoGenInitializer::SetupCustomTargetsMoc() { cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); cmMakefile* makefile = this->Target->Target->GetMakefile(); @@ -1142,7 +1171,7 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargetsMoc() } } -void cmQtAutoGeneratorInitializer::SetupCustomTargetsUic() +void cmQtAutoGenInitializer::SetupCustomTargetsUic() { cmMakefile* makefile = this->Target->Target->GetMakefile(); @@ -1188,9 +1217,16 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargetsUic() std::vector<std::vector<std::string>> uiFileOptions; { std::string const uiExt = "ui"; + std::string pathError; for (cmSourceFile* sf : makefile->GetSourceFiles()) { // sf->GetExtension() is only valid after sf->GetFullPath() ... - std::string const& fPath = sf->GetFullPath(); + // Since we're iterating over source files that might be not in the + // target we need to check for path errors (not existing files). + std::string const& fPath = sf->GetFullPath(&pathError); + if (!pathError.empty()) { + pathError.clear(); + continue; + } if (sf->GetExtension() == uiExt) { std::string const absFile = cmSystemTools::GetRealPath(fPath); // Check if the .ui file should be skipped @@ -1253,17 +1289,16 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargetsUic() } } -std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource( - std::string const& filename, cmQtAutoGen::Generator genType) +std::vector<std::string> cmQtAutoGenInitializer::AddGeneratedSource( + std::string const& filename, GeneratorT genType) { std::vector<std::string> genFiles; // Register source file in makefile and source group - if (this->MultiConfig != cmQtAutoGen::FULL) { + if (this->MultiConfig != MultiConfigT::MULTI) { genFiles.push_back(filename); } else { for (std::string const& cfg : this->ConfigsList) { - genFiles.push_back( - cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg)); + genFiles.push_back(AppendFilenameSuffix(filename, "_" + cfg)); } } { @@ -1279,14 +1314,14 @@ std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource( } // Add source file to target - if (this->MultiConfig != cmQtAutoGen::FULL) { + if (this->MultiConfig != MultiConfigT::MULTI) { this->Target->AddSource(filename); } else { for (std::string const& cfg : this->ConfigsList) { std::string src = "$<$<CONFIG:"; src += cfg; src += ">:"; - src += cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg); + src += AppendFilenameSuffix(filename, "_" + cfg); src += ">"; this->Target->AddSource(src); } @@ -1295,7 +1330,7 @@ std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource( return genFiles; } -std::string cmQtAutoGeneratorInitializer::GetQtMajorVersion( +std::string cmQtAutoGenInitializer::GetQtMajorVersion( cmGeneratorTarget const* target) { cmMakefile* makefile = target->Target->GetMakefile(); @@ -1311,7 +1346,7 @@ std::string cmQtAutoGeneratorInitializer::GetQtMajorVersion( return qtMajor; } -std::string cmQtAutoGeneratorInitializer::GetQtMinorVersion( +std::string cmQtAutoGenInitializer::GetQtMinorVersion( cmGeneratorTarget const* target, std::string const& qtVersionMajor) { cmMakefile* makefile = target->Target->GetMakefile(); @@ -1331,7 +1366,7 @@ std::string cmQtAutoGeneratorInitializer::GetQtMinorVersion( return qtMinor; } -bool cmQtAutoGeneratorInitializer::QtVersionGreaterOrEqual( +bool cmQtAutoGenInitializer::QtVersionGreaterOrEqual( unsigned long requestMajor, unsigned long requestMinor) const { unsigned long majorUL(0); @@ -1343,3 +1378,84 @@ bool cmQtAutoGeneratorInitializer::QtVersionGreaterOrEqual( } return false; } + +/// @brief Reads the resource files list from from a .qrc file +/// @arg fileName Must be the absolute path of the .qrc file +/// @return True if the rcc file was successfully read +bool cmQtAutoGenInitializer::RccListInputs(std::string const& fileName, + std::vector<std::string>& files, + std::string& error) +{ + if (!cmSystemTools::FileExists(fileName.c_str())) { + error = "rcc resource file does not exist:\n "; + error += Quoted(fileName); + error += "\n"; + return false; + } + if (!RccListOptions.empty()) { + // Use rcc for file listing + if (RccExecutable.empty()) { + error = "rcc executable not available"; + return false; + } + + // Run rcc list command in the directory of the qrc file with the pathless + // qrc file name argument. This way rcc prints relative paths. + // This avoids issues on Windows when the qrc file is in a path that + // contains non-ASCII characters. + std::string const fileDir = cmSystemTools::GetFilenamePath(fileName); + std::string const fileNameName = cmSystemTools::GetFilenameName(fileName); + + bool result = false; + int retVal = 0; + std::string rccStdOut; + std::string rccStdErr; + { + std::vector<std::string> cmd; + cmd.push_back(RccExecutable); + cmd.insert(cmd.end(), RccListOptions.begin(), RccListOptions.end()); + cmd.push_back(fileNameName); + result = cmSystemTools::RunSingleCommand( + cmd, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(), + cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto); + } + if (!result || retVal) { + error = "rcc list process failed for:\n "; + error += Quoted(fileName); + error += "\n"; + error += rccStdOut; + error += "\n"; + error += rccStdErr; + error += "\n"; + return false; + } + if (!RccListParseOutput(rccStdOut, rccStdErr, files, error)) { + return false; + } + } else { + // We can't use rcc for the file listing. + // Read the qrc file content into string and parse it. + { + std::string qrcContents; + { + cmsys::ifstream ifs(fileName.c_str()); + if (ifs) { + std::ostringstream osst; + osst << ifs.rdbuf(); + qrcContents = osst.str(); + } else { + error = "rcc file not readable:\n "; + error += Quoted(fileName); + error += "\n"; + return false; + } + } + // Parse string content + RccListParseContent(qrcContents, files); + } + } + + // Convert relative paths to absolute paths + RccListConvertFullPath(cmSystemTools::GetFilenamePath(fileName), files); + return true; +} diff --git a/Source/cmQtAutoGeneratorInitializer.h b/Source/cmQtAutoGenInitializer.h index e06e1c4..a667017 100644 --- a/Source/cmQtAutoGeneratorInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -1,7 +1,7 @@ /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGeneratorInitializer_h -#define cmQtAutoGeneratorInitializer_h +#ifndef cmQtAutoGenInitializer_h +#define cmQtAutoGenInitializer_h #include "cmConfigure.h" // IWYU pragma: keep #include "cmQtAutoGen.h" @@ -13,7 +13,8 @@ class cmGeneratorTarget; -class cmQtAutoGeneratorInitializer +/// @brief Initializes the QtAutoGen generators +class cmQtAutoGenInitializer : public cmQtAutoGen { public: static std::string GetQtMajorVersion(cmGeneratorTarget const* target); @@ -43,9 +44,9 @@ public: }; public: - cmQtAutoGeneratorInitializer(cmGeneratorTarget* target, bool mocEnabled, - bool uicEnabled, bool rccEnabled, - std::string const& qtVersionMajor); + cmQtAutoGenInitializer(cmGeneratorTarget* target, bool mocEnabled, + bool uicEnabled, bool rccEnabled, + std::string const& qtVersionMajor); void InitCustomTargets(); void SetupCustomTargets(); @@ -55,11 +56,15 @@ private: void SetupCustomTargetsUic(); std::vector<std::string> AddGeneratedSource(std::string const& filename, - cmQtAutoGen::Generator genType); + GeneratorT genType); bool QtVersionGreaterOrEqual(unsigned long requestMajor, unsigned long requestMinor) const; + bool RccListInputs(std::string const& fileName, + std::vector<std::string>& files, + std::string& errorMessage); + private: cmGeneratorTarget* Target; bool MocEnabled; @@ -73,7 +78,7 @@ private: // Configurations std::string ConfigDefault; std::vector<std::string> ConfigsList; - cmQtAutoGen::MultiConfig MultiConfig; + MultiConfigT MultiConfig; // Names std::string AutogenTargetName; std::string AutogenFolder; diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx index ee0ddbc..5b2b6d0 100644 --- a/Source/cmQtAutoGenerator.cxx +++ b/Source/cmQtAutoGenerator.cxx @@ -4,7 +4,6 @@ #include "cmQtAutoGenerator.h" #include "cmsys/FStream.hxx" -#include "cmsys/Terminal.h" #include "cmAlgorithms.h" #include "cmGlobalGenerator.h" @@ -14,108 +13,55 @@ #include "cmSystemTools.h" #include "cmake.h" -// -- Static functions - -static std::string HeadLine(std::string const& title) -{ - std::string head = title; - head += '\n'; - head.append(head.size() - 1, '-'); - head += '\n'; - return head; -} - -static std::string QuotedCommand(std::vector<std::string> const& command) -{ - std::string res; - for (std::string const& item : command) { - if (!res.empty()) { - res.push_back(' '); - } - std::string const cesc = cmQtAutoGen::Quoted(item); - if (item.empty() || (cesc.size() > (item.size() + 2)) || - (cesc.find(' ') != std::string::npos)) { - res += cesc; - } else { - res += item; - } - } - return res; -} +#include <algorithm> // -- Class methods -cmQtAutoGenerator::cmQtAutoGenerator() - : Verbose(cmSystemTools::HasEnv("VERBOSE")) - , ColorOutput(true) +void cmQtAutoGenerator::Logger::SetVerbose(bool value) { - { - std::string colorEnv; - cmSystemTools::GetEnv("COLOR", colorEnv); - if (!colorEnv.empty()) { - this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str()); - } - } + Verbose_ = value; } -bool cmQtAutoGenerator::Run(std::string const& infoFile, - std::string const& config) +void cmQtAutoGenerator::Logger::SetColorOutput(bool value) { - // Info settings - this->InfoFile = infoFile; - cmSystemTools::ConvertToUnixSlashes(this->InfoFile); - this->InfoDir = cmSystemTools::GetFilenamePath(infoFile); - this->InfoConfig = config; - - cmake cm(cmake::RoleScript); - cm.SetHomeOutputDirectory(this->InfoDir); - cm.SetHomeDirectory(this->InfoDir); - cm.GetCurrentSnapshot().SetDefaultDefinitions(); - cmGlobalGenerator gg(&cm); - - cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); - snapshot.GetDirectory().SetCurrentBinary(this->InfoDir); - snapshot.GetDirectory().SetCurrentSource(this->InfoDir); - - auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot); - // The OLD/WARN behavior for policy CMP0053 caused a speed regression. - // https://gitlab.kitware.com/cmake/cmake/issues/17570 - makefile->SetPolicyVersion("3.9"); - gg.SetCurrentMakefile(makefile.get()); - - return this->Process(makefile.get()); + ColorOutput_ = value; } -void cmQtAutoGenerator::LogBold(std::string const& message) const +std::string cmQtAutoGenerator::Logger::HeadLine(std::string const& title) { - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, this->ColorOutput); + std::string head = title; + head += '\n'; + head.append(head.size() - 1, '-'); + head += '\n'; + return head; } -void cmQtAutoGenerator::LogInfo(cmQtAutoGen::Generator genType, - std::string const& message) const +void cmQtAutoGenerator::Logger::Info(GeneratorT genType, + std::string const& message) { - std::string msg = cmQtAutoGen::GeneratorName(genType); + std::string msg = GeneratorName(genType); msg += ": "; msg += message; if (msg.back() != '\n') { msg.push_back('\n'); } - cmSystemTools::Stdout(msg.c_str(), msg.size()); + { + std::lock_guard<std::mutex> lock(Mutex_); + cmSystemTools::Stdout(msg.c_str(), msg.size()); + } } -void cmQtAutoGenerator::LogWarning(cmQtAutoGen::Generator genType, - std::string const& message) const +void cmQtAutoGenerator::Logger::Warning(GeneratorT genType, + std::string const& message) { - std::string msg = cmQtAutoGen::GeneratorName(genType); - msg += " warning:"; + std::string msg; if (message.find('\n') == std::string::npos) { // Single line message - msg.push_back(' '); + msg += GeneratorName(genType); + msg += " warning: "; } else { // Multi line message - msg.push_back('\n'); + msg += HeadLine(GeneratorName(genType) + " warning"); } // Message msg += message; @@ -123,55 +69,60 @@ void cmQtAutoGenerator::LogWarning(cmQtAutoGen::Generator genType, msg.push_back('\n'); } msg.push_back('\n'); - cmSystemTools::Stdout(msg.c_str(), msg.size()); + { + std::lock_guard<std::mutex> lock(Mutex_); + cmSystemTools::Stdout(msg.c_str(), msg.size()); + } } -void cmQtAutoGenerator::LogFileWarning(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const +void cmQtAutoGenerator::Logger::WarningFile(GeneratorT genType, + std::string const& filename, + std::string const& message) { std::string msg = " "; - msg += cmQtAutoGen::Quoted(filename); + msg += Quoted(filename); msg.push_back('\n'); // Message msg += message; - this->LogWarning(genType, msg); + Warning(genType, msg); } -void cmQtAutoGenerator::LogError(cmQtAutoGen::Generator genType, - std::string const& message) const +void cmQtAutoGenerator::Logger::Error(GeneratorT genType, + std::string const& message) { std::string msg; - msg.push_back('\n'); - msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error"); + msg += HeadLine(GeneratorName(genType) + " error"); // Message msg += message; if (msg.back() != '\n') { msg.push_back('\n'); } msg.push_back('\n'); - cmSystemTools::Stderr(msg.c_str(), msg.size()); + { + std::lock_guard<std::mutex> lock(Mutex_); + cmSystemTools::Stderr(msg.c_str(), msg.size()); + } } -void cmQtAutoGenerator::LogFileError(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const +void cmQtAutoGenerator::Logger::ErrorFile(GeneratorT genType, + std::string const& filename, + std::string const& message) { std::string emsg = " "; - emsg += cmQtAutoGen::Quoted(filename); + emsg += Quoted(filename); emsg += '\n'; // Message emsg += message; - this->LogError(genType, emsg); + Error(genType, emsg); } -void cmQtAutoGenerator::LogCommandError( - cmQtAutoGen::Generator genType, std::string const& message, - std::vector<std::string> const& command, std::string const& output) const +void cmQtAutoGenerator::Logger::ErrorCommand( + GeneratorT genType, std::string const& message, + std::vector<std::string> const& command, std::string const& output) { std::string msg; msg.push_back('\n'); - msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error"); + msg += HeadLine(GeneratorName(genType) + " subprocess error"); msg += message; if (msg.back() != '\n') { msg.push_back('\n'); @@ -189,135 +140,495 @@ void cmQtAutoGenerator::LogCommandError( msg.push_back('\n'); } msg.push_back('\n'); - cmSystemTools::Stderr(msg.c_str(), msg.size()); + { + std::lock_guard<std::mutex> lock(Mutex_); + cmSystemTools::Stderr(msg.c_str(), msg.size()); + } } -/** - * @brief Generates the parent directory of the given file on demand - * @return True on success - */ -bool cmQtAutoGenerator::MakeParentDirectory(cmQtAutoGen::Generator genType, - std::string const& filename) const +std::string cmQtAutoGenerator::FileSystem::RealPath( + std::string const& filename) { - bool success = true; - std::string const dirName = cmSystemTools::GetFilenamePath(filename); - if (!dirName.empty()) { - if (!cmSystemTools::MakeDirectory(dirName)) { - this->LogFileError(genType, filename, - "Could not create parent directory"); - success = false; - } - } - return success; + std::lock_guard<std::mutex> lock(Mutex_); + return cmSystemTools::GetRealPath(filename); +} + +bool cmQtAutoGenerator::FileSystem::FileExists(std::string const& filename) +{ + std::lock_guard<std::mutex> lock(Mutex_); + return cmSystemTools::FileExists(filename); } -/** - * @brief Tests if buildFile is older than sourceFile - * @return True if buildFile is older than sourceFile. - * False may indicate an error. - */ -bool cmQtAutoGenerator::FileIsOlderThan(std::string const& buildFile, - std::string const& sourceFile, - std::string* error) +bool cmQtAutoGenerator::FileSystem::FileIsOlderThan( + std::string const& buildFile, std::string const& sourceFile, + std::string* error) { + bool res(false); int result = 0; - if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) { - return (result < 0); + { + std::lock_guard<std::mutex> lock(Mutex_); + res = cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result); } - if (error != nullptr) { - error->append( - "File modification time comparison failed for the files\n "); - error->append(cmQtAutoGen::Quoted(buildFile)); - error->append("\nand\n "); - error->append(cmQtAutoGen::Quoted(sourceFile)); + if (res) { + res = (result < 0); + } else { + if (error != nullptr) { + error->append( + "File modification time comparison failed for the files\n "); + error->append(Quoted(buildFile)); + error->append("\nand\n "); + error->append(Quoted(sourceFile)); + } } - return false; + return res; } -bool cmQtAutoGenerator::FileRead(std::string& content, - std::string const& filename, - std::string* error) +bool cmQtAutoGenerator::FileSystem::FileRead(std::string& content, + std::string const& filename, + std::string* error) { bool success = false; - if (cmSystemTools::FileExists(filename)) { - std::size_t const length = cmSystemTools::FileLength(filename); - cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); - if (ifs) { - content.resize(length); - ifs.read(&content.front(), content.size()); + { + std::lock_guard<std::mutex> lock(Mutex_); + if (cmSystemTools::FileExists(filename)) { + std::size_t const length = cmSystemTools::FileLength(filename); + cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); if (ifs) { - success = true; - } else { - content.clear(); - if (error != nullptr) { - error->append("Reading from the file failed."); + content.resize(length); + ifs.read(&content.front(), content.size()); + if (ifs) { + success = true; + } else { + content.clear(); + if (error != nullptr) { + error->append("Reading from the file failed."); + } } + } else if (error != nullptr) { + error->append("Opening the file for reading failed."); } } else if (error != nullptr) { - error->append("Opening the file for reading failed."); + error->append("The file does not exist."); } - } else if (error != nullptr) { - error->append("The file does not exist."); } return success; } -bool cmQtAutoGenerator::FileWrite(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& content) +bool cmQtAutoGenerator::FileSystem::FileRead(GeneratorT genType, + std::string& content, + std::string const& filename) { std::string error; + if (!FileRead(content, filename, &error)) { + Log()->ErrorFile(genType, filename, error); + return false; + } + return true; +} + +bool cmQtAutoGenerator::FileSystem::FileWrite(std::string const& filename, + std::string const& content, + std::string* error) +{ + bool success = false; // Make sure the parent directory exists - if (this->MakeParentDirectory(genType, filename)) { + if (MakeParentDirectory(filename)) { + std::lock_guard<std::mutex> lock(Mutex_); cmsys::ofstream outfile; outfile.open(filename.c_str(), (std::ios::out | std::ios::binary | std::ios::trunc)); if (outfile) { outfile << content; // Check for write errors - if (!outfile.good()) { - error = "File writing failed"; + if (outfile.good()) { + success = true; + } else { + if (error != nullptr) { + error->assign("File writing failed"); + } } } else { - error = "Opening file for writing failed"; + if (error != nullptr) { + error->assign("Opening file for writing failed"); + } + } + } else { + if (error != nullptr) { + error->assign("Could not create parent directory"); } } - if (!error.empty()) { - this->LogFileError(genType, filename, error); + return success; +} + +bool cmQtAutoGenerator::FileSystem::FileWrite(GeneratorT genType, + std::string const& filename, + std::string const& content) +{ + std::string error; + if (!FileWrite(filename, content, &error)) { + Log()->ErrorFile(genType, filename, error); return false; } return true; } -bool cmQtAutoGenerator::FileDiffers(std::string const& filename, - std::string const& content) +bool cmQtAutoGenerator::FileSystem::FileDiffers(std::string const& filename, + std::string const& content) { bool differs = true; { std::string oldContents; - if (this->FileRead(oldContents, filename)) { + if (FileRead(oldContents, filename)) { differs = (oldContents != content); } } return differs; } -/** - * @brief Runs a command and returns true on success - * @return True on success - */ -bool cmQtAutoGenerator::RunCommand(std::vector<std::string> const& command, - std::string& output) const -{ - // Log command - if (this->Verbose) { - std::string qcmd = QuotedCommand(command); - qcmd.push_back('\n'); - cmSystemTools::Stdout(qcmd.c_str(), qcmd.size()); - } - // Execute command - int retVal = 0; - bool res = cmSystemTools::RunSingleCommand( - command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE); - return (res && (retVal == 0)); +bool cmQtAutoGenerator::FileSystem::FileRemove(std::string const& filename) +{ + std::lock_guard<std::mutex> lock(Mutex_); + return cmSystemTools::RemoveFile(filename); +} + +bool cmQtAutoGenerator::FileSystem::Touch(std::string const& filename) +{ + std::lock_guard<std::mutex> lock(Mutex_); + return cmSystemTools::Touch(filename, false); +} + +bool cmQtAutoGenerator::FileSystem::MakeDirectory(std::string const& dirname) +{ + std::lock_guard<std::mutex> lock(Mutex_); + return cmSystemTools::MakeDirectory(dirname); +} + +bool cmQtAutoGenerator::FileSystem::MakeDirectory(GeneratorT genType, + std::string const& dirname) +{ + if (!MakeDirectory(dirname)) { + Log()->ErrorFile(genType, dirname, "Could not create directory"); + return false; + } + return true; +} + +bool cmQtAutoGenerator::FileSystem::MakeParentDirectory( + std::string const& filename) +{ + bool success = true; + std::string const dirName = cmSystemTools::GetFilenamePath(filename); + if (!dirName.empty()) { + success = MakeDirectory(dirName); + } + return success; +} + +bool cmQtAutoGenerator::FileSystem::MakeParentDirectory( + GeneratorT genType, std::string const& filename) +{ + if (!MakeParentDirectory(filename)) { + Log()->ErrorFile(genType, filename, "Could not create parent directory"); + return false; + } + return true; +} + +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_.front(); + 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; +} + +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_.front()); + UVOptions_.cwd = Setup_.WorkingDirectory.c_str(); + UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE; + UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size()); + UVOptions_.stdio = &UVOptionsStdIO_.front(); + + // -- Spawn process + if (UVProcess_.spawn(*uv_loop, UVOptions_, this) != 0) { + Result()->ErrorMessage = "libuv process spawn failed"; + } + } + // -- 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() + : FileSys_(&Logger_) +{ + // Initialize logger + Logger_.SetVerbose(cmSystemTools::HasEnv("VERBOSE")); + { + std::string colorEnv; + cmSystemTools::GetEnv("COLOR", colorEnv); + if (!colorEnv.empty()) { + Logger_.SetColorOutput(cmSystemTools::IsOn(colorEnv.c_str())); + } else { + Logger_.SetColorOutput(true); + } + } + + // 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()); +} + +cmQtAutoGenerator::~cmQtAutoGenerator() +{ + // Close libuv loop + uv_loop_close(UVLoop()); +} + +bool cmQtAutoGenerator::Run(std::string const& infoFile, + std::string const& config) +{ + // Info settings + InfoFile_ = infoFile; + cmSystemTools::ConvertToUnixSlashes(InfoFile_); + InfoDir_ = cmSystemTools::GetFilenamePath(infoFile); + InfoConfig_ = config; + + bool success = false; + { + cmake cm(cmake::RoleScript); + cm.SetHomeOutputDirectory(InfoDir()); + cm.SetHomeDirectory(InfoDir()); + cm.GetCurrentSnapshot().SetDefaultDefinitions(); + cmGlobalGenerator gg(&cm); + + cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); + snapshot.GetDirectory().SetCurrentBinary(InfoDir()); + snapshot.GetDirectory().SetCurrentSource(InfoDir()); + + auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot); + // The OLD/WARN behavior for policy CMP0053 caused a speed regression. + // https://gitlab.kitware.com/cmake/cmake/issues/17570 + makefile->SetPolicyVersion("3.9"); + gg.SetCurrentMakefile(makefile.get()); + success = this->Init(makefile.get()); + } + if (success) { + success = this->Process(); + } + return success; +} + +std::string cmQtAutoGenerator::SettingsFind(std::string const& content, + const char* key) +{ + std::string prefix(key); + prefix += ':'; + std::string::size_type pos = content.find(prefix); + if (pos != std::string::npos) { + pos += prefix.size(); + if (pos < content.size()) { + std::string::size_type posE = content.find('\n', pos); + if ((posE != std::string::npos) && (posE != pos)) { + return content.substr(pos, posE - pos); + } + } + } + return std::string(); } diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h index 285340d..6b35234 100644 --- a/Source/cmQtAutoGenerator.h +++ b/Source/cmQtAutoGenerator.h @@ -6,71 +6,283 @@ #include "cmConfigure.h" // IWYU pragma: keep #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> class cmMakefile; -class cmQtAutoGenerator +/// @brief Base class for QtAutoGen gernerators +class cmQtAutoGenerator : public cmQtAutoGen { CM_DISABLE_COPY(cmQtAutoGenerator) public: + // -- Types + + /// @brief Thread safe logging + class Logger + { + public: + // -- Verbosity + bool Verbose() const { return this->Verbose_; } + void SetVerbose(bool value); + bool ColorOutput() const { return this->ColorOutput_; } + void SetColorOutput(bool value); + // -- Log info + void Info(GeneratorT genType, std::string const& message); + // -- Log warning + void Warning(GeneratorT genType, std::string const& message); + void WarningFile(GeneratorT genType, std::string const& filename, + std::string const& message); + // -- Log error + void Error(GeneratorT genType, std::string const& message); + void ErrorFile(GeneratorT genType, std::string const& filename, + std::string const& message); + void ErrorCommand(GeneratorT genType, std::string const& message, + std::vector<std::string> const& command, + std::string const& output); + + private: + static std::string HeadLine(std::string const& title); + + private: + std::mutex Mutex_; + bool volatile Verbose_ = false; + bool volatile ColorOutput_ = false; + }; + + /// @brief Thread safe file system interface + class FileSystem + { + public: + FileSystem(Logger* log) + : Log_(log) + { + } + + Logger* Log() const { return Log_; } + std::string RealPath(std::string const& filename); + bool FileExists(std::string const& filename); + bool FileIsOlderThan(std::string const& buildFile, + std::string const& sourceFile, + std::string* error = nullptr); + + bool FileRead(std::string& content, std::string const& filename, + std::string* error = nullptr); + /// @brief Error logging version + bool FileRead(GeneratorT genType, std::string& content, + std::string const& filename); + + bool FileWrite(std::string const& filename, std::string const& content, + std::string* error = nullptr); + /// @brief Error logging version + bool FileWrite(GeneratorT genType, std::string const& filename, + std::string const& content); + + bool FileDiffers(std::string const& filename, std::string const& content); + + bool FileRemove(std::string const& filename); + bool Touch(std::string const& filename); + + bool MakeDirectory(std::string const& dirname); + /// @brief Error logging version + bool MakeDirectory(GeneratorT genType, std::string const& dirname); + + bool MakeParentDirectory(std::string const& filename); + /// @brief Error logging version + bool MakeParentDirectory(GeneratorT genType, std::string const& filename); + + private: + std::mutex Mutex_; + Logger* Log_; + }; + + /// @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; + }; + + // -- Constructor + ReadOnlyProcessT() = default; + + // -- 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_; + }; + +#if defined(CMAKE_USE_SYSTEM_LIBUV) && !defined(_WIN32) && \ + UV_VERSION_MAJOR == 1 && UV_VERSION_MINOR < 19 +#define CMAKE_UV_SIGNAL_HACK + /* + libuv does not use SA_RESTART on its signal handler, but C++ streams + depend on it for reliable i/o operations. This RAII helper convinces + libuv to install its handler, and then revises the handler to add the + SA_RESTART flag. We use a distinct uv loop that never runs to avoid + ever really getting a callback. libuv may fill the hack loop's signal + pipe and then stop writing, but that won't break any real loops. + */ + class cmUVSignalHackRAII + { + uv_loop_t HackLoop; + cm::uv_signal_ptr HackSignal; + static void HackCB(uv_signal_t*, int) {} + public: + cmUVSignalHackRAII() + { + uv_loop_init(&this->HackLoop); + this->HackSignal.init(this->HackLoop); + this->HackSignal.start(HackCB, SIGCHLD); + struct sigaction hack_sa; + sigaction(SIGCHLD, NULL, &hack_sa); + if (!(hack_sa.sa_flags & SA_RESTART)) { + hack_sa.sa_flags |= SA_RESTART; + sigaction(SIGCHLD, &hack_sa, NULL); + } + } + ~cmUVSignalHackRAII() + { + this->HackSignal.stop(); + uv_loop_close(&this->HackLoop); + } + }; +#endif + +public: + // -- Constructors cmQtAutoGenerator(); - virtual ~cmQtAutoGenerator() = default; + virtual ~cmQtAutoGenerator(); + + // -- Run bool Run(std::string const& infoFile, std::string const& config); - std::string const& GetInfoFile() const { return InfoFile; } - std::string const& GetInfoDir() const { return InfoDir; } - std::string const& GetInfoConfig() const { return InfoConfig; } - bool GetVerbose() const { return Verbose; } + // -- Accessors + // Logging + Logger& Log() { return Logger_; } + // File System + FileSystem& FileSys() { return FileSys_; } + // InfoFile + std::string const& InfoFile() const { return InfoFile_; } + std::string const& InfoDir() const { return InfoDir_; } + std::string const& InfoConfig() const { return InfoConfig_; } + // libuv loop + uv_loop_t* UVLoop() { return UVLoop_.get(); } + cm::uv_async_ptr& UVRequest() { return UVRequest_; } -protected: - // -- Central processing - virtual bool Process(cmMakefile* makefile) = 0; - - // -- Log info - void LogBold(std::string const& message) const; - void LogInfo(cmQtAutoGen::Generator genType, - std::string const& message) const; - // -- Log warning - void LogWarning(cmQtAutoGen::Generator genType, - std::string const& message) const; - void LogFileWarning(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const; - // -- Log error - void LogError(cmQtAutoGen::Generator genType, - std::string const& message) const; - void LogFileError(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const; - void LogCommandError(cmQtAutoGen::Generator genType, - std::string const& message, - std::vector<std::string> const& command, - std::string const& output) const; // -- Utility - bool MakeParentDirectory(cmQtAutoGen::Generator genType, - std::string const& filename) const; - bool FileIsOlderThan(std::string const& buildFile, - std::string const& sourceFile, - std::string* error = nullptr); - bool FileRead(std::string& content, std::string const& filename, - std::string* error = nullptr); - bool FileWrite(cmQtAutoGen::Generator genType, std::string const& filename, - std::string const& content); - bool FileDiffers(std::string const& filename, std::string const& content); - bool RunCommand(std::vector<std::string> const& command, - std::string& output) const; + static std::string SettingsFind(std::string const& content, const char* key); + +protected: + // -- Abstract processing interface + virtual bool Init(cmMakefile* makefile) = 0; + virtual bool Process() = 0; private: + // -- Logging + Logger Logger_; + FileSystem FileSys_; // -- Info settings - std::string InfoFile; - std::string InfoDir; - std::string InfoConfig; - // -- Settings - bool Verbose; - bool ColorOutput; + std::string InfoFile_; + std::string InfoDir_; + std::string InfoConfig_; +// -- 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_; }; #endif diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx index bce148e..4b02e0b 100644 --- a/Source/cmQtAutoGeneratorMocUic.cxx +++ b/Source/cmQtAutoGeneratorMocUic.cxx @@ -5,16 +5,15 @@ #include <algorithm> #include <array> +#include <functional> #include <list> #include <memory> #include <sstream> -#include <string.h> #include <utility> #include "cmAlgorithms.h" #include "cmCryptoHash.h" #include "cmMakefile.h" -#include "cmOutputConverter.h" #include "cmSystemTools.h" #include "cmake.h" @@ -22,51 +21,1126 @@ #include <unistd.h> #endif -// -- Static variables +// -- Class methods -static const char* SettingsKeyMoc = "AM_MOC_SETTINGS_HASH"; -static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH"; +std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath( + std::string const& relativePath) const +{ + return cmSystemTools::CollapseCombinedPath(AutogenBuildDir, relativePath); +} -// -- Static functions +/** + * @brief Tries to find the header file to the given file base path by + * appending different header extensions + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::BaseSettingsT::FindHeader( + std::string& header, std::string const& testBasePath) const +{ + for (std::string const& ext : HeaderExtensions) { + std::string testFilePath(testBasePath); + testFilePath.push_back('.'); + testFilePath += ext; + if (FileSys->FileExists(testFilePath)) { + header = testFilePath; + return true; + } + } + return false; +} -static std::string SubDirPrefix(std::string const& fileName) +bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped( + std::string const& fileName) const { - std::string res(cmSystemTools::GetFilenamePath(fileName)); - if (!res.empty()) { - res += '/'; + return (!Enabled || (SkipList.find(fileName) != SkipList.end())); +} + +/** + * @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 const& content) const +{ + for (KeyExpT const& filter : MacroFilters) { + // Run a simple find string operation before the expensive + // regular expression check + if (content.find(filter.Key) != std::string::npos) { + cmsys::RegularExpressionMatch match; + if (filter.Exp.find(content.c_str(), match)) { + // Return macro name on demand + return filter.Key; + } + } + } + return std::string(); +} + +std::string cmQtAutoGeneratorMocUic::MocSettingsT::MacrosString() const +{ + std::string res; + const auto itB = MacroFilters.cbegin(); + const auto itE = MacroFilters.cend(); + const auto itL = itE - 1; + auto itC = itB; + for (; itC != itE; ++itC) { + // Separator + if (itC != itB) { + if (itC != itL) { + res += ", "; + } else { + res += " or "; + } + } + // Key + res += itC->Key; } return res; } -static bool ListContains(std::vector<std::string> const& list, - std::string const& entry) +std::string cmQtAutoGeneratorMocUic::MocSettingsT::FindIncludedFile( + std::string const& sourcePath, std::string const& includeString) const +{ + // Search in vicinity of the source + { + std::string testPath = sourcePath; + testPath += includeString; + if (FileSys->FileExists(testPath)) { + return FileSys->RealPath(testPath); + } + } + // Search in include directories + for (std::string const& path : IncludePaths) { + std::string fullPath = path; + fullPath.push_back('/'); + fullPath += includeString; + if (FileSys->FileExists(fullPath)) { + return FileSys->RealPath(fullPath); + } + } + // Return empty string + return std::string(); +} + +void cmQtAutoGeneratorMocUic::MocSettingsT::FindDependencies( + std::string const& content, std::set<std::string>& depends) const { - return (std::find(list.begin(), list.end(), entry) != list.end()); + if (!DependFilters.empty() && !content.empty()) { + for (KeyExpT const& filter : DependFilters) { + // Run a simple find string check + if (content.find(filter.Key) != std::string::npos) { + // Run the expensive regular expression check loop + const char* contentChars = content.c_str(); + cmsys::RegularExpressionMatch match; + while (filter.Exp.find(contentChars, match)) { + { + std::string dep = match.match(1); + if (!dep.empty()) { + depends.emplace(std::move(dep)); + } + } + contentChars += match.end(); + } + } + } + } } -// -- Class methods +bool cmQtAutoGeneratorMocUic::UicSettingsT::skipped( + std::string const& fileName) const +{ + return (!Enabled || (SkipList.find(fileName) != SkipList.end())); +} + +void cmQtAutoGeneratorMocUic::JobParseT::Process(WorkerT& wrk) +{ + if (AutoMoc && Header) { + // Don't parse header for moc if the file is included by a source already + if (wrk.Gen().ParallelMocIncluded(FileName)) { + AutoMoc = false; + } + } + + if (AutoMoc || AutoUic) { + std::string error; + MetaT meta; + if (wrk.FileSys().FileRead(meta.Content, FileName, &error)) { + if (!meta.Content.empty()) { + meta.FileDir = SubDirPrefix(FileName); + meta.FileBase = + cmSystemTools::GetFilenameWithoutLastExtension(FileName); + + bool success = true; + if (AutoMoc) { + if (Header) { + success = ParseMocHeader(wrk, meta); + } else { + success = ParseMocSource(wrk, meta); + } + } + if (AutoUic && success) { + ParseUic(wrk, meta); + } + } else { + wrk.LogFileWarning(GeneratorT::GEN, FileName, + "The source file is empty"); + } + } else { + wrk.LogFileError(GeneratorT::GEN, FileName, + "Could not read the file: " + error); + } + } +} + +bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocSource(WorkerT& wrk, + MetaT const& meta) +{ + struct JobPre + { + bool self; // source file is self + bool underscore; // "moc_" style include + std::string SourceFile; + std::string IncludeString; + }; + + struct MocInclude + { + std::string Inc; // full include string + std::string Dir; // include string directory + std::string Base; // include string file base + }; + + // Check if this source file contains a relevant macro + std::string const ownMacro = wrk.Moc().FindMacro(meta.Content); + + // Extract moc includes from file + std::deque<MocInclude> mocIncsUsc; + std::deque<MocInclude> mocIncsDot; + { + 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)) { + std::string incString = match.match(1); + std::string incDir(SubDirPrefix(incString)); + std::string incBase = + cmSystemTools::GetFilenameWithoutLastExtension(incString); + if (cmHasLiteralPrefix(incBase, "moc_")) { + // moc_<BASE>.cxx + // Remove the moc_ part from the base name + mocIncsUsc.emplace_back(MocInclude{ + std::move(incString), std::move(incDir), incBase.substr(4) }); + } else { + // <BASE>.moc + mocIncsDot.emplace_back(MocInclude{ + std::move(incString), std::move(incDir), std::move(incBase) }); + } + // Forward content pointer + contentChars += match.end(); + } + } + } + + // Check if there is anything to do + if (ownMacro.empty() && mocIncsUsc.empty() && mocIncsDot.empty()) { + return true; + } + + bool ownDotMocIncluded = false; + bool ownMocUscIncluded = false; + std::deque<JobPre> jobs; + + // Process moc_<BASE>.cxx includes + for (const MocInclude& mocInc : mocIncsUsc) { + std::string const header = + MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base); + if (!header.empty()) { + // Check if header is skipped + if (wrk.Moc().skipped(header)) { + continue; + } + // Register moc job + const bool ownMoc = (mocInc.Base == meta.FileBase); + jobs.emplace_back(JobPre{ ownMoc, true, header, mocInc.Inc }); + // Store meta information for relaxed mode + if (ownMoc) { + ownMocUscIncluded = true; + } + } else { + { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", but the header "; + emsg += Quoted(MocStringHeaders(wrk, mocInc.Base)); + emsg += " could not be found."; + wrk.LogFileError(GeneratorT::MOC, FileName, emsg); + } + return false; + } + } + + // Process <BASE>.moc includes + for (const MocInclude& mocInc : mocIncsDot) { + const bool ownMoc = (mocInc.Base == meta.FileBase); + if (wrk.Moc().RelaxedMode) { + // Relaxed mode + if (!ownMacro.empty() && ownMoc) { + // Add self + jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc }); + ownDotMocIncluded = true; + } else { + // In relaxed mode try to find a header instead but issue a warning. + // This is for KDE4 compatibility + std::string const header = + MocFindIncludedHeader(wrk, meta.FileDir, mocInc.Dir + mocInc.Base); + if (!header.empty()) { + // Check if header is skipped + if (wrk.Moc().skipped(header)) { + continue; + } + // Register moc job + jobs.emplace_back(JobPre{ ownMoc, false, header, mocInc.Inc }); + if (ownMacro.empty()) { + if (ownMoc) { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", but does not contain a "; + emsg += wrk.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(GeneratorT::MOC, FileName, emsg); + } else { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += " instead of "; + emsg += Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += ".\nRunning moc on\n "; + emsg += Quoted(header); + emsg += "!\nBetter include "; + emsg += Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += " for compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; + wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg); + } + } + } else { + { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", which seems to be the moc file from a different " + "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a " + "matching header "; + emsg += Quoted(MocStringHeaders(wrk, mocInc.Base)); + emsg += " could not be found."; + wrk.LogFileError(GeneratorT::MOC, FileName, emsg); + } + return false; + } + } + } else { + // Strict mode + if (ownMoc) { + // Include self + jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc }); + ownDotMocIncluded = true; + // Accept but issue a warning if moc isn't required + if (ownMacro.empty()) { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", but does not contain a "; + emsg += wrk.Moc().MacrosString(); + emsg += " macro."; + wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg); + } + } else { + // Don't allow <BASE>.moc include other than self in strict mode + { + std::string emsg = "The file includes the moc file "; + emsg += Quoted(mocInc.Inc); + emsg += ", which seems to be the moc file from a different " + "source file.\nThis is not supported. Include "; + emsg += Quoted(meta.FileBase + ".moc"); + emsg += " to run moc on this source file."; + wrk.LogFileError(GeneratorT::MOC, FileName, emsg); + } + return false; + } + } + } + + if (!ownMacro.empty() && !ownDotMocIncluded) { + // In this case, check whether the scanned file itself contains a + // Q_OBJECT. + // If this is the case, the moc_foo.cpp should probably be generated from + // foo.cpp instead of foo.h, because otherwise it won't build. + // But warn, since this is not how it is supposed to be used. + // This is for KDE4 compatibility. + if (wrk.Moc().RelaxedMode && ownMocUscIncluded) { + JobPre uscJobPre; + // Remove underscore job request + { + auto itC = jobs.begin(); + auto itE = jobs.end(); + for (; itC != itE; ++itC) { + JobPre& job(*itC); + if (job.self && job.underscore) { + uscJobPre = std::move(job); + jobs.erase(itC); + break; + } + } + } + // Issue a warning + { + std::string emsg = "The file contains a "; + emsg += ownMacro; + emsg += " macro, but does not include "; + emsg += Quoted(meta.FileBase + ".moc"); + emsg += ". Instead it includes "; + emsg += Quoted(uscJobPre.IncludeString); + emsg += ".\nRunning moc on\n "; + emsg += Quoted(FileName); + emsg += "!\nBetter include "; + emsg += Quoted(meta.FileBase + ".moc"); + emsg += " for compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; + wrk.LogFileWarning(GeneratorT::MOC, FileName, emsg); + } + // Add own source job + jobs.emplace_back( + JobPre{ true, false, FileName, uscJobPre.IncludeString }); + } else { + // Otherwise always error out since it will not compile. + { + std::string emsg = "The file contains a "; + emsg += ownMacro; + emsg += " macro, but does not include "; + emsg += Quoted(meta.FileBase + ".moc"); + emsg += "!\nConsider to\n - add #include \""; + emsg += meta.FileBase; + emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; + wrk.LogFileError(GeneratorT::MOC, FileName, emsg); + } + return false; + } + } + + // Convert pre jobs to actual jobs + for (JobPre& jobPre : jobs) { + JobHandleT jobHandle(new JobMocT(std::move(jobPre.SourceFile), FileName, + std::move(jobPre.IncludeString))); + if (jobPre.self) { + // Read depdendencies from this source + static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content); + } + if (!wrk.Gen().ParallelJobPushMoc(jobHandle)) { + return false; + } + } + return true; +} + +bool cmQtAutoGeneratorMocUic::JobParseT::ParseMocHeader(WorkerT& wrk, + MetaT const& meta) +{ + bool success = true; + std::string const macroName = wrk.Moc().FindMacro(meta.Content); + if (!macroName.empty()) { + JobHandleT jobHandle( + new JobMocT(std::string(FileName), std::string(), std::string())); + // Read depdendencies from this source + static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content); + success = wrk.Gen().ParallelJobPushMoc(jobHandle); + } + return success; +} + +std::string cmQtAutoGeneratorMocUic::JobParseT::MocStringHeaders( + WorkerT& wrk, std::string const& fileBase) const +{ + std::string res = fileBase; + res += ".{"; + res += cmJoin(wrk.Base().HeaderExtensions, ","); + res += "}"; + return res; +} + +std::string cmQtAutoGeneratorMocUic::JobParseT::MocFindIncludedHeader( + WorkerT& wrk, std::string const& includerDir, std::string const& includeBase) +{ + std::string header; + // Search in vicinity of the source + if (!wrk.Base().FindHeader(header, includerDir + includeBase)) { + // Search in include directories + for (std::string const& path : wrk.Moc().IncludePaths) { + std::string fullPath = path; + fullPath.push_back('/'); + fullPath += includeBase; + if (wrk.Base().FindHeader(header, fullPath)) { + break; + } + } + } + // Sanitize + if (!header.empty()) { + header = wrk.FileSys().RealPath(header); + } + return header; +} + +bool cmQtAutoGeneratorMocUic::JobParseT::ParseUic(WorkerT& wrk, + 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(1))) { + success = false; + break; + } + contentChars += match.end(); + } + } + return success; +} + +bool cmQtAutoGeneratorMocUic::JobParseT::ParseUicInclude( + WorkerT& wrk, MetaT const& meta, std::string&& includeString) +{ + bool success = false; + std::string uiInputFile = UicFindIncludedFile(wrk, meta, includeString); + if (!uiInputFile.empty()) { + if (!wrk.Uic().skipped(uiInputFile)) { + JobHandleT jobHandle(new JobUicT(std::move(uiInputFile), FileName, + std::move(includeString))); + success = wrk.Gen().ParallelJobPushUic(jobHandle); + } else { + // A skipped file is successful + success = true; + } + } + return success; +} + +std::string cmQtAutoGeneratorMocUic::JobParseT::UicFindIncludedFile( + WorkerT& wrk, MetaT const& meta, std::string const& includeString) +{ + std::string res; + std::string searchFile = + cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3); + searchFile += ".ui"; + // Collect search paths list + std::deque<std::string> testFiles; + { + std::string const searchPath = SubDirPrefix(includeString); + + std::string searchFileFull; + if (!searchPath.empty()) { + searchFileFull = searchPath; + searchFileFull += searchFile; + } + // Vicinity of the source + { + std::string const sourcePath = meta.FileDir; + testFiles.push_back(sourcePath + searchFile); + if (!searchPath.empty()) { + testFiles.push_back(sourcePath + searchFileFull); + } + } + // AUTOUIC search paths + if (!wrk.Uic().SearchPaths.empty()) { + for (std::string const& sPath : wrk.Uic().SearchPaths) { + testFiles.push_back((sPath + "/").append(searchFile)); + } + if (!searchPath.empty()) { + for (std::string const& sPath : wrk.Uic().SearchPaths) { + testFiles.push_back((sPath + "/").append(searchFileFull)); + } + } + } + } + + // Search for the .ui file! + for (std::string const& testFile : testFiles) { + if (wrk.FileSys().FileExists(testFile)) { + res = wrk.FileSys().RealPath(testFile); + break; + } + } + + // Log error + if (res.empty()) { + std::string emsg = "Could not find "; + emsg += Quoted(searchFile); + emsg += " in\n"; + for (std::string const& testFile : testFiles) { + emsg += " "; + emsg += Quoted(testFile); + emsg += "\n"; + } + wrk.LogFileError(GeneratorT::UIC, FileName, emsg); + } + + return res; +} + +void cmQtAutoGeneratorMocUic::JobMocPredefsT::Process(WorkerT& wrk) +{ + // (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(GeneratorT::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(GeneratorT::MOC, reason); + } + generate = true; + } + 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(GeneratorT::MOC, result, cmd)) { + std::string emsg = "The content generation command for "; + emsg += Quoted(wrk.Moc().PredefsFileRel); + emsg += " failed.\n"; + emsg += result.ErrorMessage; + wrk.LogCommandError(GeneratorT::MOC, emsg, cmd, result.StdOut); + } + } + + // (Re)write predefs file only on demand + if (!result.error()) { + if (!fileExists || + wrk.FileSys().FileDiffers(wrk.Moc().PredefsFileAbs, result.StdOut)) { + if (wrk.FileSys().FileWrite(GeneratorT::MOC, wrk.Moc().PredefsFileAbs, + result.StdOut)) { + // Success + } else { + std::string emsg = "Writing "; + emsg += Quoted(wrk.Moc().PredefsFileRel); + emsg += " failed."; + wrk.LogFileError(GeneratorT::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(GeneratorT::MOC, msg); + } + wrk.FileSys().Touch(wrk.Moc().PredefsFileAbs); + } + } + } +} + +void cmQtAutoGeneratorMocUic::JobMocT::FindDependencies( + WorkerT& wrk, std::string const& content) +{ + wrk.Moc().FindDependencies(content, Depends); + DependsValid = true; +} + +void cmQtAutoGeneratorMocUic::JobMocT::Process(WorkerT& wrk) +{ + // Compute build file name + if (!IncludeString.empty()) { + BuildFile = wrk.Base().AutogenIncludeDirAbs; + BuildFile += IncludeString; + } else { + std::string buildRel = wrk.Base().FilePathChecksum.getPart(SourceFile); + buildRel += '/'; + buildRel += "moc_"; + buildRel += cmSystemTools::GetFilenameWithoutLastExtension(SourceFile); + if (wrk.Base().MultiConfig != MultiConfigT::SINGLE) { + buildRel += wrk.Base().ConfigSuffix; + } + buildRel += ".cpp"; + wrk.Gen().ParallelMocAutoRegister(buildRel); + BuildFile = wrk.Base().AbsoluteBuildPath(buildRel); + } + + if (UpdateRequired(wrk)) { + GenerateMoc(wrk); + } +} + +bool cmQtAutoGeneratorMocUic::JobMocT::UpdateRequired(WorkerT& wrk) +{ + bool const verbose = wrk.Gen().Log().Verbose(); + + // Test if the build file exists + if (!wrk.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(GeneratorT::MOC, reason); + } + return true; + } + + // Test if any setting changed + if (wrk.Moc().SettingsChanged) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from "; + reason += Quoted(SourceFile); + reason += " because the MOC settings changed"; + wrk.LogInfo(GeneratorT::MOC, reason); + } + return true; + } + + // Test if the moc_predefs file is newer + if (!wrk.Moc().PredefsFileAbs.empty()) { + bool isOlder = false; + { + std::string error; + isOlder = wrk.FileSys().FileIsOlderThan( + BuildFile, wrk.Moc().PredefsFileAbs, &error); + if (!isOlder && !error.empty()) { + wrk.LogError(GeneratorT::MOC, error); + return false; + } + } + if (isOlder) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " because it's older than: "; + reason += Quoted(wrk.Moc().PredefsFileAbs); + wrk.LogInfo(GeneratorT::MOC, reason); + } + return true; + } + } + + // Test if the source file is newer + { + bool isOlder = false; + { + std::string error; + isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); + if (!isOlder && !error.empty()) { + wrk.LogError(GeneratorT::MOC, error); + return false; + } + } + if (isOlder) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " because it's older than its source file "; + reason += Quoted(SourceFile); + wrk.LogInfo(GeneratorT::MOC, reason); + } + return true; + } + } + + // Test if a dependency file is newer + { + // Read dependencies on demand + if (!DependsValid) { + std::string content; + { + std::string error; + if (!wrk.FileSys().FileRead(content, SourceFile, &error)) { + std::string emsg = "Could not read file\n "; + emsg += Quoted(SourceFile); + emsg += "\nrequired by moc include "; + emsg += Quoted(IncludeString); + emsg += " in\n "; + emsg += Quoted(IncluderFile); + emsg += ".\n"; + emsg += error; + wrk.LogError(GeneratorT::MOC, emsg); + return false; + } + } + FindDependencies(wrk, content); + } + // Check dependency timestamps + std::string error; + std::string sourceDir = SubDirPrefix(SourceFile); + for (std::string const& depFileRel : Depends) { + std::string depFileAbs = + wrk.Moc().FindIncludedFile(sourceDir, depFileRel); + if (!depFileAbs.empty()) { + if (wrk.FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from "; + reason += Quoted(SourceFile); + reason += " because it is older than it's dependency file "; + reason += Quoted(depFileAbs); + wrk.LogInfo(GeneratorT::MOC, reason); + } + return true; + } + if (!error.empty()) { + wrk.LogError(GeneratorT::MOC, error); + return false; + } + } else { + std::string message = "Could not find dependency file "; + message += Quoted(depFileRel); + wrk.LogFileWarning(GeneratorT::MOC, SourceFile, message); + } + } + } + + return false; +} + +void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk) +{ + // Make sure the parent directory exists + if (wrk.FileSys().MakeParentDirectory(GeneratorT::MOC, BuildFile)) { + // Compose moc command + std::vector<std::string> cmd; + cmd.push_back(wrk.Moc().Executable); + // Add options + cmd.insert(cmd.end(), wrk.Moc().AllOptions.begin(), + wrk.Moc().AllOptions.end()); + // Add predefs include + if (!wrk.Moc().PredefsFileAbs.empty()) { + cmd.push_back("--include"); + cmd.push_back(wrk.Moc().PredefsFileAbs); + } + cmd.push_back("-o"); + cmd.push_back(BuildFile); + cmd.push_back(SourceFile); + + // Execute moc command + ProcessResultT result; + if (wrk.RunProcess(GeneratorT::MOC, result, cmd)) { + // Moc command success + if (IncludeString.empty()) { + // Notify the generator that a not included file changed + wrk.Gen().ParallelMocAutoUpdated(); + } + } else { + // Moc command failed + { + std::string emsg = "The moc process failed to compile\n "; + emsg += Quoted(SourceFile); + emsg += "\ninto\n "; + emsg += Quoted(BuildFile); + emsg += ".\n"; + emsg += result.ErrorMessage; + wrk.LogCommandError(GeneratorT::MOC, emsg, cmd, result.StdOut); + } + wrk.FileSys().FileRemove(BuildFile); + } + } +} + +void cmQtAutoGeneratorMocUic::JobUicT::Process(WorkerT& wrk) +{ + // Compute build file name + BuildFile = wrk.Base().AutogenIncludeDirAbs; + BuildFile += IncludeString; + + if (UpdateRequired(wrk)) { + GenerateUic(wrk); + } +} + +bool cmQtAutoGeneratorMocUic::JobUicT::UpdateRequired(WorkerT& wrk) +{ + bool const verbose = wrk.Gen().Log().Verbose(); + + // Test if the build file exists + if (!wrk.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(GeneratorT::UIC, reason); + } + return true; + } + + // Test if the uic settings changed + if (wrk.Uic().SettingsChanged) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " from "; + reason += Quoted(SourceFile); + reason += " because the UIC settings changed"; + wrk.LogInfo(GeneratorT::UIC, reason); + } + return true; + } + + // Test if the source file is newer + { + bool isOlder = false; + { + std::string error; + isOlder = wrk.FileSys().FileIsOlderThan(BuildFile, SourceFile, &error); + if (!isOlder && !error.empty()) { + wrk.LogError(GeneratorT::UIC, error); + return false; + } + } + if (isOlder) { + if (verbose) { + std::string reason = "Generating "; + reason += Quoted(BuildFile); + reason += " because it's older than its source file "; + reason += Quoted(SourceFile); + wrk.LogInfo(GeneratorT::UIC, reason); + } + return true; + } + } + + return false; +} + +void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk) +{ + // Make sure the parent directory exists + if (wrk.FileSys().MakeParentDirectory(GeneratorT::UIC, BuildFile)) { + // Compose uic command + std::vector<std::string> cmd; + cmd.push_back(wrk.Uic().Executable); + { + std::vector<std::string> allOpts = wrk.Uic().TargetOptions; + auto optionIt = wrk.Uic().Options.find(SourceFile); + if (optionIt != wrk.Uic().Options.end()) { + UicMergeOptions(allOpts, optionIt->second, + (wrk.Base().QtVersionMajor == 5)); + } + cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); + } + cmd.push_back("-o"); + cmd.push_back(BuildFile); + cmd.push_back(SourceFile); + + ProcessResultT result; + if (wrk.RunProcess(GeneratorT::UIC, result, cmd)) { + // Success + } else { + // Command failed + { + std::string emsg = "The uic process failed to compile\n "; + emsg += Quoted(SourceFile); + emsg += "\ninto\n "; + emsg += Quoted(BuildFile); + emsg += "\nincluded by\n "; + emsg += Quoted(IncluderFile); + emsg += ".\n"; + emsg += result.ErrorMessage; + wrk.LogCommandError(GeneratorT::UIC, emsg, cmd, result.StdOut); + } + wrk.FileSys().FileRemove(BuildFile); + } + } +} + +void cmQtAutoGeneratorMocUic::JobDeleterT::operator()(JobT* job) +{ + delete job; +} + +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( + GeneratorT genType, std::string const& message) const +{ + return Log().Info(genType, message); +} + +void cmQtAutoGeneratorMocUic::WorkerT::LogWarning( + GeneratorT genType, std::string const& message) const +{ + return Log().Warning(genType, message); +} + +void cmQtAutoGeneratorMocUic::WorkerT::LogFileWarning( + GeneratorT genType, std::string const& filename, + std::string const& message) const +{ + return Log().WarningFile(genType, filename, message); +} + +void cmQtAutoGeneratorMocUic::WorkerT::LogError( + GeneratorT genType, std::string const& message) const +{ + Gen().ParallelRegisterJobError(); + Log().Error(genType, message); +} + +void cmQtAutoGeneratorMocUic::WorkerT::LogFileError( + GeneratorT genType, std::string const& filename, + std::string const& message) const +{ + Gen().ParallelRegisterJobError(); + Log().ErrorFile(genType, filename, message); +} + +void cmQtAutoGeneratorMocUic::WorkerT::LogCommandError( + GeneratorT 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( + GeneratorT 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, + std::bind(&WorkerT::UVProcessFinished, &wrk)); + } + } +} + +void cmQtAutoGeneratorMocUic::WorkerT::UVProcessFinished() +{ + { + std::lock_guard<std::mutex> lock(ProcessMutex_); + if (Process_ && Process_->IsFinished()) { + Process_.reset(); + } + } + // Notify idling thread + ProcessCondition_.notify_one(); +} cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic() - : MultiConfig(cmQtAutoGen::WRAP) - , IncludeProjectDirsBefore(false) - , QtVersionMajor(4) - , MocSettingsChanged(false) - , MocPredefsChanged(false) - , MocRelaxedMode(false) - , UicSettingsChanged(false) + : Base_(&FileSys()) + , Moc_(&FileSys()) + , Stage_(StageT::SETTINGS_READ) + , JobsRemain_(0) + , JobError_(false) + , JobThreadsAbort_(false) + , MocAutoFileUpdated_(false) { // Precompile regular expressions - this->MocRegExpInclude.compile( + Moc_.RegExpInclude.compile( "[\n][ \t]*#[ \t]*include[ \t]+" "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); - this->UicRegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); + Uic_.RegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); + + // Initialize libuv asynchronous iteration request + UVRequest().init(*UVLoop(), &cmQtAutoGeneratorMocUic::UVPollStage, this); } -bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) +cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic() +{ +} + +bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile) { // -- Meta - this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); + Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); // Utility lambdas auto InfoGet = [makefile](const char* key) { @@ -87,7 +1161,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) std::string const value = makefile->GetSafeDefinition(key); std::string::size_type pos = 0; while (pos < value.size()) { - std::string::size_type next = value.find(cmQtAutoGen::listSep, pos); + std::string::size_type next = value.find(ListSep, pos); std::string::size_type length = (next != std::string::npos) ? next - pos : value.size() - pos; // Remove enclosing braces @@ -102,7 +1176,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) } } pos += length; - pos += cmQtAutoGen::listSep.size(); + pos += ListSep.size(); } } return lists; @@ -112,7 +1186,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) { std::string keyConf = key; keyConf += '_'; - keyConf += this->GetInfoConfig(); + keyConf += InfoConfig(); valueConf = makefile->GetDefinition(keyConf); } if (valueConf == nullptr) { @@ -128,122 +1202,177 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) }; // -- Read info file - if (!makefile->ReadListFile(this->GetInfoFile().c_str())) { - this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(), - "File processing failed"); + if (!makefile->ReadListFile(InfoFile().c_str())) { + Log().ErrorFile(GeneratorT::GEN, InfoFile(), "File processing failed"); return false; } // -- Meta - this->MultiConfig = cmQtAutoGen::MultiConfigType(InfoGet("AM_MULTI_CONFIG")); - this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX"); - if (this->ConfigSuffix.empty()) { - this->ConfigSuffix = "_"; - this->ConfigSuffix += this->GetInfoConfig(); + Base_.MultiConfig = MultiConfigType(InfoGet("AM_MULTI_CONFIG")); + + Base_.ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX"); + if (Base_.ConfigSuffix.empty()) { + Base_.ConfigSuffix = "_"; + Base_.ConfigSuffix += InfoConfig(); } - this->SettingsFile = InfoGetConfig("AM_SETTINGS_FILE"); - if (this->SettingsFile.empty()) { - this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(), - "Settings file name missing"); + SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE"); + if (SettingsFile_.empty()) { + Log().ErrorFile(GeneratorT::GEN, InfoFile(), "Settings file name missing"); return false; } + { + unsigned long num = Base_.NumThreads; + if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL"), &num)) { + num = std::max<unsigned long>(num, 1); + num = std::min<unsigned long>(num, ParallelMax); + Base_.NumThreads = static_cast<unsigned int>(num); + } + } + // - Files and directories - this->ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); - this->ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); - this->CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); - this->CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); - this->IncludeProjectDirsBefore = + Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); + Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); + Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); + Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); + Base_.IncludeProjectDirsBefore = InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); - this->AutogenBuildDir = InfoGet("AM_BUILD_DIR"); - if (this->AutogenBuildDir.empty()) { - this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(), - "Autogen build directory missing"); + Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR"); + if (Base_.AutogenBuildDir.empty()) { + Log().ErrorFile(GeneratorT::GEN, InfoFile(), + "Autogen build directory missing"); return false; } // - Qt environment - if (!cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR"), - &this->QtVersionMajor)) { - this->QtVersionMajor = 4; + { + unsigned long qtv = Base_.QtVersionMajor; + if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR"), &qtv)) { + Base_.QtVersionMajor = static_cast<unsigned int>(qtv); + } } - this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE"); - this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE"); // - Moc - if (this->MocEnabled()) { - this->MocSkipList = InfoGetList("AM_MOC_SKIP"); - this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); + Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE"); + Moc_.Enabled = !Moc().Executable.empty(); + if (Moc().Enabled) { + { + auto lst = InfoGetList("AM_MOC_SKIP"); + Moc_.SkipList.insert(lst.begin(), lst.end()); + } + Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); #ifdef _WIN32 { - std::string const win32("WIN32"); - if (!ListContains(this->MocDefinitions, win32)) { - this->MocDefinitions.push_back(win32); + std::string win32("WIN32"); + auto itB = Moc().Definitions.cbegin(); + auto itE = Moc().Definitions.cend(); + if (std::find(itB, itE, win32) == itE) { + Moc_.Definitions.emplace_back(std::move(win32)); } } #endif - this->MocIncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); - this->MocOptions = InfoGetList("AM_MOC_OPTIONS"); - this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); - { - std::vector<std::string> const MocMacroNames = - InfoGetList("AM_MOC_MACRO_NAMES"); - for (std::string const& item : MocMacroNames) { - this->MocMacroFilters.emplace_back( - item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); - } + Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); + Moc_.Options = InfoGetList("AM_MOC_OPTIONS"); + Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); + for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) { + Moc_.MacroFilters.emplace_back( + item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); } { - std::vector<std::string> const mocDependFilters = - InfoGetList("AM_MOC_DEPEND_FILTERS"); - // Insert Q_PLUGIN_METADATA dependency filter - if (this->QtVersionMajor != 4) { - this->MocDependFilterPush("Q_PLUGIN_METADATA", - "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" - "[^\\)]*FILE[ \t]*\"([^\"]+)\""); + auto pushFilter = [this](std::string const& key, std::string const& exp, + std::string& error) { + if (!key.empty()) { + if (!exp.empty()) { + Moc_.DependFilters.push_back(KeyExpT()); + KeyExpT& filter(Moc_.DependFilters.back()); + if (filter.Exp.compile(exp)) { + filter.Key = key; + } else { + error = "Regular expression compiling failed"; + } + } else { + error = "Regular expression is empty"; + } + } else { + error = "Key is empty"; + } + if (!error.empty()) { + error = ("AUTOMOC_DEPEND_FILTERS: " + error); + error += "\n"; + error += " Key: "; + error += Quoted(key); + error += "\n"; + error += " Exp: "; + error += Quoted(exp); + error += "\n"; + } + }; + + std::string error; + // Insert default filter for Q_PLUGIN_METADATA + if (Base().QtVersionMajor != 4) { + pushFilter("Q_PLUGIN_METADATA", "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" + "[^\\)]*FILE[ \t]*\"([^\"]+)\"", + error); } // Insert user defined dependency filters - if ((mocDependFilters.size() % 2) == 0) { - for (std::vector<std::string>::const_iterator - dit = mocDependFilters.begin(), - ditEnd = mocDependFilters.end(); - dit != ditEnd; dit += 2) { - if (!this->MocDependFilterPush(*dit, *(dit + 1))) { - return false; + { + std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS"); + if ((flts.size() % 2) == 0) { + for (std::vector<std::string>::iterator itC = flts.begin(), + itE = flts.end(); + itC != itE; itC += 2) { + pushFilter(*itC, *(itC + 1), error); + if (!error.empty()) { + break; + } } + } else { + Log().ErrorFile( + GeneratorT::MOC, InfoFile(), + "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); + return false; } - } else { - this->LogFileError( - cmQtAutoGen::MOC, this->GetInfoFile(), - "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); + } + if (!error.empty()) { + Log().ErrorFile(GeneratorT::MOC, InfoFile(), error); return false; } } - this->MocPredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); + Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); + // Install moc predefs job + if (!Moc().PredefsCmd.empty()) { + JobQueues_.MocPredefs.emplace_back(new JobMocPredefsT()); + } } // - Uic - if (this->UicEnabled()) { - this->UicSkipList = InfoGetList("AM_UIC_SKIP"); - this->UicSearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); - this->UicTargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); + Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE"); + Uic_.Enabled = !Uic().Executable.empty(); + if (Uic().Enabled) { + { + auto lst = InfoGetList("AM_UIC_SKIP"); + Uic_.SkipList.insert(lst.begin(), lst.end()); + } + Uic_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); + Uic_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); { auto sources = InfoGetList("AM_UIC_OPTIONS_FILES"); auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS"); // Compare list sizes if (sources.size() != options.size()) { std::ostringstream ost; - ost << "files/options lists sizes mismatch (" << sources.size() << "/" + ost << "files/options lists sizes missmatch (" << sources.size() << "/" << options.size() << ")"; - this->LogFileError(cmQtAutoGen::UIC, this->GetInfoFile(), ost.str()); + Log().ErrorFile(GeneratorT::UIC, InfoFile(), ost.str()); return false; } auto fitEnd = sources.cend(); auto fit = sources.begin(); auto oit = options.begin(); while (fit != fitEnd) { - this->UicOptions[*fit] = std::move(*oit); + Uic_.Options[*fit] = std::move(*oit); ++fit; ++oit; } @@ -252,54 +1381,51 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) // Initialize source file jobs { - // Utility lambdas - auto AddJob = [this](std::map<std::string, SourceJob>& jobs, - std::string&& sourceFile) { - const bool moc = !this->MocSkip(sourceFile); - const bool uic = !this->UicSkip(sourceFile); - if (moc || uic) { - SourceJob& job = jobs[std::move(sourceFile)]; - job.Moc = moc; - job.Uic = uic; - } - }; + std::hash<std::string> stringHash; + std::set<std::size_t> uniqueHeaders; // Add header jobs for (std::string& hdr : InfoGetList("AM_HEADERS")) { - AddJob(this->HeaderJobs, std::move(hdr)); + const bool moc = !Moc().skipped(hdr); + const bool uic = !Uic().skipped(hdr); + if ((moc || uic) && uniqueHeaders.emplace(stringHash(hdr)).second) { + JobQueues_.Headers.emplace_back( + new JobParseT(std::move(hdr), moc, uic, true)); + } } // Add source jobs { std::vector<std::string> sources = InfoGetList("AM_SOURCES"); // Add header(s) for the source file - for (std::string const& src : sources) { - const bool srcMoc = !this->MocSkip(src); - const bool srcUic = !this->UicSkip(src); + for (std::string& src : sources) { + const bool srcMoc = !Moc().skipped(src); + const bool srcUic = !Uic().skipped(src); if (!srcMoc && !srcUic) { continue; } // Search for the default header file and a private header - std::array<std::string, 2> headerBases; - headerBases[0] = SubDirPrefix(src); - headerBases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src); - headerBases[1] = headerBases[0]; - headerBases[1] += "_p"; - for (std::string const& headerBase : headerBases) { - std::string header; - if (this->FindHeader(header, headerBase)) { - const bool moc = srcMoc && !this->MocSkip(header); - const bool uic = srcUic && !this->UicSkip(header); - if (moc || uic) { - SourceJob& job = this->HeaderJobs[std::move(header)]; - job.Moc = moc; - job.Uic = uic; + { + std::array<std::string, 2> bases; + bases[0] = SubDirPrefix(src); + bases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src); + bases[1] = bases[0]; + bases[1] += "_p"; + for (std::string const& headerBase : bases) { + std::string header; + if (Base().FindHeader(header, headerBase)) { + const bool moc = srcMoc && !Moc().skipped(header); + const bool uic = srcUic && !Uic().skipped(header); + if ((moc || uic) && + uniqueHeaders.emplace(stringHash(header)).second) { + JobQueues_.Headers.emplace_back( + new JobParseT(std::move(header), moc, uic, true)); + } } } } - } - // Add Source jobs - for (std::string& src : sources) { - AddJob(this->SourceJobs, std::move(src)); + // Add source job + JobQueues_.Sources.emplace_back( + new JobParseT(std::move(src), srcMoc, srcUic)); } } } @@ -308,58 +1434,58 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) // ------------------------ // Init file path checksum generator - this->FilePathChecksum.setupParentDirs( - this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir, - this->ProjectBinaryDir); + Base_.FilePathChecksum.setupParentDirs( + Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir, + Base().ProjectBinaryDir); // include directory - this->AutogenIncludeDir = "include"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - this->AutogenIncludeDir += this->ConfigSuffix; + Base_.AutogenIncludeDirRel = "include"; + if (Base().MultiConfig != MultiConfigT::SINGLE) { + Base_.AutogenIncludeDirRel += Base().ConfigSuffix; } - this->AutogenIncludeDir += "/"; + Base_.AutogenIncludeDirRel += "/"; + Base_.AutogenIncludeDirAbs = + Base_.AbsoluteBuildPath(Base().AutogenIncludeDirRel); // Moc variables - if (this->MocEnabled()) { + if (Moc().Enabled) { // Mocs compilation file - this->MocCompFileRel = "mocs_compilation"; - if (this->MultiConfig == cmQtAutoGen::FULL) { - this->MocCompFileRel += this->ConfigSuffix; + Moc_.CompFileRel = "mocs_compilation"; + if (Base_.MultiConfig == MultiConfigT::MULTI) { + Moc_.CompFileRel += Base().ConfigSuffix; } - this->MocCompFileRel += ".cpp"; - this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->MocCompFileRel); + Moc_.CompFileRel += ".cpp"; + Moc_.CompFileAbs = Base_.AbsoluteBuildPath(Moc().CompFileRel); // Moc predefs file - if (!this->MocPredefsCmd.empty()) { - this->MocPredefsFileRel = "moc_predefs"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - this->MocPredefsFileRel += this->ConfigSuffix; + if (!Moc_.PredefsCmd.empty()) { + Moc_.PredefsFileRel = "moc_predefs"; + if (Base_.MultiConfig != MultiConfigT::SINGLE) { + Moc_.PredefsFileRel += Base().ConfigSuffix; } - this->MocPredefsFileRel += ".h"; - this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->MocPredefsFileRel); + Moc_.PredefsFileRel += ".h"; + Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel); } // Sort include directories on demand - if (this->IncludeProjectDirsBefore) { + if (Base().IncludeProjectDirsBefore) { // Move strings to temporary list std::list<std::string> includes; - includes.insert(includes.end(), this->MocIncludePaths.begin(), - this->MocIncludePaths.end()); - this->MocIncludePaths.clear(); - this->MocIncludePaths.reserve(includes.size()); + includes.insert(includes.end(), Moc().IncludePaths.begin(), + Moc().IncludePaths.end()); + Moc_.IncludePaths.clear(); + Moc_.IncludePaths.reserve(includes.size()); // Append project directories only { std::array<std::string const*, 2> const movePaths = { - { &this->ProjectBinaryDir, &this->ProjectSourceDir } + { &Base().ProjectBinaryDir, &Base().ProjectSourceDir } }; for (std::string const* ppath : movePaths) { std::list<std::string>::iterator it = includes.begin(); while (it != includes.end()) { std::string const& path = *it; if (cmSystemTools::StringStartsWith(path, ppath->c_str())) { - this->MocIncludePaths.push_back(path); + Moc_.IncludePaths.push_back(path); it = includes.erase(it); } else { ++it; @@ -368,14 +1494,14 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) } } // Append remaining directories - this->MocIncludePaths.insert(this->MocIncludePaths.end(), - includes.begin(), includes.end()); + Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(), + includes.end()); } // Compose moc includes list { std::set<std::string> frameworkPaths; - for (std::string const& path : this->MocIncludePaths) { - this->MocIncludes.push_back("-I" + path); + for (std::string const& path : Moc().IncludePaths) { + Moc_.Includes.push_back("-I" + path); // Extract framework path if (cmHasLiteralSuffix(path, ".framework/Headers")) { // Go up twice to get to the framework root @@ -388,1346 +1514,511 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) } // Append framework includes for (std::string const& path : frameworkPaths) { - this->MocIncludes.push_back("-F"); - this->MocIncludes.push_back(path); + Moc_.Includes.push_back("-F"); + Moc_.Includes.push_back(path); } } // Setup single list with all options { // Add includes - this->MocAllOptions.insert(this->MocAllOptions.end(), - this->MocIncludes.begin(), - this->MocIncludes.end()); + Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(), + Moc().Includes.end()); // Add definitions - for (std::string const& def : this->MocDefinitions) { - this->MocAllOptions.push_back("-D" + def); + for (std::string const& def : Moc().Definitions) { + Moc_.AllOptions.push_back("-D" + def); } // Add options - this->MocAllOptions.insert(this->MocAllOptions.end(), - this->MocOptions.begin(), - this->MocOptions.end()); + Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(), + Moc().Options.end()); } } return true; } -void cmQtAutoGeneratorMocUic::SettingsFileRead(cmMakefile* makefile) +bool cmQtAutoGeneratorMocUic::Process() +{ + // Run libuv event loop + UVRequest().send(); + if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) { + if (JobError_) { + return false; + } + } else { + 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; + } +} + +void cmQtAutoGeneratorMocUic::SetStage(StageT stage) +{ + if (JobError_) { + stage = StageT::FINISH; + } + // Only allow to increase the stage + if (Stage_ < stage) { + Stage_ = stage; + UVRequest().send(); + } +} + +void cmQtAutoGeneratorMocUic::SettingsFileRead() { // Compose current settings strings { cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); std::string const sep(" ~~~ "); - if (this->MocEnabled()) { + if (Moc_.Enabled) { std::string str; - str += this->MocExecutable; + str += Moc().Executable; str += sep; - str += cmJoin(this->MocAllOptions, ";"); + str += cmJoin(Moc().AllOptions, ";"); str += sep; - str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE"; + str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE"; str += sep; - str += cmJoin(this->MocPredefsCmd, ";"); + str += cmJoin(Moc().PredefsCmd, ";"); str += sep; - this->SettingsStringMoc = crypt.HashString(str); + SettingsStringMoc_ = crypt.HashString(str); } - if (this->UicEnabled()) { + if (Uic().Enabled) { std::string str; - str += this->UicExecutable; + str += Uic().Executable; str += sep; - str += cmJoin(this->UicTargetOptions, ";"); - for (const auto& item : this->UicOptions) { + str += cmJoin(Uic().TargetOptions, ";"); + for (const auto& item : Uic().Options) { str += sep; str += item.first; str += sep; str += cmJoin(item.second, ";"); } str += sep; - this->SettingsStringUic = crypt.HashString(str); + SettingsStringUic_ = crypt.HashString(str); } } - // Read old settings - if (makefile->ReadListFile(this->SettingsFile.c_str())) { - { - auto SMatch = [makefile](const char* key, std::string const& value) { - return (value == makefile->GetSafeDefinition(key)); - }; - if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) { - this->MocSettingsChanged = true; + // Read old settings and compare + { + std::string content; + if (FileSys().FileRead(content, SettingsFile_)) { + if (Moc().Enabled) { + if (SettingsStringMoc_ != SettingsFind(content, "moc")) { + Moc_.SettingsChanged = true; + } } - if (!SMatch(SettingsKeyUic, this->SettingsStringUic)) { - this->UicSettingsChanged = true; + if (Uic().Enabled) { + if (SettingsStringUic_ != SettingsFind(content, "uic")) { + Uic_.SettingsChanged = true; + } + } + // In case any setting changed remove the old settings file. + // This triggers a full rebuild on the next run if the current + // build is aborted before writing the current settings in the end. + if (Moc().SettingsChanged || Uic().SettingsChanged) { + FileSys().FileRemove(SettingsFile_); + } + } else { + // Settings file read failed + if (Moc().Enabled) { + Moc_.SettingsChanged = true; + } + if (Uic().Enabled) { + Uic_.SettingsChanged = true; } } - // In case any setting changed remove the old settings file. - // This triggers a full rebuild on the next run if the current - // build is aborted before writing the current settings in the end. - if (this->SettingsChanged()) { - cmSystemTools::RemoveFile(this->SettingsFile); - } - } else { - // If the file could not be read re-generate everythiung. - this->MocSettingsChanged = true; - this->UicSettingsChanged = true; } } -bool cmQtAutoGeneratorMocUic::SettingsFileWrite() +void cmQtAutoGeneratorMocUic::SettingsFileWrite() { - bool success = true; + std::lock_guard<std::mutex> jobsLock(JobsMutex_); // Only write if any setting changed - if (this->SettingsChanged()) { - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " + - cmQtAutoGen::Quoted(this->SettingsFile)); + if (!JobError_ && (Moc().SettingsChanged || Uic().SettingsChanged)) { + if (Log().Verbose()) { + Log().Info(GeneratorT::GEN, + "Writing settings file " + Quoted(SettingsFile_)); } // Compose settings file content - std::string settings; + std::string content; { - auto SettingAppend = [&settings](const char* key, - std::string const& value) { - settings += "set("; - settings += key; - settings += " "; - settings += cmOutputConverter::EscapeForCMake(value); - settings += ")\n"; + auto SettingAppend = [&content](const char* key, + std::string const& value) { + if (!value.empty()) { + content += key; + content += ':'; + content += value; + content += '\n'; + } }; - SettingAppend(SettingsKeyMoc, this->SettingsStringMoc); - SettingAppend(SettingsKeyUic, this->SettingsStringUic); + SettingAppend("moc", SettingsStringMoc_); + SettingAppend("uic", SettingsStringUic_); } // Write settings file - if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) { - this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile, - "Settings file writing failed"); + if (!FileSys().FileWrite(GeneratorT::GEN, SettingsFile_, content)) { + Log().ErrorFile(GeneratorT::GEN, SettingsFile_, + "Settings file writing failed"); // Remove old settings file to trigger a full rebuild on the next run - cmSystemTools::RemoveFile(this->SettingsFile); - success = false; + FileSys().FileRemove(SettingsFile_); + RegisterJobError(); } } - return success; } -bool cmQtAutoGeneratorMocUic::Process(cmMakefile* makefile) +void cmQtAutoGeneratorMocUic::CreateDirectories() { - // the program goes through all .cpp files to see which moc files are - // included. It is not really interesting how the moc file is named, but - // what file the moc is created from. Once a moc is included the same moc - // may not be included in the mocs_compilation.cpp file anymore. - // OTOH if there's a header containing Q_OBJECT where no corresponding - // moc file is included anywhere a moc_<filename>.cpp file is created and - // included in the mocs_compilation.cpp file. - - if (!this->InitInfoFile(makefile)) { - return false; - } - // Read latest settings - this->SettingsFileRead(makefile); - // Create AUTOGEN include directory - { - std::string const incDirAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->AutogenIncludeDir); - if (!cmSystemTools::MakeDirectory(incDirAbs)) { - this->LogFileError(cmQtAutoGen::GEN, incDirAbs, - "Could not create directory"); - return false; - } - } - - // Parse source files - for (const auto& item : this->SourceJobs) { - if (!this->ParseSourceFile(item.first, item.second)) { - return false; - } - } - // Parse header files - for (const auto& item : this->HeaderJobs) { - if (!this->ParseHeaderFile(item.first, item.second)) { - return false; - } - } - // Read missing dependency information - if (!this->ParsePostprocess()) { - return false; + if (!FileSys().MakeDirectory(GeneratorT::GEN, Base().AutogenIncludeDirAbs)) { + RegisterJobError(); } - - // Generate files - if (!this->MocGenerateAll()) { - return false; - } - if (!this->UicGenerateAll()) { - return false; - } - - if (!this->SettingsFileWrite()) { - return false; - } - - return true; } -/** - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::ParseSourceFile(std::string const& absFilename, - const SourceJob& job) +bool cmQtAutoGeneratorMocUic::ThreadsStartJobs(JobQueueT& queue) { - std::string contentText; - std::string error; - bool success = this->FileRead(contentText, absFilename, &error); - if (success) { - if (!contentText.empty()) { - if (job.Moc) { - success = this->MocParseSourceContent(absFilename, contentText); - } - if (success && job.Uic) { - success = this->UicParseContent(absFilename, contentText); + 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; } - } else { - this->LogFileWarning(cmQtAutoGen::GEN, absFilename, - "The source file is empty"); + done = true; } - } else { - this->LogFileError(cmQtAutoGen::GEN, absFilename, - "Could not read the source file: " + error); } - return success; -} -/** - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::ParseHeaderFile(std::string const& absFilename, - const SourceJob& job) -{ - std::string contentText; - std::string error; - bool success = this->FileRead(contentText, absFilename, &error); - if (success) { - if (!contentText.empty()) { - if (job.Moc) { - this->MocParseHeaderContent(absFilename, contentText); - } - if (job.Uic) { - success = this->UicParseContent(absFilename, contentText); + 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()); } } else { - this->LogFileWarning(cmQtAutoGen::GEN, absFilename, - "The header file is empty"); - } - } else { - this->LogFileError(cmQtAutoGen::GEN, absFilename, - "Could not read the header file: " + error); - } - return success; -} - -/** - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::ParsePostprocess() -{ - bool success = true; - // Read missing dependencies - for (auto& item : this->MocJobsIncluded) { - if (!item->DependsValid) { - std::string content; - std::string error; - if (this->FileRead(content, item->SourceFile, &error)) { - this->MocFindDepends(item->SourceFile, content, item->Depends); - item->DependsValid = true; + // Notify threads + if (queueSize == 1) { + JobsConditionRead_.notify_one(); } else { - std::string emsg = "Could not read file\n "; - emsg += item->SourceFile; - emsg += "\nrequired by moc include \""; - emsg += item->IncludeString; - emsg += "\".\n"; - emsg += error; - this->LogFileError(cmQtAutoGen::MOC, item->Includer, emsg); - success = false; - break; + JobsConditionRead_.notify_all(); } } } - return success; -} - -/** - * @brief Tests if the file should be ignored for moc scanning - * @return True if the file should be ignored - */ -bool cmQtAutoGeneratorMocUic::MocSkip(std::string const& absFilename) const -{ - if (this->MocEnabled()) { - // Test if the file name is on the skip list - if (!ListContains(this->MocSkipList, absFilename)) { - return false; - } - } - return true; -} -/** - * @brief Tests if the C++ content requires moc processing - * @return True if moc is required - */ -bool cmQtAutoGeneratorMocUic::MocRequired(std::string const& contentText, - std::string* macroName) -{ - for (KeyRegExp& filter : this->MocMacroFilters) { - // Run a simple find string operation before the expensive - // regular expression check - if (contentText.find(filter.Key) != std::string::npos) { - if (filter.RegExp.find(contentText)) { - // Return macro name on demand - if (macroName != nullptr) { - *macroName = filter.Key; - } - return true; - } - } - } - return false; + return done; } -std::string cmQtAutoGeneratorMocUic::MocStringMacros() const +void cmQtAutoGeneratorMocUic::ThreadsStop() { - std::string res; - const auto itB = this->MocMacroFilters.cbegin(); - const auto itE = this->MocMacroFilters.cend(); - const auto itL = itE - 1; - auto itC = itB; - for (; itC != itE; ++itC) { - // Separator - if (itC != itB) { - if (itC != itL) { - res += ", "; - } else { - res += " or "; - } - } - // Key - res += itC->Key; + 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(); } - return res; } -std::string cmQtAutoGeneratorMocUic::MocStringHeaders( - std::string const& fileBase) const +bool cmQtAutoGeneratorMocUic::ThreadsJobsDone() { - std::string res = fileBase; - res += ".{"; - res += cmJoin(this->HeaderExtensions, ","); - res += "}"; - return res; + std::lock_guard<std::mutex> jobsLock(JobsMutex_); + return (JobsRemain_ == 0); } -std::string cmQtAutoGeneratorMocUic::MocFindIncludedHeader( - std::string const& sourcePath, std::string const& includeBase) const +void cmQtAutoGeneratorMocUic::WorkerSwapJob(JobHandleT& jobHandle) { - std::string header; - // Search in vicinity of the source - if (!this->FindHeader(header, sourcePath + includeBase)) { - // Search in include directories - for (std::string const& path : this->MocIncludePaths) { - std::string fullPath = path; - fullPath.push_back('/'); - fullPath += includeBase; - if (this->FindHeader(header, fullPath)) { - break; - } - } - } - // Sanitize - if (!header.empty()) { - header = cmSystemTools::GetRealPath(header); + bool const jobProcessed(jobHandle); + if (jobProcessed) { + jobHandle.reset(nullptr); } - return header; -} - -bool cmQtAutoGeneratorMocUic::MocFindIncludedFile( - std::string& absFile, std::string const& sourcePath, - std::string const& includeString) const -{ - bool success = false; - // Search in vicinity of the source { - std::string testPath = sourcePath; - testPath += includeString; - if (cmSystemTools::FileExists(testPath.c_str())) { - absFile = cmSystemTools::GetRealPath(testPath); - success = true; - } - } - // Search in include directories - if (!success) { - for (std::string const& path : this->MocIncludePaths) { - std::string fullPath = path; - fullPath.push_back('/'); - fullPath += includeString; - if (cmSystemTools::FileExists(fullPath.c_str())) { - absFile = cmSystemTools::GetRealPath(fullPath); - success = true; - break; + 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(); } } - } - return success; -} - -bool cmQtAutoGeneratorMocUic::MocDependFilterPush(std::string const& key, - std::string const& regExp) -{ - std::string error; - if (!key.empty()) { - if (!regExp.empty()) { - KeyRegExp filter; - filter.Key = key; - if (filter.RegExp.compile(regExp)) { - this->MocDependFilters.push_back(std::move(filter)); - } else { - error = "Regular expression compiling failed"; - } - } else { - error = "Regular expression is empty"; - } - } else { - error = "Key is empty"; - } - if (!error.empty()) { - std::string emsg = "AUTOMOC_DEPEND_FILTERS: "; - emsg += error; - emsg += "\n"; - emsg += " Key: "; - emsg += cmQtAutoGen::Quoted(key); - emsg += "\n"; - emsg += " RegExp: "; - emsg += cmQtAutoGen::Quoted(regExp); - emsg += "\n"; - this->LogError(cmQtAutoGen::MOC, emsg); - return false; - } - return true; -} - -void cmQtAutoGeneratorMocUic::MocFindDepends(std::string const& absFilename, - std::string const& contentText, - std::set<std::string>& depends) -{ - if (this->MocDependFilters.empty() && contentText.empty()) { - return; - } - - std::vector<std::string> matches; - for (KeyRegExp& filter : this->MocDependFilters) { - // Run a simple find string check - if (contentText.find(filter.Key) != std::string::npos) { - // Run the expensive regular expression check loop - const char* contentChars = contentText.c_str(); - while (filter.RegExp.find(contentChars)) { - std::string match = filter.RegExp.match(1); - if (!match.empty()) { - matches.emplace_back(std::move(match)); - } - contentChars += filter.RegExp.end(); - } + // Wait for new jobs + while (!JobThreadsAbort_ && JobQueue_.empty()) { + JobsConditionRead_.wait(jobsLock); } - } - - if (!matches.empty()) { - std::string const sourcePath = SubDirPrefix(absFilename); - for (std::string const& match : matches) { - // Find the dependency file - std::string incFile; - if (this->MocFindIncludedFile(incFile, sourcePath, match)) { - depends.insert(incFile); - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::MOC, "Found dependency:\n " + - cmQtAutoGen::Quoted(absFilename) + "\n " + - cmQtAutoGen::Quoted(incFile)); - } - } else { - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, - "Could not find dependency file " + - cmQtAutoGen::Quoted(match)); - } + // Try to pick up a new job handle + if (!JobThreadsAbort_ && !JobQueue_.empty()) { + jobHandle = std::move(JobQueue_.front()); + JobQueue_.pop_front(); } } } -/** - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::MocParseSourceContent( - std::string const& absFilename, std::string const& contentText) +void cmQtAutoGeneratorMocUic::ParallelRegisterJobError() { - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); - } - - auto AddJob = [this, &absFilename](std::string const& sourceFile, - std::string const& includeString, - std::string const* content) { - auto job = cm::make_unique<MocJobIncluded>(); - job->SourceFile = sourceFile; - job->BuildFileRel = this->AutogenIncludeDir; - job->BuildFileRel += includeString; - job->Includer = absFilename; - job->IncludeString = includeString; - job->DependsValid = (content != nullptr); - if (job->DependsValid) { - this->MocFindDepends(sourceFile, *content, job->Depends); - } - this->MocJobsIncluded.push_back(std::move(job)); - }; - - struct MocInc - { - std::string Inc; // full include string - std::string Dir; // include string directory - std::string Base; // include string file base - }; - - // Extract moc includes from file - std::vector<MocInc> mocIncsUsc; - std::vector<MocInc> mocIncsDot; - { - const char* contentChars = contentText.c_str(); - if (strstr(contentChars, "moc") != nullptr) { - while (this->MocRegExpInclude.find(contentChars)) { - std::string incString = this->MocRegExpInclude.match(1); - std::string incDir(SubDirPrefix(incString)); - std::string incBase = - cmSystemTools::GetFilenameWithoutLastExtension(incString); - if (cmHasLiteralPrefix(incBase, "moc_")) { - // moc_<BASE>.cxx - // Remove the moc_ part from the base name - mocIncsUsc.push_back(MocInc{ std::move(incString), std::move(incDir), - incBase.substr(4) }); - } else { - // <BASE>.moc - mocIncsDot.push_back(MocInc{ std::move(incString), std::move(incDir), - std::move(incBase) }); - } - // Forward content pointer - contentChars += this->MocRegExpInclude.end(); - } - } - } - - std::string selfMacroName; - const bool selfRequiresMoc = this->MocRequired(contentText, &selfMacroName); - - // Check if there is anything to do - if (!selfRequiresMoc && mocIncsUsc.empty() && mocIncsDot.empty()) { - return true; - } - - // Scan file variables - std::string const scanFileDir = SubDirPrefix(absFilename); - std::string const scanFileBase = - cmSystemTools::GetFilenameWithoutLastExtension(absFilename); - // Relaxed mode variables - bool ownDotMocIncluded = false; - std::string ownMocUscInclude; - std::string ownMocUscHeader; - - // Process moc_<BASE>.cxx includes - for (const MocInc& mocInc : mocIncsUsc) { - std::string const header = - this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base); - if (!header.empty()) { - // Check if header is skipped - if (this->MocSkip(header)) { - continue; - } - // Register moc job - AddJob(header, mocInc.Inc, nullptr); - // Store meta information for relaxed mode - if (this->MocRelaxedMode && (mocInc.Base == scanFileBase)) { - ownMocUscInclude = mocInc.Inc; - ownMocUscHeader = header; - } - } else { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", but could not find the header "; - emsg += cmQtAutoGen::Quoted(this->MocStringHeaders(mocInc.Base)); - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - - // Process <BASE>.moc includes - for (const MocInc& mocInc : mocIncsDot) { - const bool ownMoc = (mocInc.Base == scanFileBase); - if (this->MocRelaxedMode) { - // Relaxed mode - if (selfRequiresMoc && ownMoc) { - // Add self - AddJob(absFilename, mocInc.Inc, &contentText); - ownDotMocIncluded = true; - } else { - // In relaxed mode try to find a header instead but issue a warning. - // This is for KDE4 compatibility - std::string const header = - this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base); - if (!header.empty()) { - // Check if header is skipped - if (this->MocSkip(header)) { - continue; - } - // Register moc job - AddJob(header, mocInc.Inc, nullptr); - if (!selfRequiresMoc) { - if (ownMoc) { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", but does not contain a "; - emsg += this->MocStringMacros(); - emsg += " macro.\nRunning moc on\n "; - emsg += cmQtAutoGen::Quoted(header); - emsg += "!\nBetter include "; - emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += " for a compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - } else { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += " instead of "; - emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += ".\nRunning moc on\n "; - emsg += cmQtAutoGen::Quoted(header); - emsg += "!\nBetter include "; - emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - } - } - } else { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", which seems to be the moc file from a different " - "source file. CMake also could not find a matching " - "header."; - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - } else { - // Strict mode - if (ownMoc) { - // Include self - AddJob(absFilename, mocInc.Inc, &contentText); - ownDotMocIncluded = true; - // Accept but issue a warning if moc isn't required - if (!selfRequiresMoc) { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", but does not contain a "; - emsg += this->MocStringMacros(); - emsg += " macro."; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - } - } else { - // Don't allow <BASE>.moc include other than self in strict mode - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", which seems to be the moc file from a different " - "source file.\nThis is not supported. Include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += " to run moc on this source file."; - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - } - - if (selfRequiresMoc && !ownDotMocIncluded) { - // In this case, check whether the scanned file itself contains a Q_OBJECT. - // If this is the case, the moc_foo.cpp should probably be generated from - // foo.cpp instead of foo.h, because otherwise it won't build. - // But warn, since this is not how it is supposed to be used. - if (this->MocRelaxedMode && !ownMocUscInclude.empty()) { - // This is for KDE4 compatibility: - std::string emsg = "The file contains a "; - emsg += selfMacroName; - emsg += " macro, but does not include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += ". Instead it includes "; - emsg += cmQtAutoGen::Quoted(ownMocUscInclude); - emsg += ".\nRunning moc on\n "; - emsg += cmQtAutoGen::Quoted(absFilename); - emsg += "!\nBetter include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - - // Remove own header job - { - auto itC = this->MocJobsIncluded.begin(); - auto itE = this->MocJobsIncluded.end(); - for (; itC != itE; ++itC) { - if ((*itC)->SourceFile == ownMocUscHeader) { - if ((*itC)->IncludeString == ownMocUscInclude) { - this->MocJobsIncluded.erase(itC); - break; - } - } - } - } - // Add own source job - AddJob(absFilename, ownMocUscInclude, &contentText); - } else { - // Otherwise always error out since it will not compile: - std::string emsg = "The file contains a "; - emsg += selfMacroName; - emsg += " macro, but does not include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += "!\nConsider to\n - add #include \""; - emsg += scanFileBase; - emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - return true; + std::lock_guard<std::mutex> jobsLock(JobsMutex_); + RegisterJobError(); } -void cmQtAutoGeneratorMocUic::MocParseHeaderContent( - std::string const& absFilename, std::string const& contentText) +// Private method that requires cmQtAutoGeneratorMocUic::JobsMutex_ to be +// locked +void cmQtAutoGeneratorMocUic::RegisterJobError() { - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); - } - - auto const fit = - std::find_if(this->MocJobsIncluded.cbegin(), this->MocJobsIncluded.cend(), - [&absFilename](std::unique_ptr<MocJobIncluded> const& job) { - return job->SourceFile == absFilename; - }); - if (fit == this->MocJobsIncluded.cend()) { - if (this->MocRequired(contentText)) { - auto job = cm::make_unique<MocJobAuto>(); - job->SourceFile = absFilename; - { - std::string& bld = job->BuildFileRel; - bld = this->FilePathChecksum.getPart(absFilename); - bld += '/'; - bld += "moc_"; - bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename); - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - bld += this->ConfigSuffix; - } - bld += ".cpp"; - } - this->MocFindDepends(absFilename, contentText, job->Depends); - this->MocJobsAuto.push_back(std::move(job)); + JobError_ = true; + if (!JobThreadsAbort_) { + JobThreadsAbort_ = true; + // Clear remaining jobs + if (JobsRemain_ != 0) { + JobsRemain_ -= JobQueue_.size(); + JobQueue_.clear(); } } } -bool cmQtAutoGeneratorMocUic::MocGenerateAll() +bool cmQtAutoGeneratorMocUic::ParallelJobPushMoc(JobHandleT& jobHandle) { - if (!this->MocEnabled()) { - return true; - } - - // Look for name collisions in included moc files - { - bool collision = false; - std::map<std::string, std::vector<MocJobIncluded const*>> collisions; - for (auto const& job : this->MocJobsIncluded) { - auto& list = collisions[job->IncludeString]; - if (!list.empty()) { - collision = true; - } - list.push_back(job.get()); - } - if (collision) { - std::string emsg = - "Included moc files with the same name will be " - "generated from different sources.\n" - "Consider 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" - "Include conflicts\n" - "-----------------\n"; - const auto& colls = collisions; - for (auto const& coll : colls) { - if (coll.second.size() > 1) { - emsg += cmQtAutoGen::Quoted(coll.first); - emsg += " included in\n"; - for (const MocJobIncluded* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->Includer); - emsg += "\n"; - } - emsg += "would be generated from\n"; - for (const MocJobIncluded* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->SourceFile); - emsg += "\n"; + 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 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(GeneratorT::MOC, error); + RegisterJobError(); + } + // Do not push this job in since the included moc file already + // gets generated by an other job. + pushJobHandle = false; + break; } } } - this->LogError(cmQtAutoGen::MOC, emsg); - return false; } - } - - // (Re)generate moc_predefs.h on demand - if (!this->MocPredefsCmd.empty()) { - if (this->MocSettingsChanged || - !cmSystemTools::FileExists(this->MocPredefsFileAbs)) { - if (this->GetVerbose()) { - this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel); - } - - std::string output; - { - // Compose command - std::vector<std::string> cmd = this->MocPredefsCmd; - // Add includes - cmd.insert(cmd.end(), this->MocIncludes.begin(), - this->MocIncludes.end()); - // Add definitions - for (std::string const& def : this->MocDefinitions) { - cmd.push_back("-D" + def); - } - // Execute command - if (!this->RunCommand(cmd, output)) { - this->LogCommandError(cmQtAutoGen::MOC, - "moc_predefs generation failed", cmd, output); - return false; - } - } - - // (Re)write predefs file only on demand - if (this->FileDiffers(this->MocPredefsFileAbs, output)) { - if (this->FileWrite(cmQtAutoGen::MOC, this->MocPredefsFileAbs, - output)) { - this->MocPredefsChanged = true; - } else { - this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs, - "moc_predefs file writing failed"); - return false; - } - } else { - // Touch to update the time stamp - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::MOC, - "Touching moc_predefs " + this->MocPredefsFileRel); - } - cmSystemTools::Touch(this->MocPredefsFileAbs, false); - } - } - - // Add moc_predefs.h to moc file dependencies - for (auto const& item : this->MocJobsIncluded) { - item->Depends.insert(this->MocPredefsFileAbs); - } - for (auto const& item : this->MocJobsAuto) { - item->Depends.insert(this->MocPredefsFileAbs); - } - } - - // Generate moc files that are included by source files. - for (auto const& item : this->MocJobsIncluded) { - if (!this->MocGenerateFile(*item)) { - return false; - } - } - // Generate moc files that are _not_ included by source files. - bool autoNameGenerated = false; - for (auto const& item : this->MocJobsAuto) { - if (!this->MocGenerateFile(*item, &autoNameGenerated)) { - return false; - } - } - - // Compose mocs compilation file content - { - std::string mocs = - "// This file is autogenerated. Changes will be overwritten.\n"; - if (this->MocJobsAuto.empty()) { - // Placeholder content - mocs += - "// No files found that require moc or the moc files are included\n"; - mocs += "enum some_compilers { need_more_than_nothing };\n"; - } else { - // Valid content - for (const auto& item : this->MocJobsAuto) { - mocs += "#include \""; - mocs += item->BuildFileRel; - mocs += "\"\n"; - } - } - - if (this->FileDiffers(this->MocCompFileAbs, mocs)) { - // Actually write mocs compilation file - if (this->GetVerbose()) { - this->LogBold("Generating MOC compilation " + this->MocCompFileRel); - } - if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, mocs)) { - this->LogFileError(cmQtAutoGen::MOC, this->MocCompFileAbs, - "mocs compilation file writing failed"); - return false; - } - } else if (autoNameGenerated) { - // Only touch mocs compilation file - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::MOC, - "Touching mocs compilation " + this->MocCompFileRel); - } - cmSystemTools::Touch(this->MocCompFileAbs, false); + // Push job on demand + if (pushJobHandle) { + JobQueues_.Moc.emplace_back(std::move(jobHandle)); } } - - return true; + return !JobError_; } -/** - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob, - bool* generated) +bool cmQtAutoGeneratorMocUic::ParallelJobPushUic(JobHandleT& jobHandle) { - bool success = true; - - std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, mocJob.BuildFileRel); - - bool generate = false; - std::string generateReason; - if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from its source file "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because it doesn't exist"; - } - generate = true; - } - if (!generate && this->MocSettingsChanged) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because the MOC settings changed"; - } - generate = true; - } - if (!generate && this->MocPredefsChanged) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because moc_predefs.h changed"; - } - generate = true; - } - if (!generate) { - std::string error; - if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " because it's older than its source file "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - } - generate = true; - } else { - if (!error.empty()) { - this->LogError(cmQtAutoGen::MOC, error); - success = false; - } - } - } - if (success && !generate) { - // Test if a dependency file is newer - std::string error; - for (std::string const& depFile : mocJob.Depends) { - if (FileIsOlderThan(mocFileAbs, depFile, &error)) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because it is older than "; - generateReason += cmQtAutoGen::Quoted(depFile); + 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 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(GeneratorT::UIC, error); + RegisterJobError(); } - generate = true; - break; - } - if (!error.empty()) { - this->LogError(cmQtAutoGen::MOC, error); - success = false; + // Do not push this job in since the uic file already + // gets generated by an other job. + pushJobHandle = false; break; } } - } - - if (generate) { - // Log - if (this->GetVerbose()) { - this->LogBold("Generating MOC source " + mocJob.BuildFileRel); - this->LogInfo(cmQtAutoGen::MOC, generateReason); - } - - // Make sure the parent directory exists - if (this->MakeParentDirectory(cmQtAutoGen::MOC, mocFileAbs)) { - // Compose moc command - std::vector<std::string> cmd; - cmd.push_back(this->MocExecutable); - // Add options - cmd.insert(cmd.end(), this->MocAllOptions.begin(), - this->MocAllOptions.end()); - // Add predefs include - if (!this->MocPredefsFileAbs.empty()) { - cmd.push_back("--include"); - cmd.push_back(this->MocPredefsFileAbs); - } - cmd.push_back("-o"); - cmd.push_back(mocFileAbs); - cmd.push_back(mocJob.SourceFile); - - // Execute moc command - std::string output; - if (this->RunCommand(cmd, output)) { - // Success - if (generated != nullptr) { - *generated = true; - } - } else { - // Moc command failed - { - std::string emsg = "moc failed for\n "; - emsg += cmQtAutoGen::Quoted(mocJob.SourceFile); - this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output); - } - cmSystemTools::RemoveFile(mocFileAbs); - success = false; - } - } else { - // Parent directory creation failed - success = false; + if (pushJobHandle) { + JobQueues_.Uic.emplace_back(std::move(jobHandle)); } } - return success; + return !JobError_; } -/** - * @brief Tests if the file name is in the skip list - */ -bool cmQtAutoGeneratorMocUic::UicSkip(std::string const& absFilename) const +bool cmQtAutoGeneratorMocUic::ParallelMocIncluded( + std::string const& sourceFile) { - if (this->UicEnabled()) { - // Test if the file name is on the skip list - if (!ListContains(this->UicSkipList, absFilename)) { - return false; - } - } - return true; + std::lock_guard<std::mutex> mocLock(JobsMutex_); + return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end()); } -bool cmQtAutoGeneratorMocUic::UicParseContent(std::string const& absFilename, - std::string const& contentText) +void cmQtAutoGeneratorMocUic::ParallelMocAutoRegister( + std::string const& mocFile) { - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename); - } - - std::vector<std::string> includes; - // Extracte includes - { - const char* contentChars = contentText.c_str(); - if (strstr(contentChars, "ui_") != nullptr) { - while (this->UicRegExpInclude.find(contentChars)) { - includes.push_back(this->UicRegExpInclude.match(1)); - contentChars += this->UicRegExpInclude.end(); - } - } - } - - for (std::string const& includeString : includes) { - std::string uiInputFile; - if (!UicFindIncludedFile(uiInputFile, absFilename, includeString)) { - return false; - } - // Check if this file should be skipped - if (this->UicSkip(uiInputFile)) { - continue; - } - // Check if the job already exists - bool jobExists = false; - for (const auto& job : this->UicJobs) { - if ((job->SourceFile == uiInputFile) && - (job->IncludeString == includeString)) { - jobExists = true; - break; - } - } - if (!jobExists) { - auto job = cm::make_unique<UicJob>(); - job->SourceFile = uiInputFile; - job->BuildFileRel = this->AutogenIncludeDir; - job->BuildFileRel += includeString; - job->Includer = absFilename; - job->IncludeString = includeString; - this->UicJobs.push_back(std::move(job)); - } - } - - return true; + std::lock_guard<std::mutex> mocLock(JobsMutex_); + MocAutoFiles_.emplace(mocFile); } -bool cmQtAutoGeneratorMocUic::UicFindIncludedFile( - std::string& absFile, std::string const& sourceFile, - std::string const& includeString) +void cmQtAutoGeneratorMocUic::ParallelMocAutoUpdated() { - bool success = false; - std::string searchFile = - cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3); - searchFile += ".ui"; - // Collect search paths list - std::vector<std::string> testFiles; - { - std::string const searchPath = SubDirPrefix(includeString); - - std::string searchFileFull; - if (!searchPath.empty()) { - searchFileFull = searchPath; - searchFileFull += searchFile; - } - // Vicinity of the source - { - std::string const sourcePath = SubDirPrefix(sourceFile); - testFiles.push_back(sourcePath + searchFile); - if (!searchPath.empty()) { - testFiles.push_back(sourcePath + searchFileFull); - } - } - // AUTOUIC search paths - if (!this->UicSearchPaths.empty()) { - for (std::string const& sPath : this->UicSearchPaths) { - testFiles.push_back((sPath + "/").append(searchFile)); - } - if (!searchPath.empty()) { - for (std::string const& sPath : this->UicSearchPaths) { - testFiles.push_back((sPath + "/").append(searchFileFull)); - } - } - } - } - - // Search for the .ui file! - for (std::string const& testFile : testFiles) { - if (cmSystemTools::FileExists(testFile.c_str())) { - absFile = cmSystemTools::GetRealPath(testFile); - success = true; - break; - } - } - - // Log error - if (!success) { - std::string emsg = "Could not find "; - emsg += cmQtAutoGen::Quoted(searchFile); - emsg += " in\n"; - for (std::string const& testFile : testFiles) { - emsg += " "; - emsg += cmQtAutoGen::Quoted(testFile); - emsg += "\n"; - } - this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg); - } - - return success; + std::lock_guard<std::mutex> mocLock(JobsMutex_); + MocAutoFileUpdated_ = true; } -bool cmQtAutoGeneratorMocUic::UicGenerateAll() +void cmQtAutoGeneratorMocUic::MocGenerateCompilation() { - if (!this->UicEnabled()) { - return true; - } - - // Look for name collisions in included uic files - { - bool collision = false; - std::map<std::string, std::vector<UicJob const*>> collisions; - for (auto const& job : this->UicJobs) { - auto& list = collisions[job->IncludeString]; - if (!list.empty()) { - collision = true; - } - list.push_back(job.get()); - } - if (collision) { - std::string emsg = - "Included uic files with the same name will be " - "generated from different sources.\n" - "Consider 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" - "Include conflicts\n" - "-----------------\n"; - const auto& colls = collisions; - for (auto const& coll : colls) { - if (coll.second.size() > 1) { - emsg += cmQtAutoGen::Quoted(coll.first); - emsg += " included in\n"; - for (const UicJob* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->Includer); - emsg += "\n"; - } - emsg += "would be generated from\n"; - for (const UicJob* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->SourceFile); - emsg += "\n"; - } + std::lock_guard<std::mutex> mocLock(JobsMutex_); + if (!JobThreadsAbort_ && Moc().Enabled) { + // 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 + for (std::string const& mocfile : MocAutoFiles_) { + content += "#include \""; + content += mocfile; + content += "\"\n"; } } - this->LogError(cmQtAutoGen::UIC, emsg); - return false; - } - } - - // Generate ui header files - for (const auto& item : this->UicJobs) { - if (!this->UicGenerateFile(*item)) { - return false; - } - } - - return true; -} - -/** - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob) -{ - bool success = true; - - std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, uicJob.BuildFileRel); - - bool generate = false; - std::string generateReason; - if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(uicFileAbs); - generateReason += " from its source file "; - generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); - generateReason += " because it doesn't exist"; - } - generate = true; - } - if (!generate && this->UicSettingsChanged) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(uicFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); - generateReason += " because the UIC settings changed"; - } - generate = true; - } - if (!generate) { - std::string error; - if (FileIsOlderThan(uicFileAbs, uicJob.SourceFile, &error)) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(uicFileAbs); - generateReason += " because it's older than its source file "; - generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); - } - generate = true; - } else { - if (!error.empty()) { - this->LogError(cmQtAutoGen::UIC, error); - success = false; - } - } - } - if (generate) { - // Log - if (this->GetVerbose()) { - this->LogBold("Generating UIC header " + uicJob.BuildFileRel); - this->LogInfo(cmQtAutoGen::UIC, generateReason); - } - // Make sure the parent directory exists - if (this->MakeParentDirectory(cmQtAutoGen::UIC, uicFileAbs)) { - // Compose uic command - std::vector<std::string> cmd; - cmd.push_back(this->UicExecutable); - { - std::vector<std::string> allOpts = this->UicTargetOptions; - auto optionIt = this->UicOptions.find(uicJob.SourceFile); - if (optionIt != this->UicOptions.end()) { - cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second, - (this->QtVersionMajor == 5)); + std::string const& compRel = Moc().CompFileRel; + std::string const& compAbs = Moc().CompFileAbs; + if (FileSys().FileDiffers(compAbs, content)) { + // Actually write mocs compilation file + if (Log().Verbose()) { + Log().Info(GeneratorT::MOC, "Generating MOC compilation " + compRel); } - cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); - } - cmd.push_back("-o"); - cmd.push_back(uicFileAbs); - cmd.push_back(uicJob.SourceFile); - - std::string output; - if (this->RunCommand(cmd, output)) { - // Success - } else { - // Command failed - { - std::string emsg = "uic failed for\n "; - emsg += cmQtAutoGen::Quoted(uicJob.SourceFile); - emsg += "\nincluded by\n "; - emsg += cmQtAutoGen::Quoted(uicJob.Includer); - this->LogCommandError(cmQtAutoGen::UIC, emsg, cmd, output); + if (!FileSys().FileWrite(GeneratorT::MOC, compAbs, content)) { + Log().ErrorFile(GeneratorT::MOC, compAbs, + "mocs compilation file writing failed"); + RegisterJobError(); + return; } - cmSystemTools::RemoveFile(uicFileAbs); - success = false; + } else if (MocAutoFileUpdated_) { + // Only touch mocs compilation file + if (Log().Verbose()) { + Log().Info(GeneratorT::MOC, "Touching mocs compilation " + compRel); + } + FileSys().Touch(compAbs); } - } else { - // Parent directory creation failed - success = false; } } - return success; -} - -/** - * @brief Tries to find the header file to the given file base path by - * appending different header extensions - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::FindHeader(std::string& header, - std::string const& testBasePath) const -{ - for (std::string const& ext : this->HeaderExtensions) { - std::string testFilePath(testBasePath); - testFilePath.push_back('.'); - testFilePath += ext; - if (cmSystemTools::FileExists(testFilePath.c_str())) { - header = testFilePath; - return true; - } - } - return false; } diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoGeneratorMocUic.h index d510939..215e25a 100644 --- a/Source/cmQtAutoGeneratorMocUic.h +++ b/Source/cmQtAutoGeneratorMocUic.h @@ -8,188 +8,434 @@ #include "cmFilePathChecksum.h" #include "cmQtAutoGen.h" #include "cmQtAutoGenerator.h" +#include "cmUVHandlePtr.h" +#include "cm_uv.h" #include "cmsys/RegularExpression.hxx" +#include <algorithm> +#include <condition_variable> +#include <cstddef> +#include <deque> #include <map> #include <memory> // IWYU pragma: keep +#include <mutex> #include <set> #include <string> +#include <thread> #include <vector> class cmMakefile; +// @brief AUTOMOC and AUTOUIC generator class cmQtAutoGeneratorMocUic : public cmQtAutoGenerator { CM_DISABLE_COPY(cmQtAutoGeneratorMocUic) public: cmQtAutoGeneratorMocUic(); + ~cmQtAutoGeneratorMocUic() override; -private: +public: // -- Types + class WorkerT; /// @brief Search key plus regular expression pair - struct KeyRegExp + /// + struct KeyExpT { - KeyRegExp() = default; + KeyExpT() = default; - KeyRegExp(const char* key, const char* regExp) + KeyExpT(const char* key, const char* exp) : Key(key) - , RegExp(regExp) + , Exp(exp) { } - KeyRegExp(std::string const& key, std::string const& regExp) + KeyExpT(std::string const& key, std::string const& exp) : Key(key) - , RegExp(regExp) + , Exp(exp) { } std::string Key; - cmsys::RegularExpression RegExp; + cmsys::RegularExpression Exp; }; - /// @brief Source file job - struct SourceJob + /// @brief Common settings + /// + class BaseSettingsT { - bool Moc = false; - bool Uic = false; + CM_DISABLE_COPY(BaseSettingsT) + public: + // -- Volatile methods + BaseSettingsT(FileSystem* fileSystem) + : MultiConfig(MultiConfigT::WRAPPER) + , IncludeProjectDirsBefore(false) + , QtVersionMajor(4) + , NumThreads(1) + , FileSys(fileSystem) + { + } + + // -- Const methods + std::string AbsoluteBuildPath(std::string const& relativePath) const; + bool FindHeader(std::string& header, + std::string const& testBasePath) const; + + // -- Attributes + // - Config + std::string ConfigSuffix; + MultiConfigT MultiConfig; + bool IncludeProjectDirsBefore; + unsigned int QtVersionMajor; + unsigned int NumThreads; + // - Directories + std::string ProjectSourceDir; + std::string ProjectBinaryDir; + std::string CurrentSourceDir; + std::string CurrentBinaryDir; + std::string AutogenBuildDir; + std::string AutogenIncludeDirRel; + std::string AutogenIncludeDirAbs; + // - Files + cmFilePathChecksum FilePathChecksum; + std::vector<std::string> HeaderExtensions; + // - File system + FileSystem* FileSys; }; - /// @brief MOC job - struct MocJobAuto + /// @brief Moc settings + /// + class MocSettingsT { - std::string SourceFile; - std::string BuildFileRel; - std::set<std::string> Depends; + CM_DISABLE_COPY(MocSettingsT) + public: + MocSettingsT(FileSystem* fileSys) + : FileSys(fileSys) + { + } + + // -- Const methods + bool skipped(std::string const& fileName) const; + std::string FindMacro(std::string const& content) const; + std::string MacrosString() const; + std::string FindIncludedFile(std::string const& sourcePath, + std::string const& includeString) const; + void FindDependencies(std::string const& content, + std::set<std::string>& depends) const; + + // -- Attributes + bool Enabled = false; + bool SettingsChanged = false; + bool RelaxedMode = false; + std::string Executable; + std::string CompFileRel; + std::string CompFileAbs; + std::string PredefsFileRel; + std::string PredefsFileAbs; + std::set<std::string> SkipList; + std::vector<std::string> IncludePaths; + std::vector<std::string> Includes; + std::vector<std::string> Definitions; + std::vector<std::string> Options; + std::vector<std::string> AllOptions; + std::vector<std::string> PredefsCmd; + std::vector<KeyExpT> DependFilters; + std::vector<KeyExpT> MacroFilters; + cmsys::RegularExpression RegExpInclude; + // - File system + FileSystem* FileSys; }; - /// @brief MOC job - struct MocJobIncluded : MocJobAuto + /// @brief Uic settings + /// + class UicSettingsT { - bool DependsValid = false; - std::string Includer; + CM_DISABLE_COPY(UicSettingsT) + public: + UicSettingsT() = default; + // -- Const methods + bool skipped(std::string const& fileName) const; + + // -- Attributes + bool Enabled = false; + bool SettingsChanged = false; + std::string Executable; + std::set<std::string> SkipList; + std::vector<std::string> TargetOptions; + std::map<std::string, std::vector<std::string>> Options; + std::vector<std::string> SearchPaths; + cmsys::RegularExpression RegExpInclude; + }; + + /// @brief Abstract job class for threaded processing + /// + class JobT + { + CM_DISABLE_COPY(JobT) + public: + JobT() = default; + virtual ~JobT() = default; + // -- Abstract processing interface + virtual void Process(WorkerT& wrk) = 0; + }; + + /// @brief Deleter for classes derived from Job + /// + struct JobDeleterT + { + void operator()(JobT* job); + }; + + // Job management types + typedef std::unique_ptr<JobT, JobDeleterT> JobHandleT; + typedef std::deque<JobHandleT> JobQueueT; + + /// @brief Parse source job + /// + class JobParseT : public JobT + { + public: + JobParseT(std::string&& fileName, bool moc, bool uic, bool header = false) + : FileName(std::move(fileName)) + , AutoMoc(moc) + , AutoUic(uic) + , Header(header) + { + } + + private: + struct MetaT + { + std::string Content; + std::string FileDir; + 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, + 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, + std::string const& includeString); + + private: + std::string FileName; + bool AutoMoc = false; + bool AutoUic = false; + bool Header = false; + }; + + /// @brief Generate moc_predefs + /// + class JobMocPredefsT : public JobT + { + private: + void Process(WorkerT& wrk) override; + }; + + /// @brief Moc a file job + /// + class JobMocT : public JobT + { + public: + JobMocT(std::string&& sourceFile, std::string const& includerFile, + std::string&& includeString) + : SourceFile(std::move(sourceFile)) + , IncluderFile(includerFile) + , IncludeString(std::move(includeString)) + { + } + + void FindDependencies(WorkerT& wrk, std::string const& content); + + private: + void Process(WorkerT& wrk) override; + bool UpdateRequired(WorkerT& wrk); + void GenerateMoc(WorkerT& wrk); + + public: + std::string SourceFile; + std::string IncluderFile; std::string IncludeString; + std::string BuildFile; + bool DependsValid = false; + std::set<std::string> Depends; }; - /// @brief UIC job - struct UicJob + /// @brief Uic a file job + /// + class JobUicT : public JobT { + public: + JobUicT(std::string&& sourceFile, std::string const& includerFile, + std::string&& includeString) + : SourceFile(std::move(sourceFile)) + , IncluderFile(includerFile) + , IncludeString(std::move(includeString)) + { + } + + private: + void Process(WorkerT& wrk) override; + bool UpdateRequired(WorkerT& wrk); + void GenerateUic(WorkerT& wrk); + + public: std::string SourceFile; - std::string BuildFileRel; - std::string Includer; + std::string IncluderFile; std::string IncludeString; + std::string BuildFile; }; - // -- Initialization - bool InitInfoFile(cmMakefile* makefile); + /// @brief Worker Thread + /// + class WorkerT + { + CM_DISABLE_COPY(WorkerT) + public: + WorkerT(cmQtAutoGeneratorMocUic* gen, uv_loop_t* uvLoop); + ~WorkerT(); + + // -- 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(); } - // -- Settings file - void SettingsFileRead(cmMakefile* makefile); - bool SettingsFileWrite(); - bool SettingsChanged() const + // -- Log info + void LogInfo(GeneratorT genType, std::string const& message) const; + // -- Log warning + void LogWarning(GeneratorT genType, std::string const& message) const; + void LogFileWarning(GeneratorT genType, std::string const& filename, + std::string const& message) const; + // -- Log error + void LogError(GeneratorT genType, std::string const& message) const; + void LogFileError(GeneratorT genType, std::string const& filename, + std::string const& message) const; + void LogCommandError(GeneratorT genType, std::string const& message, + std::vector<std::string> const& command, + std::string const& output) const; + + // -- External processes + /// @brief Verbose logging version + bool RunProcess(GeneratorT 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 { - return (this->MocSettingsChanged || this->UicSettingsChanged); - } - - // -- Central processing - bool Process(cmMakefile* makefile) override; - - // -- Source parsing - bool ParseSourceFile(std::string const& absFilename, const SourceJob& job); - bool ParseHeaderFile(std::string const& absFilename, const SourceJob& job); - bool ParsePostprocess(); - - // -- Moc - bool MocEnabled() const { return !this->MocExecutable.empty(); } - bool MocSkip(std::string const& absFilename) const; - bool MocRequired(std::string const& contentText, - std::string* macroName = nullptr); - // Moc strings - std::string MocStringMacros() const; - std::string MocStringHeaders(std::string const& fileBase) const; - std::string MocFindIncludedHeader(std::string const& sourcePath, - std::string const& includeBase) const; - bool MocFindIncludedFile(std::string& absFile, std::string const& sourceFile, - std::string const& includeString) const; - // Moc depends - bool MocDependFilterPush(std::string const& key, std::string const& regExp); - void MocFindDepends(std::string const& absFilename, - std::string const& contentText, - std::set<std::string>& depends); - // Moc - bool MocParseSourceContent(std::string const& absFilename, - std::string const& contentText); - void MocParseHeaderContent(std::string const& absFilename, - std::string const& contentText); - - bool MocGenerateAll(); - bool MocGenerateFile(const MocJobAuto& mocJob, bool* generated = nullptr); - - // -- Uic - bool UicEnabled() const { return !this->UicExecutable.empty(); } - bool UicSkip(std::string const& absFilename) const; - bool UicParseContent(std::string const& fileName, - std::string const& contentText); - bool UicFindIncludedFile(std::string& absFile, std::string const& sourceFile, - std::string const& includeString); - bool UicGenerateAll(); - bool UicGenerateFile(const UicJob& uicJob); - - // -- Utility - bool FindHeader(std::string& header, std::string const& testBasePath) const; - - // -- Meta - std::string ConfigSuffix; - cmQtAutoGen::MultiConfig MultiConfig; + SETTINGS_READ, + CREATE_DIRECTORIES, + PARSE_SOURCES, + PARSE_HEADERS, + MOC_PREDEFS, + MOC_PROCESS, + MOCS_COMPILATION, + UIC_PROCESS, + SETTINGS_WRITE, + FINISH, + END + }; + + // -- Const settings interface + const BaseSettingsT& Base() const { return this->Base_; } + 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); + void ParallelMocAutoRegister(std::string const& mocFile); + void ParallelMocAutoUpdated(); + +private: + // -- 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(); + // -- Thread processing + bool ThreadsStartJobs(JobQueueT& queue); + bool ThreadsJobsDone(); + void ThreadsStop(); + void RegisterJobError(); + // -- Generation + void CreateDirectories(); + void MocGenerateCompilation(); + +private: // -- Settings - bool IncludeProjectDirsBefore; - std::string SettingsFile; - std::string SettingsStringMoc; - std::string SettingsStringUic; - // -- Directories - std::string ProjectSourceDir; - std::string ProjectBinaryDir; - std::string CurrentSourceDir; - std::string CurrentBinaryDir; - std::string AutogenBuildDir; - std::string AutogenIncludeDir; - // -- Qt environment - unsigned long QtVersionMajor; - std::string MocExecutable; - std::string UicExecutable; - // -- File lists - std::map<std::string, SourceJob> HeaderJobs; - std::map<std::string, SourceJob> SourceJobs; - std::vector<std::string> HeaderExtensions; - cmFilePathChecksum FilePathChecksum; - // -- Moc - bool MocSettingsChanged; - bool MocPredefsChanged; - bool MocRelaxedMode; - std::string MocCompFileRel; - std::string MocCompFileAbs; - std::string MocPredefsFileRel; - std::string MocPredefsFileAbs; - std::vector<std::string> MocSkipList; - std::vector<std::string> MocIncludePaths; - std::vector<std::string> MocIncludes; - std::vector<std::string> MocDefinitions; - std::vector<std::string> MocOptions; - std::vector<std::string> MocAllOptions; - std::vector<std::string> MocPredefsCmd; - std::vector<KeyRegExp> MocDependFilters; - std::vector<KeyRegExp> MocMacroFilters; - cmsys::RegularExpression MocRegExpInclude; - std::vector<std::unique_ptr<MocJobIncluded>> MocJobsIncluded; - std::vector<std::unique_ptr<MocJobAuto>> MocJobsAuto; - // -- Uic - bool UicSettingsChanged; - std::vector<std::string> UicSkipList; - std::vector<std::string> UicTargetOptions; - std::map<std::string, std::vector<std::string>> UicOptions; - std::vector<std::string> UicSearchPaths; - cmsys::RegularExpression UicRegExpInclude; - std::vector<std::unique_ptr<UicJob>> UicJobs; + BaseSettingsT Base_; + MocSettingsT Moc_; + UicSettingsT Uic_; + // -- Progress + StageT Stage_; + // -- Job queues + std::mutex JobsMutex_; + struct + { + JobQueueT Sources; + JobQueueT Headers; + JobQueueT MocPredefs; + JobQueueT Moc; + JobQueueT Uic; + } JobQueues_; + JobQueueT JobQueue_; + std::size_t volatile JobsRemain_; + bool volatile JobError_; + bool volatile JobThreadsAbort_; + std::condition_variable JobsConditionRead_; + // -- Moc meta + std::set<std::string> MocIncludedStrings_; + std::set<std::string> MocIncludedFiles_; + std::set<std::string> MocAutoFiles_; + bool volatile MocAutoFileUpdated_; + // -- Settings file + std::string SettingsFile_; + std::string SettingsStringMoc_; + std::string SettingsStringUic_; + // -- Threads and loops + std::vector<std::unique_ptr<WorkerT>> Workers_; }; #endif diff --git a/Source/cmQtAutoGeneratorRcc.cxx b/Source/cmQtAutoGeneratorRcc.cxx index 3c9f1a8..e8ff75a 100644 --- a/Source/cmQtAutoGeneratorRcc.cxx +++ b/Source/cmQtAutoGeneratorRcc.cxx @@ -6,22 +6,30 @@ #include "cmAlgorithms.h" #include "cmCryptoHash.h" #include "cmMakefile.h" -#include "cmOutputConverter.h" #include "cmSystemTools.h" +#include "cmUVHandlePtr.h" -// -- Static variables - -static const char* SettingsKeyRcc = "ARCC_SETTINGS_HASH"; +#include <functional> // -- Class methods cmQtAutoGeneratorRcc::cmQtAutoGeneratorRcc() - : MultiConfig(cmQtAutoGen::WRAP) - , SettingsChanged(false) + : SettingsChanged_(false) + , MultiConfig_(MultiConfigT::WRAPPER) + , Stage_(StageT::SETTINGS_READ) + , Error_(false) + , Generate_(false) + , BuildFileChanged_(false) { + // Initialize libuv asynchronous iteration request + UVRequest().init(*UVLoop(), &cmQtAutoGeneratorRcc::UVPollStage, this); } -bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) +cmQtAutoGeneratorRcc::~cmQtAutoGeneratorRcc() +{ +} + +bool cmQtAutoGeneratorRcc::Init(cmMakefile* makefile) { // Utility lambdas auto InfoGet = [makefile](const char* key) { @@ -37,7 +45,7 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) { std::string keyConf = key; keyConf += '_'; - keyConf += this->GetInfoConfig(); + keyConf += InfoConfig(); valueConf = makefile->GetDefinition(keyConf); } if (valueConf == nullptr) { @@ -53,79 +61,180 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) }; // -- Read info file - if (!makefile->ReadListFile(this->GetInfoFile().c_str())) { - this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), - "File processing failed"); + if (!makefile->ReadListFile(InfoFile().c_str())) { + Log().ErrorFile(GeneratorT::RCC, InfoFile(), "File processing failed"); return false; } // -- Meta - this->MultiConfig = - cmQtAutoGen::MultiConfigType(InfoGet("ARCC_MULTI_CONFIG")); - this->ConfigSuffix = InfoGetConfig("ARCC_CONFIG_SUFFIX"); - if (this->ConfigSuffix.empty()) { - this->ConfigSuffix = "_"; - this->ConfigSuffix += this->GetInfoConfig(); + MultiConfig_ = MultiConfigType(InfoGet("ARCC_MULTI_CONFIG")); + ConfigSuffix_ = InfoGetConfig("ARCC_CONFIG_SUFFIX"); + if (ConfigSuffix_.empty()) { + ConfigSuffix_ = "_"; + ConfigSuffix_ += InfoConfig(); } - this->SettingsFile = InfoGetConfig("ARCC_SETTINGS_FILE"); + SettingsFile_ = InfoGetConfig("ARCC_SETTINGS_FILE"); // - Files and directories - this->ProjectSourceDir = InfoGet("ARCC_CMAKE_SOURCE_DIR"); - this->ProjectBinaryDir = InfoGet("ARCC_CMAKE_BINARY_DIR"); - this->CurrentSourceDir = InfoGet("ARCC_CMAKE_CURRENT_SOURCE_DIR"); - this->CurrentBinaryDir = InfoGet("ARCC_CMAKE_CURRENT_BINARY_DIR"); - this->AutogenBuildDir = InfoGet("ARCC_BUILD_DIR"); + AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR"); // - Qt environment - this->RccExecutable = InfoGet("ARCC_RCC_EXECUTABLE"); - this->RccListOptions = InfoGetList("ARCC_RCC_LIST_OPTIONS"); + RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE"); + RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS"); // - Job - this->QrcFile = InfoGet("ARCC_SOURCE"); - this->RccFile = InfoGet("ARCC_OUTPUT"); - this->Options = InfoGetConfigList("ARCC_OPTIONS"); - this->Inputs = InfoGetList("ARCC_INPUTS"); + QrcFile_ = InfoGet("ARCC_SOURCE"); + QrcFileName_ = cmSystemTools::GetFilenameName(QrcFile_); + QrcFileDir_ = cmSystemTools::GetFilenamePath(QrcFile_); + RccFile_ = InfoGet("ARCC_OUTPUT"); + Options_ = InfoGetConfigList("ARCC_OPTIONS"); + Inputs_ = InfoGetList("ARCC_INPUTS"); // - Validity checks - if (this->SettingsFile.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), - "Settings file name missing"); + if (SettingsFile_.empty()) { + Log().ErrorFile(GeneratorT::RCC, InfoFile(), "Settings file name missing"); return false; } - if (this->AutogenBuildDir.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), - "Autogen build directory missing"); + if (AutogenBuildDir_.empty()) { + Log().ErrorFile(GeneratorT::RCC, InfoFile(), + "Autogen build directory missing"); return false; } - if (this->RccExecutable.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), - "rcc executable missing"); + if (RccExecutable_.empty()) { + Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc executable missing"); return false; } - if (this->QrcFile.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), - "rcc input file missing"); + if (QrcFile_.empty()) { + Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc input file missing"); return false; } - if (this->RccFile.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), - "rcc output file missing"); + if (RccFile_.empty()) { + Log().ErrorFile(GeneratorT::RCC, InfoFile(), "rcc output file missing"); return false; } // Init derived information // ------------------------ - // Init file path checksum generator - this->FilePathChecksum.setupParentDirs( - this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir, - this->ProjectBinaryDir); + // Compute rcc output file name + { + std::string suffix; + switch (MultiConfig_) { + case MultiConfigT::SINGLE: + break; + case MultiConfigT::WRAPPER: + suffix = "_CMAKE"; + suffix += ConfigSuffix_; + suffix += "_"; + break; + case MultiConfigT::MULTI: + suffix = ConfigSuffix_; + break; + } + RccFileBuild_ = AppendFilenameSuffix(RccFile_, suffix); + } + + return true; +} +bool cmQtAutoGeneratorRcc::Process() +{ + // Run libuv event loop + UVRequest().send(); + if (uv_run(UVLoop(), UV_RUN_DEFAULT) == 0) { + if (Error_) { + return false; + } + } else { + return false; + } return true; } -void cmQtAutoGeneratorRcc::SettingsFileRead(cmMakefile* makefile) +void cmQtAutoGeneratorRcc::UVPollStage(uv_async_t* handle) +{ + reinterpret_cast<cmQtAutoGeneratorRcc*>(handle->data)->PollStage(); +} + +void cmQtAutoGeneratorRcc::PollStage() +{ + switch (Stage_) { + // -- Initialize + case StageT::SETTINGS_READ: + SettingsFileRead(); + SetStage(StageT::TEST_QRC_RCC_FILES); + break; + + // -- Change detection + case StageT::TEST_QRC_RCC_FILES: + if (TestQrcRccFiles()) { + SetStage(StageT::GENERATE); + } else { + SetStage(StageT::TEST_RESOURCES_READ); + } + break; + case StageT::TEST_RESOURCES_READ: + if (TestResourcesRead()) { + SetStage(StageT::TEST_RESOURCES); + } + break; + case StageT::TEST_RESOURCES: + if (TestResources()) { + SetStage(StageT::GENERATE); + } else { + SetStage(StageT::TEST_INFO_FILE); + } + break; + case StageT::TEST_INFO_FILE: + TestInfoFile(); + SetStage(StageT::GENERATE_WRAPPER); + break; + + // -- Generation + case StageT::GENERATE: + GenerateParentDir(); + SetStage(StageT::GENERATE_RCC); + break; + case StageT::GENERATE_RCC: + if (GenerateRcc()) { + SetStage(StageT::GENERATE_WRAPPER); + } + break; + case StageT::GENERATE_WRAPPER: + GenerateWrapper(); + SetStage(StageT::SETTINGS_WRITE); + break; + + // -- Finalize + case StageT::SETTINGS_WRITE: + SettingsFileWrite(); + SetStage(StageT::FINISH); + break; + case StageT::FINISH: + // Clear all libuv handles + UVRequest().reset(); + // Set highest END stage manually + Stage_ = StageT::END; + break; + case StageT::END: + break; + } +} + +void cmQtAutoGeneratorRcc::SetStage(StageT stage) +{ + if (Error_) { + stage = StageT::FINISH; + } + // Only allow to increase the stage + if (Stage_ < stage) { + Stage_ = stage; + UVRequest().send(); + } +} + +void cmQtAutoGeneratorRcc::SettingsFileRead() { // Compose current settings strings { @@ -133,293 +242,375 @@ void cmQtAutoGeneratorRcc::SettingsFileRead(cmMakefile* makefile) std::string const sep(" ~~~ "); { std::string str; - str += this->RccExecutable; + str += RccExecutable_; str += sep; - str += cmJoin(this->RccListOptions, ";"); + str += cmJoin(RccListOptions_, ";"); str += sep; - str += this->QrcFile; + str += QrcFile_; str += sep; - str += this->RccFile; + str += RccFile_; str += sep; - str += cmJoin(this->Options, ";"); + str += cmJoin(Options_, ";"); str += sep; - str += cmJoin(this->Inputs, ";"); + str += cmJoin(Inputs_, ";"); str += sep; - this->SettingsString = crypt.HashString(str); + SettingsString_ = crypt.HashString(str); } } // Read old settings - if (makefile->ReadListFile(this->SettingsFile.c_str())) { - { - auto SMatch = [makefile](const char* key, std::string const& value) { - return (value == makefile->GetSafeDefinition(key)); - }; - if (!SMatch(SettingsKeyRcc, this->SettingsString)) { - this->SettingsChanged = true; + { + std::string content; + if (FileSys().FileRead(content, SettingsFile_)) { + SettingsChanged_ = (SettingsString_ != SettingsFind(content, "rcc")); + // In case any setting changed remove the old settings file. + // This triggers a full rebuild on the next run if the current + // build is aborted before writing the current settings in the end. + if (SettingsChanged_) { + FileSys().FileRemove(SettingsFile_); } + } else { + SettingsChanged_ = true; } - // In case any setting changed remove the old settings file. - // This triggers a full rebuild on the next run if the current - // build is aborted before writing the current settings in the end. - if (this->SettingsChanged) { - cmSystemTools::RemoveFile(this->SettingsFile); - } - } else { - // If the file could not be read re-generate everythiung. - this->SettingsChanged = true; } } -bool cmQtAutoGeneratorRcc::SettingsFileWrite() +void cmQtAutoGeneratorRcc::SettingsFileWrite() { - bool success = true; // Only write if any setting changed - if (this->SettingsChanged) { - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::RCC, "Writing settings file " + - cmQtAutoGen::Quoted(this->SettingsFile)); - } - // Compose settings file content - std::string settings; - { - auto SettingAppend = [&settings](const char* key, - std::string const& value) { - settings += "set("; - settings += key; - settings += " "; - settings += cmOutputConverter::EscapeForCMake(value); - settings += ")\n"; - }; - SettingAppend(SettingsKeyRcc, this->SettingsString); + if (SettingsChanged_) { + if (Log().Verbose()) { + Log().Info(GeneratorT::RCC, + "Writing settings file " + Quoted(SettingsFile_)); } // Write settings file - if (!this->FileWrite(cmQtAutoGen::RCC, this->SettingsFile, settings)) { - this->LogFileError(cmQtAutoGen::RCC, this->SettingsFile, - "Settings file writing failed"); + std::string content = "rcc:"; + content += SettingsString_; + content += '\n'; + if (!FileSys().FileWrite(GeneratorT::RCC, SettingsFile_, content)) { + Log().ErrorFile(GeneratorT::RCC, SettingsFile_, + "Settings file writing failed"); // Remove old settings file to trigger a full rebuild on the next run - cmSystemTools::RemoveFile(this->SettingsFile); - success = false; + FileSys().FileRemove(SettingsFile_); + Error_ = true; } } - return success; } -bool cmQtAutoGeneratorRcc::Process(cmMakefile* makefile) +bool cmQtAutoGeneratorRcc::TestQrcRccFiles() { - // Read info file - if (!this->InfoFileRead(makefile)) { - return false; + // Do basic checks if rcc generation is required + + // Test if the rcc output file exists + if (!FileSys().FileExists(RccFileBuild_)) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(RccFileBuild_); + reason += " from its source file "; + reason += Quoted(QrcFile_); + reason += " because it doesn't exist"; + Log().Info(GeneratorT::RCC, reason); + } + Generate_ = true; + return Generate_; } - // Read latest settings - this->SettingsFileRead(makefile); - // Generate rcc file - if (!this->RccGenerate()) { - return false; + + // Test if the settings changed + if (SettingsChanged_) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(RccFileBuild_); + reason += " from "; + reason += Quoted(QrcFile_); + reason += " because the RCC settings changed"; + Log().Info(GeneratorT::RCC, reason); + } + Generate_ = true; + return Generate_; } - // Write latest settings - if (!this->SettingsFileWrite()) { - return false; + + // Test if the rcc output file is older than the .qrc file + { + bool isOlder = false; + { + std::string error; + isOlder = FileSys().FileIsOlderThan(RccFileBuild_, QrcFile_, &error); + if (!error.empty()) { + Log().ErrorFile(GeneratorT::RCC, QrcFile_, error); + Error_ = true; + } + } + if (isOlder) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(RccFileBuild_); + reason += " because it is older than "; + reason += Quoted(QrcFile_); + Log().Info(GeneratorT::RCC, reason); + } + Generate_ = true; + } } - return true; + + return Generate_; } -/** - * @return True on success - */ -bool cmQtAutoGeneratorRcc::RccGenerate() +bool cmQtAutoGeneratorRcc::TestResourcesRead() { - bool success = true; - bool rccGenerated = false; - - std::string rccFileAbs; - { - std::string suffix; - switch (this->MultiConfig) { - case cmQtAutoGen::SINGLE: - break; - case cmQtAutoGen::WRAP: - suffix = "_CMAKE"; - suffix += this->ConfigSuffix; - suffix += "_"; - break; - case cmQtAutoGen::FULL: - suffix = this->ConfigSuffix; - break; - } - rccFileAbs = cmQtAutoGen::AppendFilenameSuffix(this->RccFile, suffix); + if (!Inputs_.empty()) { + // Inputs are known already + return true; } - std::string const rccFileRel = cmSystemTools::RelativePath( - this->AutogenBuildDir.c_str(), rccFileAbs.c_str()); - // Check if regeneration is required - bool generate = false; - std::string generateReason; - if (!cmSystemTools::FileExists(this->QrcFile)) { - { - std::string error = "Could not find the file\n "; - error += cmQtAutoGen::Quoted(this->QrcFile); - this->LogError(cmQtAutoGen::RCC, error); + if (!RccListOptions_.empty()) { + // Start a rcc list process and parse the output + if (Process_) { + // Process is running already + if (Process_->IsFinished()) { + // Process is finished + if (!ProcessResult_.error()) { + // Process success + std::string parseError; + if (!RccListParseOutput(ProcessResult_.StdOut, ProcessResult_.StdErr, + Inputs_, parseError)) { + Log().ErrorFile(GeneratorT::RCC, QrcFile_, parseError); + Error_ = true; + } + } else { + Log().ErrorFile(GeneratorT::RCC, QrcFile_, + ProcessResult_.ErrorMessage); + Error_ = true; + } + // Clean up + Process_.reset(); + ProcessResult_.reset(); + } else { + // Process is not finished, yet. + return false; + } + } else { + // Start a new process + // rcc prints relative entry paths when started in the directory of the + // qrc file with a pathless qrc file name argument. + // This is important because on Windows absolute paths returned by rcc + // might contain bad multibyte characters when the qrc file path + // contains non-ASCII pcharacters. + std::vector<std::string> cmd; + cmd.push_back(RccExecutable_); + cmd.insert(cmd.end(), RccListOptions_.begin(), RccListOptions_.end()); + cmd.push_back(QrcFileName_); + // We're done here if the process fails to start + return !StartProcess(QrcFileDir_, cmd, false); } - success = false; - } - if (success && !generate && !cmSystemTools::FileExists(rccFileAbs.c_str())) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(rccFileAbs); - generateReason += " from its source file "; - generateReason += cmQtAutoGen::Quoted(this->QrcFile); - generateReason += " because it doesn't exist"; + } else { + // rcc does not support the --list command. + // Read the qrc file content and parse it. + std::string qrcContent; + if (FileSys().FileRead(GeneratorT::RCC, qrcContent, QrcFile_)) { + RccListParseContent(qrcContent, Inputs_); } - generate = true; } - if (success && !generate && this->SettingsChanged) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(rccFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(this->QrcFile); - generateReason += " because the RCC settings changed"; - } - generate = true; + + if (!Inputs_.empty()) { + // Convert relative paths to absolute paths + RccListConvertFullPath(QrcFileDir_, Inputs_); + } + + return true; +} + +bool cmQtAutoGeneratorRcc::TestResources() +{ + if (Inputs_.empty()) { + return true; } - if (success && !generate) { + { std::string error; - if (FileIsOlderThan(rccFileAbs, this->QrcFile, &error)) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(rccFileAbs); - generateReason += " because it is older than "; - generateReason += cmQtAutoGen::Quoted(this->QrcFile); + for (std::string const& resFile : Inputs_) { + // Check if the resource file exists + if (!FileSys().FileExists(resFile)) { + error = "Could not find the resource file\n "; + error += Quoted(resFile); + error += '\n'; + Log().ErrorFile(GeneratorT::RCC, QrcFile_, error); + Error_ = true; + break; } - generate = true; - } else { + // Check if the resource file is newer than the build file + if (FileSys().FileIsOlderThan(RccFileBuild_, resFile, &error)) { + if (Log().Verbose()) { + std::string reason = "Generating "; + reason += Quoted(RccFileBuild_); + reason += " from "; + reason += Quoted(QrcFile_); + reason += " because it is older than "; + reason += Quoted(resFile); + Log().Info(GeneratorT::RCC, reason); + } + Generate_ = true; + break; + } + // Print error and break on demand if (!error.empty()) { - this->LogError(cmQtAutoGen::RCC, error); - success = false; + Log().ErrorFile(GeneratorT::RCC, QrcFile_, error); + Error_ = true; + break; } } } - if (success && !generate) { - // Acquire input file list - std::vector<std::string> readFiles; - std::vector<std::string> const* files = nullptr; - if (!this->Inputs.empty()) { - files = &this->Inputs; - } else { - // Read input file list from qrc file + + return Generate_; +} + +void cmQtAutoGeneratorRcc::TestInfoFile() +{ + // Test if the rcc output file is older than the info file + { + bool isOlder = false; + { std::string error; - if (cmQtAutoGen::RccListInputs(this->RccExecutable, this->RccListOptions, - this->QrcFile, readFiles, &error)) { - files = &readFiles; - } else { - this->LogFileError(cmQtAutoGen::RCC, this->QrcFile, error); - success = false; + isOlder = FileSys().FileIsOlderThan(RccFileBuild_, InfoFile(), &error); + if (!error.empty()) { + Log().ErrorFile(GeneratorT::RCC, QrcFile_, error); + Error_ = true; } } - // Test if any input file is newer than the build file - if (files != nullptr) { - std::string error; - for (std::string const& resFile : *files) { - if (!cmSystemTools::FileExists(resFile.c_str())) { - error = "Could not find the file\n "; - error += cmQtAutoGen::Quoted(resFile); - error += "\nwhich is listed in\n "; - error += cmQtAutoGen::Quoted(this->QrcFile); - break; - } - if (FileIsOlderThan(rccFileAbs, resFile, &error)) { - if (this->GetVerbose()) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(rccFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(this->QrcFile); - generateReason += " because it is older than "; - generateReason += cmQtAutoGen::Quoted(resFile); - } - generate = true; - break; - } - if (!error.empty()) { - break; - } - } - // Print error - if (!error.empty()) { - this->LogError(cmQtAutoGen::RCC, error); - success = false; + if (isOlder) { + if (Log().Verbose()) { + std::string reason = "Touching "; + reason += Quoted(RccFileBuild_); + reason += " because it is older than "; + reason += Quoted(InfoFile()); + Log().Info(GeneratorT::RCC, reason); } + // Touch build file + FileSys().Touch(RccFileBuild_); + BuildFileChanged_ = true; } } - // Regenerate on demand - if (generate) { - // Log - if (this->GetVerbose()) { - this->LogBold("Generating RCC source " + rccFileRel); - this->LogInfo(cmQtAutoGen::RCC, generateReason); - } +} - // Make sure the parent directory exists - if (this->MakeParentDirectory(cmQtAutoGen::RCC, rccFileAbs)) { - // Compose rcc command - std::vector<std::string> cmd; - cmd.push_back(this->RccExecutable); - cmd.insert(cmd.end(), this->Options.begin(), this->Options.end()); - cmd.push_back("-o"); - cmd.push_back(rccFileAbs); - cmd.push_back(this->QrcFile); - - std::string output; - if (this->RunCommand(cmd, output)) { - // Success - rccGenerated = true; +void cmQtAutoGeneratorRcc::GenerateParentDir() +{ + // Make sure the parent directory exists + if (!FileSys().MakeParentDirectory(GeneratorT::RCC, RccFileBuild_)) { + Error_ = true; + } +} + +/** + * @return True when finished + */ +bool cmQtAutoGeneratorRcc::GenerateRcc() +{ + if (!Generate_) { + // Nothing to do + return true; + } + + if (Process_) { + // Process is running already + if (Process_->IsFinished()) { + // Process is finished + if (!ProcessResult_.error()) { + // Process success + BuildFileChanged_ = true; } else { + // Process failed { - std::string emsg = "rcc failed for\n "; - emsg += cmQtAutoGen::Quoted(this->QrcFile); - this->LogCommandError(cmQtAutoGen::RCC, emsg, cmd, output); + std::string emsg = "The rcc process failed to compile\n "; + emsg += Quoted(QrcFile_); + emsg += "\ninto\n "; + emsg += Quoted(RccFileBuild_); + if (ProcessResult_.error()) { + emsg += "\n"; + emsg += ProcessResult_.ErrorMessage; + } + Log().ErrorCommand(GeneratorT::RCC, emsg, Process_->Setup().Command, + ProcessResult_.StdOut); } - cmSystemTools::RemoveFile(rccFileAbs); - success = false; + FileSys().FileRemove(RccFileBuild_); + Error_ = true; } + // Clean up + Process_.reset(); + ProcessResult_.reset(); } else { - // Parent directory creation failed - success = false; + // Process is not finished, yet. + return false; } + } else { + // Start a rcc process + std::vector<std::string> cmd; + cmd.push_back(RccExecutable_); + cmd.insert(cmd.end(), Options_.begin(), Options_.end()); + cmd.push_back("-o"); + cmd.push_back(RccFileBuild_); + cmd.push_back(QrcFile_); + // We're done here if the process fails to start + return !StartProcess(AutogenBuildDir_, cmd, true); } + return true; +} + +void cmQtAutoGeneratorRcc::GenerateWrapper() +{ // Generate a wrapper source file on demand - if (success && (this->MultiConfig == cmQtAutoGen::WRAP)) { + if (MultiConfig_ == MultiConfigT::WRAPPER) { // Wrapper file name - std::string const& wrapperFileAbs = this->RccFile; - std::string const wrapperFileRel = cmSystemTools::RelativePath( - this->AutogenBuildDir.c_str(), wrapperFileAbs.c_str()); + std::string const& wrapperAbs = RccFile_; // Wrapper file content std::string content = "// This is an autogenerated configuration " "wrapper file. Changes will be overwritten.\n" "#include \""; - content += cmSystemTools::GetFilenameName(rccFileRel); + content += cmSystemTools::GetFilenameName(RccFileBuild_); content += "\"\n"; // Write content to file - if (this->FileDiffers(wrapperFileAbs, content)) { + if (FileSys().FileDiffers(wrapperAbs, content)) { // Write new wrapper file - if (this->GetVerbose()) { - this->LogBold("Generating RCC wrapper " + wrapperFileRel); + if (Log().Verbose()) { + Log().Info(GeneratorT::RCC, "Generating RCC wrapper " + wrapperAbs); } - if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) { - this->LogFileError(cmQtAutoGen::RCC, wrapperFileAbs, - "rcc wrapper file writing failed"); - success = false; + if (!FileSys().FileWrite(GeneratorT::RCC, wrapperAbs, content)) { + Log().ErrorFile(GeneratorT::RCC, wrapperAbs, + "RCC wrapper file writing failed"); + Error_ = true; } - } else if (rccGenerated) { + } else if (BuildFileChanged_) { // Just touch the wrapper file - if (this->GetVerbose()) { - this->LogInfo(cmQtAutoGen::RCC, - "Touching RCC wrapper " + wrapperFileRel); + if (Log().Verbose()) { + Log().Info(GeneratorT::RCC, "Touching RCC wrapper " + wrapperAbs); } - cmSystemTools::Touch(wrapperFileAbs, false); + FileSys().Touch(wrapperAbs); } } +} + +bool cmQtAutoGeneratorRcc::StartProcess( + std::string const& workingDirectory, std::vector<std::string> const& command, + bool mergedOutput) +{ + // Log command + if (Log().Verbose()) { + std::string msg = "Running command:\n"; + msg += QuotedCommand(command); + msg += '\n'; + Log().Info(GeneratorT::RCC, msg); + } - return success; + // Create process handler + Process_ = cm::make_unique<ReadOnlyProcessT>(); + Process_->setup(&ProcessResult_, mergedOutput, command, workingDirectory); + // Start process + if (!Process_->start(UVLoop(), + std::bind(&cm::uv_async_ptr::send, &UVRequest()))) { + Log().ErrorFile(GeneratorT::RCC, QrcFile_, ProcessResult_.ErrorMessage); + Error_ = true; + // Clean up + Process_.reset(); + ProcessResult_.reset(); + return false; + } + return true; } diff --git a/Source/cmQtAutoGeneratorRcc.h b/Source/cmQtAutoGeneratorRcc.h index 0e3f690..8a69c6c 100644 --- a/Source/cmQtAutoGeneratorRcc.h +++ b/Source/cmQtAutoGeneratorRcc.h @@ -5,52 +5,97 @@ #include "cmConfigure.h" // IWYU pragma: keep -#include "cmFilePathChecksum.h" #include "cmQtAutoGen.h" #include "cmQtAutoGenerator.h" +#include "cm_uv.h" #include <string> #include <vector> class cmMakefile; +// @brief AUTORCC generator class cmQtAutoGeneratorRcc : public cmQtAutoGenerator { CM_DISABLE_COPY(cmQtAutoGeneratorRcc) public: cmQtAutoGeneratorRcc(); + ~cmQtAutoGeneratorRcc() override; private: - // -- Initialization & settings - bool InfoFileRead(cmMakefile* makefile); - void SettingsFileRead(cmMakefile* makefile); - bool SettingsFileWrite(); - // -- Central processing - bool Process(cmMakefile* makefile) override; - bool RccGenerate(); + // -- Types + /// @brief Processing stage + enum class StageT + { + SETTINGS_READ, + TEST_QRC_RCC_FILES, + TEST_RESOURCES_READ, + TEST_RESOURCES, + TEST_INFO_FILE, + GENERATE, + GENERATE_RCC, + GENERATE_WRAPPER, + SETTINGS_WRITE, + FINISH, + END + }; + + // -- 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(); + // -- Tests + bool TestQrcRccFiles(); + bool TestResourcesRead(); + bool TestResources(); + void TestInfoFile(); + // -- Generation + void GenerateParentDir(); + bool GenerateRcc(); + void GenerateWrapper(); + + // -- Utility + bool StartProcess(std::string const& workingDirectory, + std::vector<std::string> const& command, + bool mergedOutput); + +private: // -- Config settings - std::string ConfigSuffix; - cmQtAutoGen::MultiConfig MultiConfig; - // -- Settings - bool SettingsChanged; - std::string SettingsFile; - std::string SettingsString; + bool SettingsChanged_; + std::string ConfigSuffix_; + MultiConfigT MultiConfig_; // -- Directories - std::string ProjectSourceDir; - std::string ProjectBinaryDir; - std::string CurrentSourceDir; - std::string CurrentBinaryDir; - std::string AutogenBuildDir; - cmFilePathChecksum FilePathChecksum; + std::string AutogenBuildDir_; // -- Qt environment - std::string RccExecutable; - std::vector<std::string> RccListOptions; + std::string RccExecutable_; + std::vector<std::string> RccListOptions_; // -- Job - std::string QrcFile; - std::string RccFile; - std::vector<std::string> Options; - std::vector<std::string> Inputs; + std::string QrcFile_; + std::string QrcFileName_; + std::string QrcFileDir_; + std::string RccFile_; + std::string RccFileWrapper_; + std::string RccFileBuild_; + std::vector<std::string> Options_; + std::vector<std::string> Inputs_; + // -- Subprocess + ProcessResultT ProcessResult_; + std::unique_ptr<ReadOnlyProcessT> Process_; + // -- Settings file + std::string SettingsFile_; + std::string SettingsString_; + // -- libuv loop + StageT Stage_; + bool Error_; + bool Generate_; + bool BuildFileChanged_; }; #endif diff --git a/Source/cmServerProtocol.cxx b/Source/cmServerProtocol.cxx index d745c49..6b7143b 100644 --- a/Source/cmServerProtocol.cxx +++ b/Source/cmServerProtocol.cxx @@ -748,7 +748,8 @@ static Json::Value DumpSourceFilesList( return result; } -static Json::Value DumpCTestInfo(cmTest* testInfo) +static Json::Value DumpCTestInfo(cmLocalGenerator* lg, cmTest* testInfo, + const std::string& config) { Json::Value result = Json::objectValue; result[kCTEST_NAME] = testInfo->GetName(); @@ -760,14 +761,24 @@ static Json::Value DumpCTestInfo(cmTest* testInfo) command.append(cmd); command.append(" "); } - result[kCTEST_COMMAND] = command; + + // Remove any config specific variables from the output. + cmGeneratorExpression ge; + auto cge = ge.Parse(command.c_str()); + const char* processed = cge->Evaluate(lg, config); + + result[kCTEST_COMMAND] = processed; // Build up the list of properties that may have been specified Json::Value properties = Json::arrayValue; for (auto& prop : testInfo->GetProperties()) { Json::Value entry = Json::objectValue; entry[kKEY_KEY] = prop.first; - entry[kVALUE_KEY] = prop.second.GetValue(); + + // Remove config variables from the value too. + auto cge_value = ge.Parse(prop.second.GetValue()); + const char* processed_value = cge_value->Evaluate(lg, config); + entry[kVALUE_KEY] = processed_value; properties.append(entry); } result[kPROPERTIES_KEY] = properties; @@ -775,13 +786,14 @@ static Json::Value DumpCTestInfo(cmTest* testInfo) return result; } -static void DumpMakefileTests(cmMakefile* mf, const std::string& config, +static void DumpMakefileTests(cmLocalGenerator* lg, const std::string& config, Json::Value* result) { + auto mf = lg->GetMakefile(); std::vector<cmTest*> tests; mf->GetTests(config, tests); for (auto test : tests) { - Json::Value tmp = DumpCTestInfo(test); + Json::Value tmp = DumpCTestInfo(lg, test, config); if (!tmp.isNull()) { result->append(tmp); } @@ -805,8 +817,7 @@ static Json::Value DumpCTestProjectList(const cmake* cm, for (const auto& lg : projectIt.second) { // Make sure they're generated. lg->GenerateTestFiles(); - cmMakefile* mf = lg->GetMakefile(); - DumpMakefileTests(mf, config, &tests); + DumpMakefileTests(lg, config, &tests); } pObj[kCTEST_INFO] = tests; diff --git a/Source/cmSourceFile.cxx b/Source/cmSourceFile.cxx index d5475d2..215f974 100644 --- a/Source/cmSourceFile.cxx +++ b/Source/cmSourceFile.cxx @@ -12,8 +12,9 @@ #include "cmSystemTools.h" #include "cmake.h" -cmSourceFile::cmSourceFile(cmMakefile* mf, const std::string& name) - : Location(mf, name) +cmSourceFile::cmSourceFile(cmMakefile* mf, const std::string& name, + cmSourceFileLocationKind kind) + : Location(mf, name, kind) { this->CustomCommand = nullptr; this->FindFullPathFailed = false; diff --git a/Source/cmSourceFile.h b/Source/cmSourceFile.h index c2105d2..1516d98 100644 --- a/Source/cmSourceFile.h +++ b/Source/cmSourceFile.h @@ -7,6 +7,7 @@ #include "cmPropertyMap.h" #include "cmSourceFileLocation.h" +#include "cmSourceFileLocationKind.h" #include <string> #include <vector> @@ -27,7 +28,9 @@ public: * Construct with the makefile storing the source and the initial * name referencing it. */ - cmSourceFile(cmMakefile* mf, const std::string& name); + cmSourceFile( + cmMakefile* mf, const std::string& name, + cmSourceFileLocationKind kind = cmSourceFileLocationKind::Ambiguous); ~cmSourceFile(); @@ -120,7 +123,8 @@ private: #define CM_HEADER_REGEX "\\.(h|hh|h\\+\\+|hm|hpp|hxx|in|txx|inl)$" #define CM_SOURCE_REGEX \ - "\\.(C|M|c|c\\+\\+|cc|cpp|cxx|f|f90|for|fpp|ftn|m|mm|rc|def|r|odl|idl|hpj" \ + "\\.(C|M|c|c\\+\\+|cc|cpp|cxx|cu|f|f90|for|fpp|ftn|m|mm|rc|def|r|odl|idl|" \ + "hpj" \ "|bat)$" #define CM_RESOURCE_REGEX "\\.(pdf|plist|png|jpeg|jpg|storyboard|xcassets)$" diff --git a/Source/cmSourceFileLocation.cxx b/Source/cmSourceFileLocation.cxx index 6add7b3..5558ef3 100644 --- a/Source/cmSourceFileLocation.cxx +++ b/Source/cmSourceFileLocation.cxx @@ -27,7 +27,8 @@ cmSourceFileLocation::cmSourceFileLocation(const cmSourceFileLocation& loc) } cmSourceFileLocation::cmSourceFileLocation(cmMakefile const* mf, - const std::string& name) + const std::string& name, + cmSourceFileLocationKind kind) : Makefile(mf) { this->AmbiguousDirectory = !cmSystemTools::FileIsFullPath(name.c_str()); @@ -37,7 +38,12 @@ cmSourceFileLocation::cmSourceFileLocation(cmMakefile const* mf, this->Directory = cmSystemTools::CollapseFullPath(this->Directory); } this->Name = cmSystemTools::GetFilenameName(name); - this->UpdateExtension(name); + if (kind == cmSourceFileLocationKind::Known) { + this->DirectoryUseSource(); + this->AmbiguousExtension = false; + } else { + this->UpdateExtension(name); + } } void cmSourceFileLocation::Update(cmSourceFileLocation const& loc) diff --git a/Source/cmSourceFileLocation.h b/Source/cmSourceFileLocation.h index a6819bd..f325e54 100644 --- a/Source/cmSourceFileLocation.h +++ b/Source/cmSourceFileLocation.h @@ -7,6 +7,8 @@ #include <string> +#include "cmSourceFileLocationKind.h" + class cmMakefile; /** \class cmSourceFileLocation @@ -26,7 +28,9 @@ public: * Construct for a source file created in a given cmMakefile * instance with an initial name. */ - cmSourceFileLocation(cmMakefile const* mf, const std::string& name); + cmSourceFileLocation( + cmMakefile const* mf, const std::string& name, + cmSourceFileLocationKind kind = cmSourceFileLocationKind::Ambiguous); cmSourceFileLocation(); cmSourceFileLocation(const cmSourceFileLocation& loc); diff --git a/Source/cmSourceFileLocationKind.h b/Source/cmSourceFileLocationKind.h new file mode 100644 index 0000000..dd4c6dd --- /dev/null +++ b/Source/cmSourceFileLocationKind.h @@ -0,0 +1,15 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmSourceFileLocationKind_h +#define cmSourceFileLocationKind_h + +enum class cmSourceFileLocationKind +{ + // The location is user-specified and may be ambiguous. + Ambiguous, + // The location is known to be at the given location; do not try to guess at + // extensions or absolute path. + Known +}; + +#endif diff --git a/Source/cmSourceGroup.cxx b/Source/cmSourceGroup.cxx index 18bcb49..12ef62b 100644 --- a/Source/cmSourceGroup.cxx +++ b/Source/cmSourceGroup.cxx @@ -8,7 +8,7 @@ public: std::vector<cmSourceGroup> GroupChildren; }; -cmSourceGroup::cmSourceGroup(const char* name, const char* regex, +cmSourceGroup::cmSourceGroup(const std::string& name, const char* regex, const char* parentName) : Name(name) { @@ -70,14 +70,14 @@ std::string const& cmSourceGroup::GetFullName() const return this->FullName; } -bool cmSourceGroup::MatchesRegex(const char* name) +bool cmSourceGroup::MatchesRegex(const std::string& name) { return this->GroupRegex.find(name); } -bool cmSourceGroup::MatchesFiles(const char* name) +bool cmSourceGroup::MatchesFiles(const std::string& name) const { - return this->GroupFiles.find(name) != this->GroupFiles.end(); + return this->GroupFiles.find(name) != this->GroupFiles.cend(); } void cmSourceGroup::AssignSource(const cmSourceFile* sf) @@ -95,21 +95,12 @@ void cmSourceGroup::AddChild(cmSourceGroup const& child) this->Internal->GroupChildren.push_back(child); } -cmSourceGroup* cmSourceGroup::LookupChild(const char* name) const +cmSourceGroup* cmSourceGroup::LookupChild(const std::string& name) { - // initializing iterators - std::vector<cmSourceGroup>::const_iterator iter = - this->Internal->GroupChildren.begin(); - const std::vector<cmSourceGroup>::const_iterator end = - this->Internal->GroupChildren.end(); - - // st - for (; iter != end; ++iter) { - std::string const& sgName = iter->GetName(); - + for (cmSourceGroup& group : this->Internal->GroupChildren) { // look if descenened is the one were looking for - if (sgName == name) { - return const_cast<cmSourceGroup*>(&(*iter)); // if it so return it + if (group.GetName() == name) { + return (&group); // if it so return it } } @@ -117,19 +108,13 @@ cmSourceGroup* cmSourceGroup::LookupChild(const char* name) const return nullptr; } -cmSourceGroup* cmSourceGroup::MatchChildrenFiles(const char* name) +cmSourceGroup* cmSourceGroup::MatchChildrenFiles(const std::string& name) { - // initializing iterators - std::vector<cmSourceGroup>::iterator iter = - this->Internal->GroupChildren.begin(); - std::vector<cmSourceGroup>::iterator end = - this->Internal->GroupChildren.end(); - if (this->MatchesFiles(name)) { return this; } - for (; iter != end; ++iter) { - cmSourceGroup* result = iter->MatchChildrenFiles(name); + for (cmSourceGroup& group : this->Internal->GroupChildren) { + cmSourceGroup* result = group.MatchChildrenFiles(name); if (result) { return result; } @@ -137,16 +122,10 @@ cmSourceGroup* cmSourceGroup::MatchChildrenFiles(const char* name) return nullptr; } -cmSourceGroup* cmSourceGroup::MatchChildrenRegex(const char* name) +cmSourceGroup* cmSourceGroup::MatchChildrenRegex(const std::string& name) { - // initializing iterators - std::vector<cmSourceGroup>::iterator iter = - this->Internal->GroupChildren.begin(); - std::vector<cmSourceGroup>::iterator end = - this->Internal->GroupChildren.end(); - - for (; iter != end; ++iter) { - cmSourceGroup* result = iter->MatchChildrenRegex(name); + for (cmSourceGroup& group : this->Internal->GroupChildren) { + cmSourceGroup* result = group.MatchChildrenRegex(name); if (result) { return result; } diff --git a/Source/cmSourceGroup.h b/Source/cmSourceGroup.h index 7c7c35f..b39f8dd 100644 --- a/Source/cmSourceGroup.h +++ b/Source/cmSourceGroup.h @@ -26,7 +26,7 @@ class cmSourceGroupInternals; class cmSourceGroup { public: - cmSourceGroup(const char* name, const char* regex, + cmSourceGroup(const std::string& name, const char* regex, const char* parentName = nullptr); cmSourceGroup(cmSourceGroup const& r); ~cmSourceGroup(); @@ -50,7 +50,7 @@ public: /** * Looks up child and returns it */ - cmSourceGroup* LookupChild(const char* name) const; + cmSourceGroup* LookupChild(const std::string& name); /** * Get the name of this group. @@ -65,23 +65,23 @@ public: /** * Check if the given name matches this group's regex. */ - bool MatchesRegex(const char* name); + bool MatchesRegex(const std::string& name); /** * Check if the given name matches this group's explicit file list. */ - bool MatchesFiles(const char* name); + bool MatchesFiles(const std::string& name) const; /** * Check if the given name matches this group's explicit file list * in children. */ - cmSourceGroup* MatchChildrenFiles(const char* name); + cmSourceGroup* MatchChildrenFiles(const std::string& name); /** * Check if the given name matches this group's regex in children. */ - cmSourceGroup* MatchChildrenRegex(const char* name); + cmSourceGroup* MatchChildrenRegex(const std::string& name); /** * Assign the given source file to this group. Used only by diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 5d1f5f7..c321236 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -1308,6 +1308,9 @@ cmSystemTools::FileFormat cmSystemTools::GetFileFormat(const char* cext) if (ext == "java" || ext == ".java") { return cmSystemTools::JAVA_FILE_FORMAT; } + if (ext == "cu" || ext == ".cu") { + return cmSystemTools::CUDA_FILE_FORMAT; + } if (ext == "H" || ext == ".H" || ext == "h" || ext == ".h" || ext == "h++" || ext == ".h++" || ext == "hm" || ext == ".hm" || ext == "hpp" || ext == ".hpp" || ext == "hxx" || ext == ".hxx" || ext == "in" || diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index cf7de5a..d29ba56 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -285,6 +285,7 @@ public: CXX_FILE_FORMAT, FORTRAN_FILE_FORMAT, JAVA_FILE_FORMAT, + CUDA_FILE_FORMAT, HEADER_FILE_FORMAT, RESOURCE_FILE_FORMAT, DEFINITION_FILE_FORMAT, diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index de23b08..663a4c9 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -23,6 +23,7 @@ #include "cmProperty.h" #include "cmSourceFile.h" #include "cmSourceFileLocation.h" +#include "cmSourceFileLocationKind.h" #include "cmState.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" @@ -246,6 +247,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type, this->SetPropertyDefault("AUTOMOC", nullptr); this->SetPropertyDefault("AUTOUIC", nullptr); this->SetPropertyDefault("AUTORCC", nullptr); + this->SetPropertyDefault("AUTOGEN_PARALLEL", nullptr); this->SetPropertyDefault("AUTOMOC_COMPILER_PREDEFINES", nullptr); this->SetPropertyDefault("AUTOMOC_DEPEND_FILTERS", nullptr); this->SetPropertyDefault("AUTOMOC_MACRO_NAMES", nullptr); @@ -606,7 +608,8 @@ public: cmSourceFile* cmTarget::AddSource(const std::string& src) { - cmSourceFileLocation sfl(this->Makefile, src); + cmSourceFileLocation sfl(this->Makefile, src, + cmSourceFileLocationKind::Known); if (std::find_if(this->Internal->SourceEntries.begin(), this->Internal->SourceEntries.end(), TargetPropertyEntryFinder(sfl)) == @@ -618,7 +621,8 @@ cmSourceFile* cmTarget::AddSource(const std::string& src) if (cmGeneratorExpression::Find(src) != std::string::npos) { return nullptr; } - return this->Makefile->GetOrCreateSource(src); + return this->Makefile->GetOrCreateSource(src, false, + cmSourceFileLocationKind::Known); } void cmTarget::AddLinkDirectory(const std::string& d) diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index f997a11..1b09600 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -2319,6 +2319,18 @@ void cmVisualStudio10TargetGenerator::OutputLinkIncremental( } } +std::vector<std::string> cmVisualStudio10TargetGenerator::GetIncludes( + std::string const& config, std::string const& lang) const +{ + std::vector<std::string> includes; + this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget, + lang, config); + for (std::string& i : includes) { + this->ConvertToWindowsSlash(i); + } + return includes; +} + bool cmVisualStudio10TargetGenerator::ComputeClOptions() { for (std::string const& i : this->Configurations) { @@ -2379,6 +2391,7 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( } } } + this->LangForClCompile = langForClCompile; if (!langForClCompile.empty()) { std::string baseFlagVar = "CMAKE_"; baseFlagVar += langForClCompile; @@ -2422,8 +2435,10 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( std::vector<std::string> targetDefines; switch (this->ProjectType) { case vcxproj: - this->GeneratorTarget->GetCompileDefinitions(targetDefines, configName, - "CXX"); + if (!langForClCompile.empty()) { + this->GeneratorTarget->GetCompileDefinitions(targetDefines, configName, + langForClCompile); + } break; case csproj: this->GeneratorTarget->GetCompileDefinitions(targetDefines, configName, @@ -2487,7 +2502,7 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( } void cmVisualStudio10TargetGenerator::WriteClOptions( - std::string const& configName, std::vector<std::string> const& includes) + std::string const& configName) { Options& clOptions = *(this->ClOptions[configName]); if (this->ProjectType == csproj) { @@ -2495,12 +2510,16 @@ void cmVisualStudio10TargetGenerator::WriteClOptions( } this->WriteString("<ClCompile>\n", 2); clOptions.PrependInheritedString("AdditionalOptions"); - clOptions.AppendFlag("AdditionalIncludeDirectories", includes); + if (!this->LangForClCompile.empty()) { + std::vector<std::string> const includes = + this->GetIncludes(configName, this->LangForClCompile); + clOptions.AppendFlag("AdditionalIncludeDirectories", includes); + } clOptions.AppendFlag("AdditionalIncludeDirectories", "%(AdditionalIncludeDirectories)"); clOptions.OutputFlagMap(*this->BuildFileStream, " "); clOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", - "\n", "CXX"); + "\n", this->LangForClCompile); if (this->NsightTegra) { if (const char* processMax = @@ -2581,7 +2600,7 @@ bool cmVisualStudio10TargetGenerator::ComputeRcOptions( } void cmVisualStudio10TargetGenerator::WriteRCOptions( - std::string const& configName, std::vector<std::string> const& includes) + std::string const& configName) { if (!this->MSTools) { return; @@ -2591,6 +2610,8 @@ void cmVisualStudio10TargetGenerator::WriteRCOptions( Options& rcOptions = *(this->RcOptions[configName]); rcOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", "\n", "RC"); + std::vector<std::string> const includes = + this->GetIncludes(configName, "RC"); rcOptions.AppendFlag("AdditionalIncludeDirectories", includes); rcOptions.AppendFlag("AdditionalIncludeDirectories", "%(AdditionalIncludeDirectories)"); @@ -2640,6 +2661,13 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( cudaOptions.Parse(defineFlags.c_str()); cudaOptions.ParseFinish(); + // If we haven't explicitly enabled GPU debug information + // explicitly disable it + if (!cudaOptions.HasFlag("GPUDebugInfo")) { + cudaOptions.AddFlag("GPUDebugInfo", "false"); + } + + bool notPtx = true; if (this->GeneratorTarget->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION")) { cudaOptions.AddFlag("GenerateRelocatableDeviceCode", "true"); } else if (this->GeneratorTarget->GetPropertyAsBool( @@ -2648,6 +2676,16 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( // We drop the %(Extension) component as CMake expects all PTX files // to not have the source file extension at all cudaOptions.AddFlag("CompileOut", "$(IntDir)%(Filename).ptx"); + notPtx = false; + } + + if (notPtx && + cmSystemTools::VersionCompareGreaterEq( + "8.0", this->GlobalGenerator->GetPlatformToolsetCudaString())) { + // Explicitly state that we want this file to be treated as a + // CUDA file no matter what the file extensions is + // This is only needed for < CUDA 9 + cudaOptions.AppendFlagString("AdditionalOptions", "-x cu"); } // CUDA automatically passes the proper '--machine' flag to nvcc @@ -2695,7 +2733,7 @@ bool cmVisualStudio10TargetGenerator::ComputeCudaOptions( } void cmVisualStudio10TargetGenerator::WriteCudaOptions( - std::string const& configName, std::vector<std::string> const& includes) + std::string const& configName) { if (!this->MSTools || !this->GlobalGenerator->IsCudaEnabled()) { return; @@ -2703,6 +2741,8 @@ void cmVisualStudio10TargetGenerator::WriteCudaOptions( this->WriteString("<CudaCompile>\n", 2); Options& cudaOptions = *(this->CudaOptions[configName]); + std::vector<std::string> const includes = + this->GetIncludes(configName, "CUDA"); cudaOptions.AppendFlag("Include", includes); cudaOptions.AppendFlag("Include", "%(Include)"); cudaOptions.OutputPreprocessorDefinitions(*this->BuildFileStream, " ", @@ -2817,7 +2857,7 @@ bool cmVisualStudio10TargetGenerator::ComputeMasmOptions( } void cmVisualStudio10TargetGenerator::WriteMasmOptions( - std::string const& configName, std::vector<std::string> const& includes) + std::string const& configName) { if (!this->MSTools || !this->GlobalGenerator->IsMasmEnabled()) { return; @@ -2830,6 +2870,8 @@ void cmVisualStudio10TargetGenerator::WriteMasmOptions( "\n", "ASM_MASM"); Options& masmOptions = *(this->MasmOptions[configName]); + std::vector<std::string> const includes = + this->GetIncludes(configName, "ASM_MASM"); masmOptions.AppendFlag("IncludePaths", includes); masmOptions.AppendFlag("IncludePaths", "%(IncludePaths)"); masmOptions.PrependInheritedString("AdditionalOptions"); @@ -2874,13 +2916,15 @@ bool cmVisualStudio10TargetGenerator::ComputeNasmOptions( } void cmVisualStudio10TargetGenerator::WriteNasmOptions( - std::string const& configName, std::vector<std::string> includes) + std::string const& configName) { if (!this->GlobalGenerator->IsNasmEnabled()) { return; } this->WriteString("<NASM>\n", 2); + std::vector<std::string> includes = + this->GetIncludes(configName, "ASM_NASM"); Options& nasmOptions = *(this->NasmOptions[configName]); for (size_t i = 0; i < includes.size(); i++) { includes[i] += "\\"; @@ -3428,7 +3472,7 @@ void cmVisualStudio10TargetGenerator::AddTargetsFileAndConfigPair( } void cmVisualStudio10TargetGenerator::WriteMidlOptions( - std::string const& /*config*/, std::vector<std::string> const& includes) + std::string const& configName) { if (!this->MSTools) { return; @@ -3454,6 +3498,8 @@ void cmVisualStudio10TargetGenerator::WriteMidlOptions( // on the CMake side? this->WriteString("<Midl>\n", 2); this->WriteString("<AdditionalIncludeDirectories>", 3); + std::vector<std::string> const includes = + this->GetIncludes(configName, "MIDL"); for (std::string const& i : includes) { *this->BuildFileStream << cmVS10EscapeXML(i) << ";"; } @@ -3478,25 +3524,19 @@ void cmVisualStudio10TargetGenerator::WriteItemDefinitionGroups() return; } for (std::string const& i : this->Configurations) { - std::vector<std::string> includes; - this->LocalGenerator->GetIncludeDirectories(includes, - this->GeneratorTarget, "C", i); - for (std::string& ii : includes) { - this->ConvertToWindowsSlash(ii); - } this->WritePlatformConfigTag("ItemDefinitionGroup", i, 1); *this->BuildFileStream << "\n"; // output cl compile flags <ClCompile></ClCompile> if (this->GeneratorTarget->GetType() <= cmStateEnums::OBJECT_LIBRARY) { - this->WriteClOptions(i, includes); + this->WriteClOptions(i); // output rc compile flags <ResourceCompile></ResourceCompile> - this->WriteRCOptions(i, includes); - this->WriteCudaOptions(i, includes); - this->WriteMasmOptions(i, includes); - this->WriteNasmOptions(i, includes); + this->WriteRCOptions(i); + this->WriteCudaOptions(i); + this->WriteMasmOptions(i); + this->WriteNasmOptions(i); } // output midl flags <Midl></Midl> - this->WriteMidlOptions(i, includes); + this->WriteMidlOptions(i); // write events if (this->ProjectType != csproj) { this->WriteEvents(i); diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h index fb24f1a..c346164 100644 --- a/Source/cmVisualStudio10TargetGenerator.h +++ b/Source/cmVisualStudio10TargetGenerator.h @@ -90,18 +90,18 @@ private: void WriteTargetSpecificReferences(); void WriteTargetsFileReferences(); + std::vector<std::string> GetIncludes(std::string const& config, + std::string const& lang) const; + bool ComputeClOptions(); bool ComputeClOptions(std::string const& configName); - void WriteClOptions(std::string const& config, - std::vector<std::string> const& includes); + void WriteClOptions(std::string const& config); bool ComputeRcOptions(); bool ComputeRcOptions(std::string const& config); - void WriteRCOptions(std::string const& config, - std::vector<std::string> const& includes); + void WriteRCOptions(std::string const& config); bool ComputeCudaOptions(); bool ComputeCudaOptions(std::string const& config); - void WriteCudaOptions(std::string const& config, - std::vector<std::string> const& includes); + void WriteCudaOptions(std::string const& config); bool ComputeCudaLinkOptions(); bool ComputeCudaLinkOptions(std::string const& config); @@ -109,20 +109,17 @@ private: bool ComputeMasmOptions(); bool ComputeMasmOptions(std::string const& config); - void WriteMasmOptions(std::string const& config, - std::vector<std::string> const& includes); + void WriteMasmOptions(std::string const& config); bool ComputeNasmOptions(); bool ComputeNasmOptions(std::string const& config); - void WriteNasmOptions(std::string const& config, - std::vector<std::string> includes); + void WriteNasmOptions(std::string const& config); bool ComputeLinkOptions(); bool ComputeLinkOptions(std::string const& config); bool ComputeLibOptions(); bool ComputeLibOptions(std::string const& config); void WriteLinkOptions(std::string const& config); - void WriteMidlOptions(std::string const& config, - std::vector<std::string> const& includes); + void WriteMidlOptions(std::string const& config); void WriteAntBuildOptions(std::string const& config); void OutputLinkIncremental(std::string const& configName); void WriteCustomRule(cmSourceFile const* source, @@ -180,6 +177,7 @@ private: OptionsMap MasmOptions; OptionsMap NasmOptions; OptionsMap LinkOptions; + std::string LangForClCompile; std::string PathToProjectFile; std::string ProjectFileExtension; enum VsProjectType diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx index 9a5986c..106bdff 100644 --- a/Source/cmVisualStudioGeneratorOptions.cxx +++ b/Source/cmVisualStudioGeneratorOptions.cxx @@ -239,20 +239,32 @@ void cmVisualStudioGeneratorOptions::FixCudaCodeGeneration() // It translates to -arch=<virtual> -code=<real>. cmSystemTools::ReplaceString(arch_name, "sm_", "compute_"); } - for (std::vector<std::string>::iterator ci = codes.begin(); - ci != codes.end(); ++ci) { - std::string entry = arch_name + "," + *ci; + for (auto const& c : codes) { + std::string entry = arch_name + "," + c; result.push_back(entry); } } - // Now add entries for the -gencode=<arch>,<code> pairs. - for (std::vector<std::string>::iterator ei = gencode.begin(); - ei != gencode.end(); ++ei) { - std::string entry = *ei; + // Now add entries for the following signatures: + // -gencode=<arch>,<code> + // -gencode=<arch>,[<code1>,<code2>] + // -gencode=<arch>,"<code1>,<code2>" + for (auto const& e : gencode) { + std::string entry = e; cmSystemTools::ReplaceString(entry, "arch=", ""); cmSystemTools::ReplaceString(entry, "code=", ""); - result.push_back(entry); + cmSystemTools::ReplaceString(entry, "[", ""); + cmSystemTools::ReplaceString(entry, "]", ""); + cmSystemTools::ReplaceString(entry, "\"", ""); + + std::vector<std::string> codes = cmSystemTools::tokenize(entry, ","); + if (codes.size() >= 2) { + auto gencode_arch = cm::cbegin(codes); + for (auto ci = gencode_arch + 1; ci != cm::cend(codes); ++ci) { + std::string code_entry = *gencode_arch + "," + *ci; + result.push_back(code_entry); + } + } } } diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 152352f..480646e 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -196,6 +196,7 @@ cmake::cmake(Role role) this->SourceFileExtensions.push_back("cc"); this->SourceFileExtensions.push_back("cpp"); this->SourceFileExtensions.push_back("cxx"); + this->SourceFileExtensions.push_back("cu"); this->SourceFileExtensions.push_back("m"); this->SourceFileExtensions.push_back("M"); this->SourceFileExtensions.push_back("mm"); diff --git a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt index f96283d..7dc7995 100644 --- a/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt +++ b/Tests/CMakeCommands/target_compile_definitions/CMakeLists.txt @@ -26,18 +26,18 @@ target_compile_definitions(consumer PRIVATE ) -if (CMAKE_GENERATOR MATCHES "Makefiles" OR CMAKE_GENERATOR MATCHES "Ninja") - target_sources(consumer PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/consumer.c" - ) - target_compile_definitions(consumer - PRIVATE - CONSUMER_LANG_$<COMPILE_LANGUAGE> - LANG_IS_CXX=$<COMPILE_LANGUAGE:CXX> - LANG_IS_C=$<COMPILE_LANGUAGE:C> - ) +target_sources(consumer PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/consumer.c" +) +target_compile_definitions(consumer + PRIVATE + CONSUMER_LANG_$<COMPILE_LANGUAGE> + LANG_IS_CXX=$<COMPILE_LANGUAGE:CXX> + LANG_IS_C=$<COMPILE_LANGUAGE:C> +) +if(CMAKE_GENERATOR MATCHES "Visual Studio|Xcode") target_compile_definitions(consumer - PRIVATE -DTEST_LANG_DEFINES + PRIVATE TEST_LANG_DEFINES_FOR_VISUAL_STUDIO_OR_XCODE ) endif() diff --git a/Tests/CMakeCommands/target_compile_definitions/consumer.c b/Tests/CMakeCommands/target_compile_definitions/consumer.c index 7931a6f..e134a8b 100644 --- a/Tests/CMakeCommands/target_compile_definitions/consumer.c +++ b/Tests/CMakeCommands/target_compile_definitions/consumer.c @@ -1,5 +1,23 @@ -#ifdef TEST_LANG_DEFINES +// Visual Studio allows only one set of flags for C and C++. +// In a target using C++ we pick the C++ flags even for C sources. +#ifdef TEST_LANG_DEFINES_FOR_VISUAL_STUDIO_OR_XCODE +#ifndef CONSUMER_LANG_CXX +#error Expected CONSUMER_LANG_CXX +#endif + +#ifdef CONSUMER_LANG_C +#error Unexpected CONSUMER_LANG_C +#endif + +#if !LANG_IS_CXX +#error Expected LANG_IS_CXX +#endif + +#if LANG_IS_C +#error Unexpected LANG_IS_C +#endif +#else #ifdef CONSUMER_LANG_CXX #error Unexpected CONSUMER_LANG_CXX #endif diff --git a/Tests/CMakeCommands/target_compile_definitions/consumer.cpp b/Tests/CMakeCommands/target_compile_definitions/consumer.cpp index 0202c17..69ea151 100644 --- a/Tests/CMakeCommands/target_compile_definitions/consumer.cpp +++ b/Tests/CMakeCommands/target_compile_definitions/consumer.cpp @@ -15,7 +15,6 @@ #error Expected DASH_D_DEFINE #endif -#ifdef TEST_LANG_DEFINES #ifndef CONSUMER_LANG_CXX #error Expected CONSUMER_LANG_CXX #endif @@ -31,7 +30,6 @@ #if LANG_IS_C #error Unexpected LANG_IS_C #endif -#endif int main() { diff --git a/Tests/CMakeCommands/target_include_directories/CMakeLists.txt b/Tests/CMakeCommands/target_include_directories/CMakeLists.txt index d57556a..8713d99 100644 --- a/Tests/CMakeCommands/target_include_directories/CMakeLists.txt +++ b/Tests/CMakeCommands/target_include_directories/CMakeLists.txt @@ -42,17 +42,17 @@ add_executable(consumer "${CMAKE_CURRENT_SOURCE_DIR}/consumer.cpp" ) -if (CMAKE_GENERATOR MATCHES "Makefiles" OR CMAKE_GENERATOR MATCHES "Ninja") - target_sources(consumer PRIVATE - "${CMAKE_CURRENT_SOURCE_DIR}/consumer.c" - ) - target_include_directories(consumer - PRIVATE - $<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_SOURCE_DIR}/cxx_only> - $<$<COMPILE_LANGUAGE:C>:${CMAKE_CURRENT_SOURCE_DIR}/c_only> - ) +target_sources(consumer PRIVATE + "${CMAKE_CURRENT_SOURCE_DIR}/consumer.c" +) +target_include_directories(consumer + PRIVATE + $<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_SOURCE_DIR}/cxx_only> + $<$<COMPILE_LANGUAGE:C>:${CMAKE_CURRENT_SOURCE_DIR}/c_only> +) +if(CMAKE_GENERATOR MATCHES "Visual Studio|Xcode") target_compile_definitions(consumer - PRIVATE -DTEST_LANG_DEFINES + PRIVATE TEST_LANG_DEFINES_FOR_VISUAL_STUDIO_OR_XCODE ) endif() diff --git a/Tests/CMakeCommands/target_include_directories/consumer.c b/Tests/CMakeCommands/target_include_directories/consumer.c index ae88f92..419c2d2 100644 --- a/Tests/CMakeCommands/target_include_directories/consumer.c +++ b/Tests/CMakeCommands/target_include_directories/consumer.c @@ -1,5 +1,13 @@ -#ifdef TEST_LANG_DEFINES +// Visual Studio allows only one set of flags for C and C++. +// In a target using C++ we pick the C++ flags even for C sources. +#ifdef TEST_LANG_DEFINES_FOR_VISUAL_STUDIO_OR_XCODE +#include "cxx_only.h" + +#ifndef CXX_ONLY_DEFINE +#error Expected CXX_ONLY_DEFINE +#endif +#else #include "c_only.h" #ifndef C_ONLY_DEFINE diff --git a/Tests/CMakeCommands/target_include_directories/consumer.cpp b/Tests/CMakeCommands/target_include_directories/consumer.cpp index 0f8153b..1e018ad 100644 --- a/Tests/CMakeCommands/target_include_directories/consumer.cpp +++ b/Tests/CMakeCommands/target_include_directories/consumer.cpp @@ -1,12 +1,10 @@ #include "consumer.h" #include "common.h" +#include "cxx_only.h" #include "interfaceinclude.h" #include "publicinclude.h" #include "relative_dir.h" -#ifdef TEST_LANG_DEFINES -#include "cxx_only.h" -#endif #ifdef PRIVATEINCLUDE_DEFINE #error Unexpected PRIVATEINCLUDE_DEFINE @@ -32,11 +30,9 @@ #error Expected CONSUMER_DEFINE #endif -#ifdef TEST_LANG_DEFINES #ifndef CXX_ONLY_DEFINE #error Expected CXX_ONLY_DEFINE #endif -#endif int main() { diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt index 9f09185..06df53f 100644 --- a/Tests/CMakeLib/CMakeLists.txt +++ b/Tests/CMakeLib/CMakeLists.txt @@ -5,21 +5,21 @@ include_directories( ) set(CMakeLib_TESTS - testGeneratedFileStream - testRST - testSystemTools - testUTF8 - testXMLParser - testXMLSafe - testFindPackageCommand - testUVRAII + testGeneratedFileStream.cxx + testRST.cxx + testSystemTools.cxx + testUTF8.cxx + testXMLParser.cxx + testXMLSafe.cxx + testFindPackageCommand.cxx + testUVRAII.cxx ) set(testRST_ARGS ${CMAKE_CURRENT_SOURCE_DIR}) if(WIN32) list(APPEND CMakeLib_TESTS - testVisualStudioSlnParser + testVisualStudioSlnParser.cxx ) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testVisualStudioSlnParser.h.in ${CMAKE_CURRENT_BINARY_DIR}/testVisualStudioSlnParser.h @ONLY) @@ -38,7 +38,8 @@ set_property(TARGET CMakeLibTests PROPERTY CXX_CLANG_TIDY "") add_executable(testEncoding testEncoding.cxx) target_link_libraries(testEncoding cmsys) -foreach(test ${CMakeLib_TESTS}) +foreach(testfile ${CMakeLib_TESTS}) + get_filename_component(test "${testfile}" NAME_WE) add_test(CMakeLib.${test} CMakeLibTests ${test} ${${test}_ARGS}) endforeach() diff --git a/Tests/CMakeServerLib/CMakeLists.txt b/Tests/CMakeServerLib/CMakeLists.txt index 5e1ad0c..2c23c2d 100644 --- a/Tests/CMakeServerLib/CMakeLists.txt +++ b/Tests/CMakeServerLib/CMakeLists.txt @@ -5,7 +5,7 @@ include_directories( ) set(CMakeServerLib_TESTS - testServerBuffering + testServerBuffering.cpp ) create_test_sourcelist(CMakeLib_TEST_SRCS CMakeServerLibTests.cxx ${CMakeServerLib_TESTS}) @@ -15,6 +15,7 @@ target_link_libraries(CMakeServerLibTests CMakeLib CMakeServerLib) SET_PROPERTY(TARGET CMakeServerLibTests PROPERTY C_CLANG_TIDY "") SET_PROPERTY(TARGET CMakeServerLibTests PROPERTY CXX_CLANG_TIDY "") -foreach(test ${CMakeServerLib_TESTS}) +foreach(testfile ${CMakeServerLib_TESTS}) + get_filename_component(test "${testfile}" NAME_WE) add_test(CMakeServerLib.${test} CMakeServerLibTests ${test} ${${test}_ARGS}) endforeach() diff --git a/Tests/CudaOnly/CMakeLists.txt b/Tests/CudaOnly/CMakeLists.txt index 5f456fc..5ad6e6b 100644 --- a/Tests/CudaOnly/CMakeLists.txt +++ b/Tests/CudaOnly/CMakeLists.txt @@ -1,6 +1,7 @@ ADD_TEST_MACRO(CudaOnly.EnableStandard CudaOnlyEnableStandard) ADD_TEST_MACRO(CudaOnly.ExportPTX CudaOnlyExportPTX) +ADD_TEST_MACRO(CudaOnly.GPUDebugFlag CudaOnlyGPUDebugFlag) +ADD_TEST_MACRO(CudaOnly.ResolveDeviceSymbols CudaOnlyResolveDeviceSymbols) ADD_TEST_MACRO(CudaOnly.SeparateCompilation CudaOnlySeparateCompilation) ADD_TEST_MACRO(CudaOnly.WithDefs CudaOnlyWithDefs) -ADD_TEST_MACRO(CudaOnly.ResolveDeviceSymbols CudaOnlyResolveDeviceSymbols) diff --git a/Tests/CudaOnly/GPUDebugFlag/CMakeLists.txt b/Tests/CudaOnly/GPUDebugFlag/CMakeLists.txt new file mode 100644 index 0000000..5b96906 --- /dev/null +++ b/Tests/CudaOnly/GPUDebugFlag/CMakeLists.txt @@ -0,0 +1,23 @@ + +cmake_minimum_required(VERSION 3.7) +project (CudaOnlGPUDebugFlag CUDA) + +#Goal for this example: +#verify that -G enables gpu debug flags +string(APPEND CMAKE_CUDA_FLAGS " -gencode=arch=compute_30,code=compute_30") +string(APPEND CMAKE_CUDA_FLAGS " -G") +set(CMAKE_CUDA_STANDARD 11) + +add_executable(CudaOnlyGPUDebugFlag main.cu) + +if(CMAKE_CUDA_COMPILER_VERSION VERSION_LESS 9.0.0) + #CUDA's __CUDACC_DEBUG__ define was added in 9.0 + #so if we are below 9.0.0 we will manually add the define so that the test + #passes + target_compile_definitions(CudaOnlyGPUDebugFlag PRIVATE "__CUDACC_DEBUG__") +endif() + +if(APPLE) + # Help the static cuda runtime find the driver (libcuda.dyllib) at runtime. + set_property(TARGET CudaOnlyGPUDebugFlag PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES}) +endif() diff --git a/Tests/CudaOnly/GPUDebugFlag/main.cu b/Tests/CudaOnly/GPUDebugFlag/main.cu new file mode 100644 index 0000000..8b97a3f --- /dev/null +++ b/Tests/CudaOnly/GPUDebugFlag/main.cu @@ -0,0 +1,66 @@ +#include <cuda.h> +#include <cuda_runtime.h> +#include <iostream> + +static __global__ void debug_kernel(bool* has_debug) +{ +// Verify using the return code if we have GPU debug flag enabled +#if defined(__CUDACC__) && defined(__CUDACC_DEBUG__) + *has_debug = true; +#else + *has_debug = false; +#endif +} + +int choose_cuda_device() +{ + int nDevices = 0; + cudaError_t err = cudaGetDeviceCount(&nDevices); + if (err != cudaSuccess) { + std::cerr << "Failed to retrieve the number of CUDA enabled devices" + << std::endl; + return 1; + } + for (int i = 0; i < nDevices; ++i) { + cudaDeviceProp prop; + cudaError_t err = cudaGetDeviceProperties(&prop, i); + if (err != cudaSuccess) { + std::cerr << "Could not retrieve properties from CUDA device " << i + << std::endl; + return 1; + } + if (prop.major >= 3) { + err = cudaSetDevice(i); + if (err != cudaSuccess) { + std::cout << "Could not select CUDA device " << i << std::endl; + } else { + return 0; + } + } + } + + std::cout << "Could not find a CUDA enabled card supporting compute >=3.0" + << std::endl; + + return 1; +} + +int main(int argc, char** argv) +{ + bool* has_debug; + cudaError_t err = cudaMallocManaged(&has_debug, sizeof(bool)); + + debug_kernel<<<1, 1>>>(has_debug); + err = cudaDeviceSynchronize(); + if (err != cudaSuccess) { + std::cerr << "debug_kernel: kernel launch shouldn't have failed\n" + << "reason:\t" << cudaGetErrorString(err) << std::endl; + return 1; + } + if (*has_debug == false) { + std::cerr << "debug_kernel: kernel not compiled with device debug" + << std::endl; + return 1; + } + return 0; +} diff --git a/Tests/CudaOnly/ResolveDeviceSymbols/CMakeLists.txt b/Tests/CudaOnly/ResolveDeviceSymbols/CMakeLists.txt index 83473ae..0c453a9 100644 --- a/Tests/CudaOnly/ResolveDeviceSymbols/CMakeLists.txt +++ b/Tests/CudaOnly/ResolveDeviceSymbols/CMakeLists.txt @@ -21,7 +21,7 @@ endif() # Resolve the device symbols into that static library # Verify that we can't use those device symbols from anything that links # to the static library -string(APPEND CMAKE_CUDA_FLAGS " -gencode arch=compute_30,code=compute_30") +string(APPEND CMAKE_CUDA_FLAGS " -gencode arch=compute_30,code=[compute_30] -gencode arch=compute_50,code=\\\"compute_50\\\"") set(CMAKE_CXX_STANDARD 11) set(CMAKE_CUDA_STANDARD 11) diff --git a/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt b/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt index cfca823..c934c51 100644 --- a/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt +++ b/Tests/CudaOnly/SeparateCompilation/CMakeLists.txt @@ -9,7 +9,8 @@ project (CudaOnlySeparateCompilation CUDA) #and executables. #We complicate the matter by also testing that multiple static libraries #all containing cuda separable compilation code links properly -string(APPEND CMAKE_CUDA_FLAGS " -gencode arch=compute_30,code=compute_30") +string(APPEND CMAKE_CUDA_FLAGS " -gencode arch=compute_30,code=\\\"compute_30,sm_30,sm_35\\\"") +string(APPEND CMAKE_CUDA_FLAGS " --generate-code=arch=compute_50,code=[compute_50,sm_50,sm_52]") set(CMAKE_CXX_STANDARD 11) set(CMAKE_CUDA_STANDARD 11) diff --git a/Tests/CudaOnly/WithDefs/CMakeLists.txt b/Tests/CudaOnly/WithDefs/CMakeLists.txt index 5bd93a4..926d9ed 100644 --- a/Tests/CudaOnly/WithDefs/CMakeLists.txt +++ b/Tests/CudaOnly/WithDefs/CMakeLists.txt @@ -21,19 +21,13 @@ set(release_compile_defs DEFREL) #this verifies we can pass things such as '_','(' to nvcc add_definitions("-DPACKED_DEFINE=__attribute__((packed))") -if(CMAKE_GENERATOR MATCHES "Visual Studio") - # CUDA MSBuild rules do not pass '-x cu' to nvcc - set(main main_for_vs.cu) -else() - set(main main.notcu) - set_source_files_properties(main.notcu PROPERTIES LANGUAGE CUDA) -endif() -add_executable(CudaOnlyWithDefs ${main}) +add_executable(CudaOnlyWithDefs main.notcu) +set_source_files_properties(main.notcu PROPERTIES LANGUAGE CUDA) target_compile_options(CudaOnlyWithDefs PRIVATE - -DCOMPILE_LANG_$<COMPILE_LANGUAGE> - -DLANG_IS_CUDA=$<COMPILE_LANGUAGE:CUDA> + -DFLAG_COMPILE_LANG_$<COMPILE_LANGUAGE> + -DFLAG_LANG_IS_CUDA=$<COMPILE_LANGUAGE:CUDA> -Xcompiler=-DHOST_DEFINE $<$<CONFIG:DEBUG>:$<BUILD_INTERFACE:${debug_compile_flags}>> ) @@ -41,8 +35,15 @@ target_compile_options(CudaOnlyWithDefs target_compile_definitions(CudaOnlyWithDefs PRIVATE $<$<CONFIG:RELEASE>:$<BUILD_INTERFACE:${release_compile_defs}>> + -DDEF_COMPILE_LANG_$<COMPILE_LANGUAGE> + -DDEF_LANG_IS_CUDA=$<COMPILE_LANGUAGE:CUDA> ) +target_include_directories(CudaOnlyWithDefs + PRIVATE + $<$<COMPILE_LANGUAGE:CUDA>:${CMAKE_CURRENT_SOURCE_DIR}/inc_cuda> +) + if(APPLE) # Help the static cuda runtime find the driver (libcuda.dyllib) at runtime. set_property(TARGET CudaOnlyWithDefs PROPERTY BUILD_RPATH ${CMAKE_CUDA_IMPLICIT_LINK_DIRECTORIES}) diff --git a/Tests/CudaOnly/WithDefs/inc_cuda/inc_cuda.h b/Tests/CudaOnly/WithDefs/inc_cuda/inc_cuda.h new file mode 100644 index 0000000..e228b58 --- /dev/null +++ b/Tests/CudaOnly/WithDefs/inc_cuda/inc_cuda.h @@ -0,0 +1 @@ +#define INC_CUDA diff --git a/Tests/CudaOnly/WithDefs/main.notcu b/Tests/CudaOnly/WithDefs/main.notcu index bfb3577..3793d74 100644 --- a/Tests/CudaOnly/WithDefs/main.notcu +++ b/Tests/CudaOnly/WithDefs/main.notcu @@ -2,6 +2,11 @@ #include <cuda_runtime.h> #include <iostream> +#include <inc_cuda.h> +#ifndef INC_CUDA +#error "INC_CUDA not defined!" +#endif + #ifndef HOST_DEFINE #error "HOST_DEFINE not defined!" #endif @@ -10,16 +15,28 @@ #error "PACKED_DEFINE not defined!" #endif -#ifndef COMPILE_LANG_CUDA -#error "COMPILE_LANG_CUDA not defined!" +#ifndef FLAG_COMPILE_LANG_CUDA +#error "FLAG_COMPILE_LANG_CUDA not defined!" +#endif + +#ifndef FLAG_LANG_IS_CUDA +#error "FLAG_LANG_IS_CUDA not defined!" +#endif + +#if !FLAG_LANG_IS_CUDA +#error "Expected FLAG_LANG_IS_CUDA" +#endif + +#ifndef DEF_COMPILE_LANG_CUDA +#error "DEF_COMPILE_LANG_CUDA not defined!" #endif -#ifndef LANG_IS_CUDA -#error "LANG_IS_CUDA not defined!" +#ifndef DEF_LANG_IS_CUDA +#error "DEF_LANG_IS_CUDA not defined!" #endif -#if !LANG_IS_CUDA -#error "Expected LANG_IS_CUDA" +#if !DEF_LANG_IS_CUDA +#error "Expected DEF_LANG_IS_CUDA" #endif static __global__ void DetermineIfValidCudaDevice() diff --git a/Tests/CudaOnly/WithDefs/main_for_vs.cu b/Tests/CudaOnly/WithDefs/main_for_vs.cu deleted file mode 100644 index 56078e7..0000000 --- a/Tests/CudaOnly/WithDefs/main_for_vs.cu +++ /dev/null @@ -1 +0,0 @@ -#include "main.notcu" diff --git a/Tests/FindOpenSSL/rand/main.cc b/Tests/FindOpenSSL/rand/main.cc index d81b318..147044b 100644 --- a/Tests/FindOpenSSL/rand/main.cc +++ b/Tests/FindOpenSSL/rand/main.cc @@ -9,7 +9,7 @@ int main() unsigned char buf[1024]; // random bytes - int rezval = RAND_bytes(buf, sizeof(buf)); /* 1 succes, 0 otherwise */ + int rezval = RAND_bytes(buf, sizeof(buf)); /* 1 success, 0 otherwise */ // check result if (rezval == 1) { diff --git a/Tests/GeneratorExpression/CMakeLists.txt b/Tests/GeneratorExpression/CMakeLists.txt index 0585d0c..4586357 100644 --- a/Tests/GeneratorExpression/CMakeLists.txt +++ b/Tests/GeneratorExpression/CMakeLists.txt @@ -269,10 +269,8 @@ set_property(SOURCE srcgenex_flags_COMPILE_LANGUAGE.c PROPERTY COMPILE_FLAGS "$< add_executable(srcgenex_defs srcgenex_defs.c) set_property(SOURCE srcgenex_defs.c PROPERTY COMPILE_DEFINITIONS NAME=$<TARGET_PROPERTY:NAME>) -if (CMAKE_GENERATOR MATCHES "Makefiles|Ninja|Watcom WMake") - add_executable(srcgenex_defs_COMPILE_LANGUAGE srcgenex_defs_COMPILE_LANGUAGE.c) - set_property(SOURCE srcgenex_defs_COMPILE_LANGUAGE.c PROPERTY COMPILE_DEFINITIONS $<$<COMPILE_LANGUAGE:C>:NAME=$<TARGET_PROPERTY:NAME>>) -endif() +add_executable(srcgenex_defs_COMPILE_LANGUAGE srcgenex_defs_COMPILE_LANGUAGE.c) +set_property(SOURCE srcgenex_defs_COMPILE_LANGUAGE.c PROPERTY COMPILE_DEFINITIONS $<$<COMPILE_LANGUAGE:C>:NAME=$<TARGET_PROPERTY:NAME>>) #----------------------------------------------------------------------------- # Cover test properties with generator expressions. diff --git a/Tests/QtAutogen/CommonTests.cmake b/Tests/QtAutogen/CommonTests.cmake index c56780e..2c2e6d6 100644 --- a/Tests/QtAutogen/CommonTests.cmake +++ b/Tests/QtAutogen/CommonTests.cmake @@ -29,6 +29,12 @@ ADD_AUTOGEN_TEST(ObjectLibrary someProgram) if(APPLE AND (NOT QT_TEST_VERSION STREQUAL 4)) ADD_AUTOGEN_TEST(MacOsFW) endif() +ADD_AUTOGEN_TEST(Parallel parallel) +ADD_AUTOGEN_TEST(Parallel1 parallel1) +ADD_AUTOGEN_TEST(Parallel2 parallel2) +ADD_AUTOGEN_TEST(Parallel3 parallel3) +ADD_AUTOGEN_TEST(Parallel4 parallel4) +ADD_AUTOGEN_TEST(ParallelAUTO parallelAUTO) ADD_AUTOGEN_TEST(SameName sameName) ADD_AUTOGEN_TEST(StaticLibraryCycle slc) ADD_AUTOGEN_TEST(Complex QtAutogen) diff --git a/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp b/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp index dea6cb5..d8bf284 100644 --- a/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp +++ b/Tests/QtAutogen/MocInclude/EObjAExtra_p.hpp @@ -1,6 +1,8 @@ #ifndef EOBJAEXTRA_P_HPP #define EOBJAEXTRA_P_HPP +#include <QObject> + class EObjAExtraPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/EObjA_p.hpp b/Tests/QtAutogen/MocInclude/EObjA_p.hpp index 1e0d7e1..9ef5624 100644 --- a/Tests/QtAutogen/MocInclude/EObjA_p.hpp +++ b/Tests/QtAutogen/MocInclude/EObjA_p.hpp @@ -1,6 +1,8 @@ #ifndef EOBJA_P_HPP #define EOBJA_P_HPP +#include <QObject> + class EObjAPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/EObjB_p.hpp b/Tests/QtAutogen/MocInclude/EObjB_p.hpp index 2905f28..84b1ea2 100644 --- a/Tests/QtAutogen/MocInclude/EObjB_p.hpp +++ b/Tests/QtAutogen/MocInclude/EObjB_p.hpp @@ -1,6 +1,8 @@ #ifndef EOBJB_P_HPP #define EOBJB_P_HPP +#include <QObject> + class EObjBPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/LObjA_p.h b/Tests/QtAutogen/MocInclude/LObjA_p.h index ebe8395..97113d6 100644 --- a/Tests/QtAutogen/MocInclude/LObjA_p.h +++ b/Tests/QtAutogen/MocInclude/LObjA_p.h @@ -1,6 +1,8 @@ #ifndef LOBJA_P_HPP #define LOBJA_P_HPP +#include <QObject> + class LObjAPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/LObjB_p.h b/Tests/QtAutogen/MocInclude/LObjB_p.h index b871f2d..b88f40e 100644 --- a/Tests/QtAutogen/MocInclude/LObjB_p.h +++ b/Tests/QtAutogen/MocInclude/LObjB_p.h @@ -1,6 +1,8 @@ #ifndef LOBJB_P_HPP #define LOBJB_P_HPP +#include <QObject> + class LObjBPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/ObjA_p.h b/Tests/QtAutogen/MocInclude/ObjA_p.h index eb60c98..d944bc6 100644 --- a/Tests/QtAutogen/MocInclude/ObjA_p.h +++ b/Tests/QtAutogen/MocInclude/ObjA_p.h @@ -1,6 +1,8 @@ #ifndef OBJA_P_HPP #define OBJA_P_HPP +#include <QObject> + class ObjAPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/ObjB_p.h b/Tests/QtAutogen/MocInclude/ObjB_p.h index 418da65..61ba604 100644 --- a/Tests/QtAutogen/MocInclude/ObjB_p.h +++ b/Tests/QtAutogen/MocInclude/ObjB_p.h @@ -1,6 +1,8 @@ #ifndef OBJB_P_HPP #define OBJB_P_HPP +#include <QObject> + class ObjBPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp b/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp index db8a096..3231fac 100644 --- a/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp +++ b/Tests/QtAutogen/MocInclude/subExtra/EObjBExtra_p.hpp @@ -1,6 +1,8 @@ #ifndef EOBJBEXTRA_P_HPP #define EOBJBEXTRA_P_HPP +#include <QObject> + class EObjBExtraPrivate : public QObject { Q_OBJECT diff --git a/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp b/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp index 7b37dfd..4a43755 100644 --- a/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp +++ b/Tests/QtAutogen/MocInclude/subGlobal/GObj_p.hpp @@ -1,6 +1,8 @@ #ifndef GOBJ_P_HPP #define GOBJ_P_HPP +#include <QObject> + namespace subGlobal { class GObjPrivate : public QObject diff --git a/Tests/QtAutogen/Parallel/CMakeLists.txt b/Tests/QtAutogen/Parallel/CMakeLists.txt new file mode 100644 index 0000000..9c64804 --- /dev/null +++ b/Tests/QtAutogen/Parallel/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(Parallel) +include("../AutogenTest.cmake") + +# Test different values for AUTOGEN_PARALLEL +include("../Parallel/parallel.cmake") + +add_executable(parallel ${PARALLEL_SRC}) +set_target_properties(parallel PROPERTIES AUTOGEN_PARALLEL "") +target_link_libraries(parallel ${QT_LIBRARIES}) diff --git a/Tests/QtAutogen/Parallel/aaa/bbb/data.qrc b/Tests/QtAutogen/Parallel/aaa/bbb/data.qrc new file mode 100644 index 0000000..0ea3537 --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/bbb/data.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="aaa/bbb"> + <file>item.hpp</file> + <file>item.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/Parallel/aaa/bbb/item.cpp b/Tests/QtAutogen/Parallel/aaa/bbb/item.cpp new file mode 100644 index 0000000..850206f --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/bbb/item.cpp @@ -0,0 +1,22 @@ +#include "item.hpp" + +namespace aaa { +namespace bbb { + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + +void Item::go() +{ + MocLocal obj; +} +} +} + +#include "aaa/bbb/item.moc" diff --git a/Tests/QtAutogen/Parallel/aaa/bbb/item.hpp b/Tests/QtAutogen/Parallel/aaa/bbb/item.hpp new file mode 100644 index 0000000..0855043 --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/bbb/item.hpp @@ -0,0 +1,18 @@ +#ifndef AAA_BBB_ITEM_HPP +#define AAA_BBB_ITEM_HPP + +#include <QObject> + +namespace aaa { +namespace bbb { + +class Item : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} +} + +#endif diff --git a/Tests/QtAutogen/Parallel/aaa/data.qrc b/Tests/QtAutogen/Parallel/aaa/data.qrc new file mode 100644 index 0000000..379af60 --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/data.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="aaa/"> + <file>item.hpp</file> + <file>item.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/Parallel/aaa/item.cpp b/Tests/QtAutogen/Parallel/aaa/item.cpp new file mode 100644 index 0000000..e35d3d1 --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/item.cpp @@ -0,0 +1,22 @@ +#include "item.hpp" +// Include ui_view.h only in header + +namespace aaa { + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + +void Item::go() +{ + Ui_ViewAAA ui; + MocLocal obj; +} +} + +#include "aaa/item.moc" diff --git a/Tests/QtAutogen/Parallel/aaa/item.hpp b/Tests/QtAutogen/Parallel/aaa/item.hpp new file mode 100644 index 0000000..875f72f --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/item.hpp @@ -0,0 +1,18 @@ +#ifndef AAA_ITEM_HPP +#define AAA_ITEM_HPP + +#include <QObject> +// Include ui_view.h only in header +#include <aaa/ui_view.h> + +namespace aaa { + +class Item : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} + +#endif diff --git a/Tests/QtAutogen/Parallel/aaa/view.ui b/Tests/QtAutogen/Parallel/aaa/view.ui new file mode 100644 index 0000000..0f09980 --- /dev/null +++ b/Tests/QtAutogen/Parallel/aaa/view.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ViewAAA</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/Parallel/bbb/aaa/data.qrc b/Tests/QtAutogen/Parallel/bbb/aaa/data.qrc new file mode 100644 index 0000000..da98009 --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/aaa/data.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="bbb/aaa/"> + <file>item.hpp</file> + <file>item.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/Parallel/bbb/aaa/item.cpp b/Tests/QtAutogen/Parallel/bbb/aaa/item.cpp new file mode 100644 index 0000000..7ad01c3 --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/aaa/item.cpp @@ -0,0 +1,22 @@ +#include "item.hpp" + +namespace bbb { +namespace aaa { + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + +void Item::go() +{ + MocLocal obj; +} +} +} + +#include "bbb/aaa/item.moc" diff --git a/Tests/QtAutogen/Parallel/bbb/aaa/item.hpp b/Tests/QtAutogen/Parallel/bbb/aaa/item.hpp new file mode 100644 index 0000000..be07ca8 --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/aaa/item.hpp @@ -0,0 +1,18 @@ +#ifndef BBB_AAA_ITEM_HPP +#define BBB_AAA_ITEM_HPP + +#include <QObject> + +namespace bbb { +namespace aaa { + +class Item : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} +} + +#endif diff --git a/Tests/QtAutogen/Parallel/bbb/data.qrc b/Tests/QtAutogen/Parallel/bbb/data.qrc new file mode 100644 index 0000000..5b080f5 --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/data.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="bbb/"> + <file>item.hpp</file> + <file>item.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/Parallel/bbb/item.cpp b/Tests/QtAutogen/Parallel/bbb/item.cpp new file mode 100644 index 0000000..9ef128e --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/item.cpp @@ -0,0 +1,23 @@ +#include "item.hpp" +// Include ui_view.h only in source +#include <bbb/ui_view.h> + +namespace bbb { + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + +void Item::go() +{ + Ui_ViewBBB ui; + MocLocal obj; +} +} + +#include "bbb/item.moc" diff --git a/Tests/QtAutogen/Parallel/bbb/item.hpp b/Tests/QtAutogen/Parallel/bbb/item.hpp new file mode 100644 index 0000000..d39a9d7 --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/item.hpp @@ -0,0 +1,17 @@ +#ifndef BBB_ITEM_HPP +#define BBB_ITEM_HPP + +#include <QObject> +// Include ui_view.h only in source + +namespace bbb { + +class Item : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} + +#endif diff --git a/Tests/QtAutogen/Parallel/bbb/view.ui b/Tests/QtAutogen/Parallel/bbb/view.ui new file mode 100644 index 0000000..a8f506e --- /dev/null +++ b/Tests/QtAutogen/Parallel/bbb/view.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ViewBBB</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/Parallel/ccc/data.qrc b/Tests/QtAutogen/Parallel/ccc/data.qrc new file mode 100644 index 0000000..f934c39 --- /dev/null +++ b/Tests/QtAutogen/Parallel/ccc/data.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource prefix="ccc/"> + <file>item.hpp</file> + <file>item.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/Parallel/ccc/item.cpp b/Tests/QtAutogen/Parallel/ccc/item.cpp new file mode 100644 index 0000000..ab8a281 --- /dev/null +++ b/Tests/QtAutogen/Parallel/ccc/item.cpp @@ -0,0 +1,25 @@ +#include "item.hpp" +// Include ui_view.h in source and header +#include <ccc/ui_view.h> + +namespace ccc { + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + +void Item::go() +{ + Ui_ViewCCC ui; + MocLocal obj; +} +} + +// Include own moc files +#include "ccc/item.moc" +#include "moc_item.cpp" diff --git a/Tests/QtAutogen/Parallel/ccc/item.hpp b/Tests/QtAutogen/Parallel/ccc/item.hpp new file mode 100644 index 0000000..20d9dd9 --- /dev/null +++ b/Tests/QtAutogen/Parallel/ccc/item.hpp @@ -0,0 +1,18 @@ +#ifndef CCC_ITEM_HPP +#define CCC_ITEM_HPP + +#include <QObject> +// Include ui_view.h in source and header +#include <ccc/ui_view.h> + +namespace ccc { + +class Item : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; +} + +#endif diff --git a/Tests/QtAutogen/Parallel/ccc/view.ui b/Tests/QtAutogen/Parallel/ccc/view.ui new file mode 100644 index 0000000..7989c69 --- /dev/null +++ b/Tests/QtAutogen/Parallel/ccc/view.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ViewCCC</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/Parallel/data.qrc b/Tests/QtAutogen/Parallel/data.qrc new file mode 100644 index 0000000..4ce0b4e --- /dev/null +++ b/Tests/QtAutogen/Parallel/data.qrc @@ -0,0 +1,5 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>main.cpp</file> +</qresource> +</RCC> diff --git a/Tests/QtAutogen/Parallel/item.cpp b/Tests/QtAutogen/Parallel/item.cpp new file mode 100644 index 0000000..3d1fbe7 --- /dev/null +++ b/Tests/QtAutogen/Parallel/item.cpp @@ -0,0 +1,20 @@ +#include "item.hpp" +// Include ui_view.h in source and header +#include <ui_view.h> + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + +void Item::go() +{ + Ui_View ui; + MocLocal obj; +} + +#include "item.moc" diff --git a/Tests/QtAutogen/Parallel/item.hpp b/Tests/QtAutogen/Parallel/item.hpp new file mode 100644 index 0000000..75e83f4 --- /dev/null +++ b/Tests/QtAutogen/Parallel/item.hpp @@ -0,0 +1,15 @@ +#ifndef ITEM_HPP +#define ITEM_HPP + +#include <QObject> +// Include ui_view.h in source and header +#include <ui_view.h> + +class Item : public QObject +{ + Q_OBJECT + Q_SLOT + void go(); +}; + +#endif diff --git a/Tests/QtAutogen/Parallel/main.cpp b/Tests/QtAutogen/Parallel/main.cpp new file mode 100644 index 0000000..a4ffcb3 --- /dev/null +++ b/Tests/QtAutogen/Parallel/main.cpp @@ -0,0 +1,16 @@ +#include "aaa/bbb/item.hpp" +#include "aaa/item.hpp" +#include "bbb/aaa/item.hpp" +#include "bbb/item.hpp" +#include "ccc/item.hpp" + +int main(int argv, char** args) +{ + // Object instances + ::aaa::Item aaa_item; + ::aaa::bbb::Item aaa_bbb_item; + ::bbb::Item bbb_item; + ::bbb::aaa::Item bbb_aaa_item; + ::ccc::Item ccc_item; + return 0; +} diff --git a/Tests/QtAutogen/Parallel/parallel.cmake b/Tests/QtAutogen/Parallel/parallel.cmake new file mode 100644 index 0000000..551bcd8 --- /dev/null +++ b/Tests/QtAutogen/Parallel/parallel.cmake @@ -0,0 +1,24 @@ +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + + +set(PBASE ${CMAKE_CURRENT_LIST_DIR}) +set(PARALLEL_SRC + ${PBASE}/aaa/bbb/item.cpp + ${PBASE}/aaa/bbb/data.qrc + ${PBASE}/aaa/item.cpp + ${PBASE}/aaa/data.qrc + + ${PBASE}/bbb/aaa/item.cpp + ${PBASE}/bbb/aaa/data.qrc + ${PBASE}/bbb/item.cpp + ${PBASE}/bbb/data.qrc + + ${PBASE}/ccc/item.cpp + ${PBASE}/ccc/data.qrc + + ${PBASE}/item.cpp + ${PBASE}/data.qrc + ${PBASE}/main.cpp +) diff --git a/Tests/QtAutogen/Parallel/view.ui b/Tests/QtAutogen/Parallel/view.ui new file mode 100644 index 0000000..2ffe734 --- /dev/null +++ b/Tests/QtAutogen/Parallel/view.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>View</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/Parallel1/CMakeLists.txt b/Tests/QtAutogen/Parallel1/CMakeLists.txt new file mode 100644 index 0000000..9c0b4e5 --- /dev/null +++ b/Tests/QtAutogen/Parallel1/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(Parallel1) +include("../AutogenTest.cmake") + +# Test different values for AUTOGEN_PARALLEL +include("../Parallel/parallel.cmake") + +add_executable(parallel1 ${PARALLEL_SRC}) +set_target_properties(parallel1 PROPERTIES AUTOGEN_PARALLEL 1) +target_link_libraries(parallel1 ${QT_LIBRARIES}) diff --git a/Tests/QtAutogen/Parallel2/CMakeLists.txt b/Tests/QtAutogen/Parallel2/CMakeLists.txt new file mode 100644 index 0000000..74c38f1 --- /dev/null +++ b/Tests/QtAutogen/Parallel2/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(Parallel2) +include("../AutogenTest.cmake") + +# Test different values for AUTOGEN_PARALLEL +include("../Parallel/parallel.cmake") + +add_executable(parallel2 ${PARALLEL_SRC}) +set_target_properties(parallel2 PROPERTIES AUTOGEN_PARALLEL 2) +target_link_libraries(parallel2 ${QT_LIBRARIES}) diff --git a/Tests/QtAutogen/Parallel3/CMakeLists.txt b/Tests/QtAutogen/Parallel3/CMakeLists.txt new file mode 100644 index 0000000..c735531 --- /dev/null +++ b/Tests/QtAutogen/Parallel3/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(Parallel3) +include("../AutogenTest.cmake") + +# Test different values for AUTOGEN_PARALLEL +include("../Parallel/parallel.cmake") + +add_executable(parallel3 ${PARALLEL_SRC}) +set_target_properties(parallel3 PROPERTIES AUTOGEN_PARALLEL 3) +target_link_libraries(parallel3 ${QT_LIBRARIES}) diff --git a/Tests/QtAutogen/Parallel4/CMakeLists.txt b/Tests/QtAutogen/Parallel4/CMakeLists.txt new file mode 100644 index 0000000..c012ccd --- /dev/null +++ b/Tests/QtAutogen/Parallel4/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(Parallel4) +include("../AutogenTest.cmake") + +# Test different values for AUTOGEN_PARALLEL +include("../Parallel/parallel.cmake") + +add_executable(parallel4 ${PARALLEL_SRC}) +set_target_properties(parallel4 PROPERTIES AUTOGEN_PARALLEL 4) +target_link_libraries(parallel4 ${QT_LIBRARIES}) diff --git a/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt b/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt new file mode 100644 index 0000000..3fd3ebc --- /dev/null +++ b/Tests/QtAutogen/ParallelAUTO/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 3.10) +project(ParallelAUTO) +include("../AutogenTest.cmake") + +# Test different values for AUTOGEN_PARALLEL +include("../Parallel/parallel.cmake") + +add_executable(parallelAUTO ${PARALLEL_SRC}) +set_target_properties(parallelAUTO PROPERTIES AUTOGEN_PARALLEL "AUTO") +target_link_libraries(parallelAUTO ${QT_LIBRARIES}) diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index f0cf88e..e440b7f 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -352,8 +352,6 @@ add_RunCMake_test(IfacePaths_INCLUDE_DIRECTORIES TEST_DIR IfacePaths) set(IfacePaths_SOURCES_ARGS -DTEST_PROP=SOURCES) add_RunCMake_test(IfacePaths_SOURCES TEST_DIR IfacePaths) -add_RunCMake_test(COMPILE_LANGUAGE-genex) - # Matlab module related tests if(CMake_TEST_FindMatlab) add_RunCMake_test(FindMatlab) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CMakeLists.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/CMakeLists.txt deleted file mode 100644 index ef2163c..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -cmake_minimum_required(VERSION 3.1) -project(${RunCMake_TEST} NONE) -include(${RunCMake_TEST}.cmake) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-result.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-result.txt deleted file mode 100644 index d00491f..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-stderr-VS.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-stderr-VS.txt deleted file mode 100644 index 42c1485..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-stderr-VS.txt +++ /dev/null @@ -1,9 +0,0 @@ -CMake Error at CompileDefinitions.cmake:5 \(target_compile_definitions\): - Error evaluating generator expression: - - \$<COMPILE_LANGUAGE:CXX> - - \$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS and - file\(GENERATE\) with the Visual Studio generator. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-stderr-Xcode.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-stderr-Xcode.txt deleted file mode 100644 index 7879a79..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions-stderr-Xcode.txt +++ /dev/null @@ -1,9 +0,0 @@ -CMake Error at CompileDefinitions.cmake:5 \(target_compile_definitions\): - Error evaluating generator expression: - - \$<COMPILE_LANGUAGE:CXX> - - \$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS and - file\(GENERATE\) with the Xcode generator. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions.cmake b/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions.cmake deleted file mode 100644 index 7935d88..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/CompileDefinitions.cmake +++ /dev/null @@ -1,5 +0,0 @@ - -enable_language(CXX) - -add_executable(main main.cpp) -target_compile_definitions(main PRIVATE $<$<COMPILE_LANGUAGE:CXX>:-DANYTHING>) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-result.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-result.txt deleted file mode 100644 index d00491f..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-stderr-VS.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-stderr-VS.txt deleted file mode 100644 index 3806ed1..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-stderr-VS.txt +++ /dev/null @@ -1,9 +0,0 @@ -CMake Error at IncludeDirectories.cmake:5 \(target_include_directories\): - Error evaluating generator expression: - - \$<COMPILE_LANGUAGE:CXX> - - \$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS and - file\(GENERATE\) with the Visual Studio generator. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-stderr-Xcode.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-stderr-Xcode.txt deleted file mode 100644 index a3fb9c5..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories-stderr-Xcode.txt +++ /dev/null @@ -1,9 +0,0 @@ -CMake Error at IncludeDirectories.cmake:5 \(target_include_directories\): - Error evaluating generator expression: - - \$<COMPILE_LANGUAGE:CXX> - - \$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS and - file\(GENERATE\) with the Xcode generator. -Call Stack \(most recent call first\): - CMakeLists.txt:3 \(include\) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories.cmake b/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories.cmake deleted file mode 100644 index 31771f6..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/IncludeDirectories.cmake +++ /dev/null @@ -1,5 +0,0 @@ - -enable_language(CXX) - -add_executable(main main.cpp) -target_include_directories(main PRIVATE $<$<COMPILE_LANGUAGE:CXX>:anydir>) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-result.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-result.txt deleted file mode 100644 index d00491f..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-result.txt +++ /dev/null @@ -1 +0,0 @@ -1 diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-stderr-VS.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-stderr-VS.txt deleted file mode 100644 index 5d19153..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-stderr-VS.txt +++ /dev/null @@ -1,7 +0,0 @@ -CMake Error: - Error evaluating generator expression: - - \$<COMPILE_LANGUAGE:CXX> - - \$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS and - file\(GENERATE\) with the Visual Studio generator. diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-stderr-Xcode.txt b/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-stderr-Xcode.txt deleted file mode 100644 index 4a4564e..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions-stderr-Xcode.txt +++ /dev/null @@ -1,7 +0,0 @@ -CMake Error: - Error evaluating generator expression: - - \$<COMPILE_LANGUAGE:CXX> - - \$<COMPILE_LANGUAGE:...> may only be used for COMPILE_OPTIONS and - file\(GENERATE\) with the Xcode generator. diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions.cmake b/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions.cmake deleted file mode 100644 index 3a07d7d..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/PerSourceCompileDefinitions.cmake +++ /dev/null @@ -1,5 +0,0 @@ - -enable_language(CXX) - -add_executable(main main.cpp) -set_property(SOURCE main.cpp PROPERTY COMPILE_DEFINITIONS $<$<COMPILE_LANGUAGE:CXX>:ANYTHING>) diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/RunCMakeTest.cmake b/Tests/RunCMake/COMPILE_LANGUAGE-genex/RunCMakeTest.cmake deleted file mode 100644 index 1a93dcd..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/RunCMakeTest.cmake +++ /dev/null @@ -1,23 +0,0 @@ -include(RunCMake) - -if (RunCMake_GENERATOR STREQUAL "Xcode") - set(RunCMake-stderr-file CompileDefinitions-stderr-Xcode.txt) - run_cmake(CompileDefinitions) -elseif (RunCMake_GENERATOR MATCHES "Visual Studio") - set(RunCMake-stderr-file CompileDefinitions-stderr-VS.txt) - run_cmake(CompileDefinitions) -endif() -if (RunCMake_GENERATOR STREQUAL "Xcode") - set(RunCMake-stderr-file IncludeDirectories-stderr-Xcode.txt) - run_cmake(IncludeDirectories) -elseif (RunCMake_GENERATOR MATCHES "Visual Studio") - set(RunCMake-stderr-file IncludeDirectories-stderr-VS.txt) - run_cmake(IncludeDirectories) -endif() -if (RunCMake_GENERATOR STREQUAL "Xcode") - set(RunCMake-stderr-file PerSourceCompileDefinitions-stderr-Xcode.txt) - run_cmake(PerSourceCompileDefinitions) -elseif (RunCMake_GENERATOR MATCHES "Visual Studio") - set(RunCMake-stderr-file PerSourceCompileDefinitions-stderr-VS.txt) - run_cmake(PerSourceCompileDefinitions) -endif() diff --git a/Tests/RunCMake/COMPILE_LANGUAGE-genex/main.cpp b/Tests/RunCMake/COMPILE_LANGUAGE-genex/main.cpp deleted file mode 100644 index 766b775..0000000 --- a/Tests/RunCMake/COMPILE_LANGUAGE-genex/main.cpp +++ /dev/null @@ -1,5 +0,0 @@ - -int main() -{ - return 0; -} diff --git a/Tests/RunCMake/GenerateExportHeader/GEH.cmake b/Tests/RunCMake/GenerateExportHeader/GEH.cmake index cfca8fe..cf81f36 100644 --- a/Tests/RunCMake/GenerateExportHeader/GEH.cmake +++ b/Tests/RunCMake/GenerateExportHeader/GEH.cmake @@ -53,7 +53,16 @@ endif() add_subdirectory(lib_shared_and_static) -add_compiler_export_flags() +if(CMAKE_SYSTEM_NAME MATCHES "AIX" AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU" + AND CMAKE_CXX_COMPILE_OPTIONS_VISIBILITY) + # With GNU 7 on AIX, passing -fvisibility=hidden when driving the + # linker for a shared library drops the so init/destruct symbols. + # Just use the modern approach instead of testing the macro. + set(CMAKE_CXX_VISIBILITY_PRESET hidden) + set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) +else() + add_compiler_export_flags() +endif() set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/Tests/RunCMake/GenerateExportHeader/libshared/CMakeLists.txt b/Tests/RunCMake/GenerateExportHeader/libshared/CMakeLists.txt index c4a761c..9516a5a 100644 --- a/Tests/RunCMake/GenerateExportHeader/libshared/CMakeLists.txt +++ b/Tests/RunCMake/GenerateExportHeader/libshared/CMakeLists.txt @@ -1,7 +1,5 @@ include(GenerateExportHeader) -add_compiler_export_flags() - set(CMAKE_INCLUDE_CURRENT_DIR ON) add_library(libshared SHARED libshared.cpp) diff --git a/Tests/RunCMake/GenerateExportHeader/libstatic/CMakeLists.txt b/Tests/RunCMake/GenerateExportHeader/libstatic/CMakeLists.txt index 0fd136c..56e8335 100644 --- a/Tests/RunCMake/GenerateExportHeader/libstatic/CMakeLists.txt +++ b/Tests/RunCMake/GenerateExportHeader/libstatic/CMakeLists.txt @@ -2,8 +2,6 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON) include(GenerateExportHeader) -add_compiler_export_flags() - # Show that the export header has no effect on a static library. add_library(libstatic STATIC libstatic.cpp) diff --git a/Tests/RunCMake/interface_library/global-interface-stderr.txt b/Tests/RunCMake/interface_library/global-interface-stderr.txt index 24edd0f..23b45d9 100644 --- a/Tests/RunCMake/interface_library/global-interface-stderr.txt +++ b/Tests/RunCMake/interface_library/global-interface-stderr.txt @@ -3,7 +3,7 @@ CMake Error at global-interface.cmake:2 \(add_library\): GLOBAL - Tried extensions \.c \.C \.c\+\+ \.cc \.cpp \.cxx \.m \.M \.mm \.h \.hh \.h\+\+ \.hm \.hpp - \.hxx \.in \.txx + Tried extensions( \.[A-Za-z+]+| + )* Call Stack \(most recent call first\): CMakeLists.txt:3 \(include\) diff --git a/Utilities/KWIML/Copyright.txt b/Utilities/KWIML/Copyright.txt index 515c4eb..fffd6d1 100644 --- a/Utilities/KWIML/Copyright.txt +++ b/Utilities/KWIML/Copyright.txt @@ -1,5 +1,5 @@ Kitware Information Macro Library -Copyright 2010-2016 Kitware, Inc. +Copyright 2010-2018 Kitware, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Utilities/KWIML/include/kwiml/abi.h b/Utilities/KWIML/include/kwiml/abi.h index 5ffd542..da525fd 100644 --- a/Utilities/KWIML/include/kwiml/abi.h +++ b/Utilities/KWIML/include/kwiml/abi.h @@ -1,6 +1,6 @@ /*============================================================================ Kitware Information Macro Library - Copyright 2010-2016 Kitware, Inc. + Copyright 2010-2018 Kitware, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -468,7 +468,7 @@ suppression macro KWIML_ABI_NO_VERIFY was defined. # define KWIML_ABI_ENDIAN_ID KWIML_ABI_ENDIAN_ID_LITTLE /* RISC-V */ -#elif defined(__riscv__) +#elif defined(__riscv) || defined(__riscv__) # define KWIML_ABI_ENDIAN_ID KWIML_ABI_ENDIAN_ID_LITTLE /* Unknown CPU */ @@ -484,9 +484,16 @@ suppression macro KWIML_ABI_NO_VERIFY was defined. #if defined(_MSC_VER) # pragma warning (push) +# pragma warning (disable:4309) /* static_cast trunction of constant value */ # pragma warning (disable:4310) /* cast truncates constant value */ #endif +#if defined(__cplusplus) && !defined(__BORLANDC__) +#define KWIML_ABI_private_STATIC_CAST(t,v) static_cast<t>(v) +#else +#define KWIML_ABI_private_STATIC_CAST(t,v) (t)(v) +#endif + #define KWIML_ABI_private_VERIFY(n, x, y) KWIML_ABI_private_VERIFY_0(KWIML_ABI_private_VERSION, n, x, y) #define KWIML_ABI_private_VERIFY_0(V, n, x, y) KWIML_ABI_private_VERIFY_1(V, n, x, y) #define KWIML_ABI_private_VERIFY_1(V, n, x, y) extern int (*n##_v##V)[x]; extern int (*n##_v##V)[y] @@ -535,9 +542,11 @@ KWIML_ABI_private_VERIFY_DIFF(KWIML_ABI___INT64_NOT_LONG_LONG, __int64, long lon #endif #if defined(KWIML_ABI_CHAR_IS_UNSIGNED) -KWIML_ABI_private_VERIFY_BOOL(KWIML_ABI_CHAR_IS_UNSIGNED, (char)0x80 > 0); +KWIML_ABI_private_VERIFY_BOOL(KWIML_ABI_CHAR_IS_UNSIGNED, + KWIML_ABI_private_STATIC_CAST(char, 0x80) > 0); #elif defined(KWIML_ABI_CHAR_IS_SIGNED) -KWIML_ABI_private_VERIFY_BOOL(KWIML_ABI_CHAR_IS_SIGNED, (char)0x80 < 0); +KWIML_ABI_private_VERIFY_BOOL(KWIML_ABI_CHAR_IS_SIGNED, + KWIML_ABI_private_STATIC_CAST(char, 0x80) < 0); #endif #undef KWIML_ABI_private_VERIFY_DIFF @@ -557,6 +566,8 @@ KWIML_ABI_private_VERIFY_BOOL(KWIML_ABI_CHAR_IS_SIGNED, (char)0x80 < 0); #undef KWIML_ABI_private_VERIFY_0 #undef KWIML_ABI_private_VERIFY +#undef KWIML_ABI_private_STATIC_CAST + #if defined(_MSC_VER) # pragma warning (pop) #endif diff --git a/Utilities/KWIML/include/kwiml/int.h b/Utilities/KWIML/include/kwiml/int.h index 489c603..b2e14d5 100644 --- a/Utilities/KWIML/include/kwiml/int.h +++ b/Utilities/KWIML/include/kwiml/int.h @@ -1,6 +1,6 @@ /*============================================================================ Kitware Information Macro Library - Copyright 2010-2016 Kitware, Inc. + Copyright 2010-2018 Kitware, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -1003,16 +1003,25 @@ An includer may test the following macros after inclusion: #if defined(_MSC_VER) # pragma warning (push) +# pragma warning (disable:4309) /* static_cast trunction of constant value */ # pragma warning (disable:4310) /* cast truncates constant value */ #endif +#if defined(__cplusplus) && !defined(__BORLANDC__) +#define KWIML_INT_private_STATIC_CAST(t,v) static_cast<t>(v) +#else +#define KWIML_INT_private_STATIC_CAST(t,v) (t)(v) +#endif + #define KWIML_INT_private_VERIFY(n, x, y) KWIML_INT_private_VERIFY_0(KWIML_INT_private_VERSION, n, x, y) #define KWIML_INT_private_VERIFY_0(V, n, x, y) KWIML_INT_private_VERIFY_1(V, n, x, y) #define KWIML_INT_private_VERIFY_1(V, n, x, y) extern int (*n##_v##V)[x]; extern int (*n##_v##V)[y] #define KWIML_INT_private_VERIFY_BOOL(m, b) KWIML_INT_private_VERIFY(KWIML_INT_detail_VERIFY_##m, 2, (b)?2:3) #define KWIML_INT_private_VERIFY_TYPE(t, s) KWIML_INT_private_VERIFY(KWIML_INT_detail_VERIFY_##t, s, sizeof(t)) -#define KWIML_INT_private_VERIFY_SIGN(t, u, o) KWIML_INT_private_VERIFY_BOOL(SIGN_##t, (t)((u)1 << ((sizeof(t)<<3)-1)) o 0) +#define KWIML_INT_private_VERIFY_SIGN(t, u, o) \ + KWIML_INT_private_VERIFY_BOOL(SIGN_##t, KWIML_INT_private_STATIC_CAST( \ + t, KWIML_INT_private_STATIC_CAST(u, 1) << ((sizeof(t)<<3)-1)) o 0) KWIML_INT_private_VERIFY_TYPE(KWIML_INT_int8_t, 1); KWIML_INT_private_VERIFY_TYPE(KWIML_INT_uint8_t, 1); @@ -1060,6 +1069,8 @@ KWIML_INT_private_VERIFY_SIGN(KWIML_INT_uintptr_t, KWIML_INT_uintptr_t, >); #undef KWIML_INT_private_VERIFY_0 #undef KWIML_INT_private_VERIFY +#undef KWIML_INT_private_STATIC_CAST + #if defined(_MSC_VER) # pragma warning (pop) #endif diff --git a/Utilities/KWIML/src/version.h.in b/Utilities/KWIML/src/version.h.in index 0ac8854..5c566bb 100644 --- a/Utilities/KWIML/src/version.h.in +++ b/Utilities/KWIML/src/version.h.in @@ -1,6 +1,6 @@ /*============================================================================ Kitware Information Macro Library - Copyright 2010-2016 Kitware, Inc. + Copyright 2010-2018 Kitware, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/Utilities/KWIML/test/test_int_format.h b/Utilities/KWIML/test/test_int_format.h index 24dcdfb..2e0310c 100644 --- a/Utilities/KWIML/test/test_int_format.h +++ b/Utilities/KWIML/test/test_int_format.h @@ -8,6 +8,7 @@ #if defined(_MSC_VER) # pragma warning (push) +# pragma warning (disable:4309) /* static_cast trunction of constant value */ # pragma warning (disable:4310) /* cast truncates constant value */ #endif @@ -17,7 +18,13 @@ # define LANG "C " #endif -#define VALUE(T, U) (T)((U)0xab << ((sizeof(T)-1)<<3)) +#if defined(__cplusplus) && !defined(__BORLANDC__) +# define STATIC_CAST(t,v) static_cast<t>(v) +#else +# define STATIC_CAST(t,v) (t)(v) +#endif + +#define VALUE(T, U) STATIC_CAST(T, STATIC_CAST(U, 0xab) << ((sizeof(T)-1)<<3)) #define TEST_C_(C, V, PRI, T, U) \ { \ diff --git a/Utilities/cmlibuv/src/unix/signal.c b/Utilities/cmlibuv/src/unix/signal.c index cb09ead..3759778 100644 --- a/Utilities/cmlibuv/src/unix/signal.c +++ b/Utilities/cmlibuv/src/unix/signal.c @@ -28,6 +28,9 @@ #include <string.h> #include <unistd.h> +#ifndef SA_RESTART +# define SA_RESTART 0 +#endif typedef struct { uv_signal_t* handle; @@ -216,7 +219,9 @@ static int uv__signal_register_handler(int signum, int oneshot) { if (sigfillset(&sa.sa_mask)) abort(); sa.sa_handler = uv__signal_handler; - sa.sa_flags = oneshot ? SA_RESETHAND : 0; + sa.sa_flags = SA_RESTART; + if (oneshot) + sa.sa_flags |= SA_RESETHAND; /* XXX save old action so we can restore it later on? */ if (sigaction(signum, &sa, NULL)) |