Merge topic 'add_xl_cxx14_support'
458ea9d76c XL: Add C++14 language level flags
Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !3235
diff --git a/Help/command/string.rst b/Help/command/string.rst
index 893fb43..2e89d7b 100644
--- a/Help/command/string.rst
+++ b/Help/command/string.rst
@@ -28,6 +28,7 @@
string(`SUBSTRING`_ <string> <begin> <length> <out-var>)
string(`STRIP`_ <string> <out-var>)
string(`GENEX_STRIP`_ <string> <out-var>)
+ string(`REPEAT`_ <string> <count> <out-var>)
`Comparison`_
string(`COMPARE`_ <op> <string1> <string2> <out-var>)
@@ -269,6 +270,14 @@
Strip any :manual:`generator expressions <cmake-generator-expressions(7)>`
from the ``input string`` and store the result in the ``output variable``.
+.. _REPEAT:
+
+.. code-block:: cmake
+
+ string(REPEAT <input string> <count> <output variable>)
+
+Produce the output string as repetion of ``input string`` ``count`` times.
+
Comparison
^^^^^^^^^^
diff --git a/Help/command/try_compile.rst b/Help/command/try_compile.rst
index ca8fc77..0bc2ca3 100644
--- a/Help/command/try_compile.rst
+++ b/Help/command/try_compile.rst
@@ -135,6 +135,7 @@
* :variable:`CMAKE_ENABLE_EXPORTS`
* :variable:`CMAKE_LINK_SEARCH_START_STATIC`
* :variable:`CMAKE_LINK_SEARCH_END_STATIC`
+* :variable:`CMAKE_MSVC_RUNTIME_LIBRARY`
* :variable:`CMAKE_POSITION_INDEPENDENT_CODE`
If :policy:`CMP0056` is set to ``NEW``, then
diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst
index e9b3f4c..7f4761f 100644
--- a/Help/manual/cmake-generator-expressions.7.rst
+++ b/Help/manual/cmake-generator-expressions.7.rst
@@ -387,14 +387,25 @@
``$<TARGET_NAME_IF_EXISTS:tgt>``
Expands to the ``tgt`` if the given target exists, an empty string
otherwise.
-``$<TARGET_OUTPUT_NAME:tgt>``
- Base name of main file where ``tgt`` is the name of a target.
-
- Note that ``tgt`` is not added as a dependency of the target this
- expression is evaluated on.
``$<TARGET_FILE:tgt>``
Full path to main file (.exe, .so.1.2, .a) where ``tgt`` is the name of a
target.
+``$<TARGET_FILE_BASE_NAME:tgt>``
+ Base name of main file where ``tgt`` is the name of a target.
+
+ The base name corresponds to the target file name (see
+ ``$<TARGET_FILE_NAME:tgt>``) without prefix and suffix. For example, if
+ target file name is ``libbase.so``, the base name is ``base``.
+
+ See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`,
+ :prop_tgt:`LIBRARY_OUTPUT_NAME` and :prop_tgt:`RUNTIME_OUTPUT_NAME`
+ target properties and their configuration specific variants
+ :prop_tgt:`OUTPUT_NAME_<CONFIG>`, :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>`,
+ :prop_tgt:`LIBRARY_OUTPUT_NAME_<CONFIG>` and
+ :prop_tgt:`RUNTIME_OUTPUT_NAME_<CONFIG>`.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
``$<TARGET_FILE_PREFIX:tgt>``
Prefix of main file where ``tgt`` is the name of a target.
@@ -409,13 +420,23 @@
Name of main file (.exe, .so.1.2, .a).
``$<TARGET_FILE_DIR:tgt>``
Directory of main file (.exe, .so.1.2, .a).
-``$<TARGET_LINKER_OUTPUT_NAME:tgt>``
+``$<TARGET_LINKER_FILE:tgt>``
+ File used to link (.a, .lib, .so) where ``tgt`` is the name of a target.
+``$<TARGET_LINKER_FILE_BASE_NAME:tgt>``
Base name of file used to link where ``tgt`` is the name of a target.
+ The base name corresponds to the target linker file name (see
+ ``$<TARGET_LINKER_FILE_NAME:tgt>``) without prefix and suffix. For example,
+ if target file name is ``libbase.a``, the base name is ``base``.
+
+ See also the :prop_tgt:`OUTPUT_NAME`, :prop_tgt:`ARCHIVE_OUTPUT_NAME`,
+ and :prop_tgt:`LIBRARY_OUTPUT_NAME` target properties and their configuration
+ specific variants :prop_tgt:`OUTPUT_NAME_<CONFIG>`,
+ :prop_tgt:`ARCHIVE_OUTPUT_NAME_<CONFIG>` and
+ :prop_tgt:`LIBRARY_OUTPUT_NAME_<CONFIG>`.
+
Note that ``tgt`` is not added as a dependency of the target this
expression is evaluated on.
-``$<TARGET_LINKER_FILE:tgt>``
- File used to link (.a, .lib, .so) where ``tgt`` is the name of a target.
``$<TARGET_LINKER_FILE_PREFIX:tgt>``
Prefix of file used to link where ``tgt`` is the name of a target.
@@ -436,15 +457,6 @@
Name of file with soname (.so.3).
``$<TARGET_SONAME_FILE_DIR:tgt>``
Directory of with soname (.so.3).
-``$<TARGET_PDB_OUTPUT_NAME:tgt>``
- Base name of the linker generated program database file (.pdb)
- where ``tgt`` is the name of a target.
-
- See also the :prop_tgt:`PDB_NAME` target property and its configuration
- specific variant :prop_tgt:`PDB_NAME_<CONFIG>`.
-
- Note that ``tgt`` is not added as a dependency of the target this
- expression is evaluated on.
``$<TARGET_PDB_FILE:tgt>``
Full path to the linker generated program database file (.pdb)
where ``tgt`` is the name of a target.
@@ -452,6 +464,19 @@
See also the :prop_tgt:`PDB_NAME` and :prop_tgt:`PDB_OUTPUT_DIRECTORY`
target properties and their configuration specific variants
:prop_tgt:`PDB_NAME_<CONFIG>` and :prop_tgt:`PDB_OUTPUT_DIRECTORY_<CONFIG>`.
+``$<TARGET_PDB_FILE_BASE_NAME:tgt>``
+ Base name of the linker generated program database file (.pdb)
+ where ``tgt`` is the name of a target.
+
+ The base name corresponds to the target PDB file name (see
+ ``$<TARGET_PDB_FILE_NAME:tgt>``) without prefix and suffix. For example,
+ if target file name is ``base.pdb``, the base name is ``base``.
+
+ See also the :prop_tgt:`PDB_NAME` target property and its configuration
+ specific variant :prop_tgt:`PDB_NAME_<CONFIG>`.
+
+ Note that ``tgt`` is not added as a dependency of the target this
+ expression is evaluated on.
``$<TARGET_PDB_FILE_NAME:tgt>``
Name of the linker generated program database file (.pdb).
``$<TARGET_PDB_FILE_DIR:tgt>``
diff --git a/Help/manual/cmake-policies.7.rst b/Help/manual/cmake-policies.7.rst
index e89ea3da..043fb5c 100644
--- a/Help/manual/cmake-policies.7.rst
+++ b/Help/manual/cmake-policies.7.rst
@@ -57,6 +57,7 @@
.. toctree::
:maxdepth: 1
+ CMP0091: MSVC runtime library flags are selected by an abstraction. </policy/CMP0091>
CMP0090: export(PACKAGE) does not populate package registry by default. </policy/CMP0090>
CMP0089: Compiler id for IBM Clang-based XL compilers is now XLClang. </policy/CMP0089>
diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index 4d4b9ff..c11496c 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -280,6 +280,7 @@
/prop_tgt/MACOSX_RPATH
/prop_tgt/MANUALLY_ADDED_DEPENDENCIES
/prop_tgt/MAP_IMPORTED_CONFIG_CONFIG
+ /prop_tgt/MSVC_RUNTIME_LIBRARY
/prop_tgt/NAME
/prop_tgt/NO_SONAME
/prop_tgt/NO_SYSTEM_FROM_IMPORTED
@@ -335,6 +336,7 @@
/prop_tgt/VS_KEYWORD
/prop_tgt/VS_MOBILE_EXTENSIONS_VERSION
/prop_tgt/VS_NO_SOLUTION_DEPLOY
+ /prop_tgt/VS_PROJECT_IMPORT
/prop_tgt/VS_SCC_AUXPATH
/prop_tgt/VS_SCC_LOCALPATH
/prop_tgt/VS_SCC_PROJECTNAME
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 18dd9d7..22e8add 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -386,6 +386,7 @@
/variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG_INIT
/variable/CMAKE_MODULE_LINKER_FLAGS_INIT
/variable/CMAKE_MSVCIDE_RUN_PATH
+ /variable/CMAKE_MSVC_RUNTIME_LIBRARY
/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX
/variable/CMAKE_NO_BUILTIN_CHRPATH
/variable/CMAKE_NO_SYSTEM_FROM_IMPORTED
diff --git a/Help/policy/CMP0091.rst b/Help/policy/CMP0091.rst
new file mode 100644
index 0000000..5b7c4e3
--- /dev/null
+++ b/Help/policy/CMP0091.rst
@@ -0,0 +1,47 @@
+CMP0091
+-------
+
+MSVC runtime library flags are selected by an abstraction.
+
+Compilers targeting the MSVC ABI have flags to select the MSVC runtime library.
+Runtime library selection typically varies with build configuration because
+there is a separate runtime library for Debug builds.
+
+In CMake 3.14 and below, MSVC runtime library selection flags are added to
+the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache entries by CMake
+automatically. This allows users to edit their cache entries to adjust the
+flags. However, the presence of such default flags is problematic for
+projects that want to choose a different runtime library programmatically.
+In particular, it requires string editing of the
+:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variables with knowledge of the
+CMake builtin defaults so they can be replaced.
+
+CMake 3.15 and above prefer to leave the MSVC runtime library selection flags
+out of the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` values and instead
+offer a first-class abstraction. The :variable:`CMAKE_MSVC_RUNTIME_LIBRARY`
+variable and :prop_tgt:`MSVC_RUNTIME_LIBRARY` target property may be set to
+select the MSVC runtime library.
+
+This policy provides compatibility with projects that have not been updated
+to be aware of the abstraction. The policy setting takes effect as of the
+first :command:`project` or :command:`enable_language` command that enables
+a language whose compiler targets the MSVC ABI.
+
+.. note::
+
+ Once the policy has taken effect at the top of a project, that choice
+ must be used throughout the tree. In projects that have nested projects
+ in subdirectories, be sure to convert everything together.
+
+The ``OLD`` behavior for this policy is to place MSVC runtime library
+flags in the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache
+entries and ignore the :variable:`CMAKE_MSVC_RUNTIME_LIBRARY` abstraction.
+The ``NEW`` behavior for this policy is to *not* place MSVC runtime
+library flags in the default cache entries and use the abstraction instead.
+
+This policy was introduced in CMake version 3.15. Use the
+:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
+Unlike many policies, CMake version |release| does *not* warn
+when this policy is not set and simply uses ``OLD`` behavior.
+
+.. include:: DEPRECATED.txt
diff --git a/Help/prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt b/Help/prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt
new file mode 100644
index 0000000..2bf71a9
--- /dev/null
+++ b/Help/prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt
@@ -0,0 +1,15 @@
+``MultiThreaded``
+ Compile with ``-MT`` or equivalent flag(s) to use a multi-threaded
+ statically-linked runtime library.
+``MultiThreadedDLL``
+ Compile with ``-MD`` or equivalent flag(s) to use a multi-threaded
+ dynamically-linked runtime library.
+``MultiThreadedDebug``
+ Compile with ``-MTd`` or equivalent flag(s) to use a multi-threaded
+ statically-linked runtime library.
+``MultiThreadedDebugDLL``
+ Compile with ``-MDd`` or equivalent flag(s) to use a multi-threaded
+ dynamically-linked runtime library.
+
+The value is ignored on non-MSVC compilers but an unsupported value will
+be rejected as an error when using a compiler targeting the MSVC ABI.
diff --git a/Help/prop_tgt/MSVC_RUNTIME_LIBRARY.rst b/Help/prop_tgt/MSVC_RUNTIME_LIBRARY.rst
new file mode 100644
index 0000000..1e3f5e9
--- /dev/null
+++ b/Help/prop_tgt/MSVC_RUNTIME_LIBRARY.rst
@@ -0,0 +1,26 @@
+MSVC_RUNTIME_LIBRARY
+--------------------
+
+Select the MSVC runtime library for use by compilers targeting the MSVC ABI.
+
+The allowed values are:
+
+.. include:: MSVC_RUNTIME_LIBRARY-VALUES.txt
+
+Use :manual:`generator expressions <cmake-generator-expressions(7)>` to
+support per-configuration specification. For example, the code:
+
+.. code-block:: cmake
+
+ add_executable(foo foo.c)
+ set_property(TARGET foo PROPERTY
+ MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
+
+selects for the target ``foo`` a multi-threaded statically-linked runtime
+library with or without debug information depending on the configuration.
+
+.. note::
+
+ This property has effect only when policy :policy:`CMP0091` is set to ``NEW``
+ prior to the first :command:`project` or :command:`enable_language` command
+ that enables a language using a compiler targeting the MSVC ABI.
diff --git a/Help/prop_tgt/VS_PROJECT_IMPORT.rst b/Help/prop_tgt/VS_PROJECT_IMPORT.rst
new file mode 100644
index 0000000..569c8ea
--- /dev/null
+++ b/Help/prop_tgt/VS_PROJECT_IMPORT.rst
@@ -0,0 +1,8 @@
+VS_PROJECT_IMPORT
+-----------------
+
+Visual Studio managed project imports
+
+Adds to a generated Visual Studio project one or more semicolon-delimited paths
+to .props files needed when building projects from some NuGet packages.
+For example, ``my_packages_path/MyPackage.1.0.0/build/MyPackage.props``.
diff --git a/Help/release/dev/genex-TARGET_FILE_BASE_NAME.rst b/Help/release/dev/genex-TARGET_FILE_BASE_NAME.rst
new file mode 100644
index 0000000..d8b2b21
--- /dev/null
+++ b/Help/release/dev/genex-TARGET_FILE_BASE_NAME.rst
@@ -0,0 +1,7 @@
+genex-TARGET_FILE_BASE_NAME
+---------------------------
+
+* New ``$<TARGET_FILE_BASE_NAME:...>``, ``$<TARGET_LINKER_FILE_BASE_NAME:...>``
+ and ``$<TARGET_PDB_FILE_BASE_NAME:...>``
+ :manual:`generator expressions <cmake-generator-expressions(7)>` have been
+ added to retrieve the base name of various artifacts.
diff --git a/Help/release/dev/genex-TARGET_OUTPUT_NAME.rst b/Help/release/dev/genex-TARGET_OUTPUT_NAME.rst
deleted file mode 100644
index e3ffe57..0000000
--- a/Help/release/dev/genex-TARGET_OUTPUT_NAME.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-genex-TARGET_OUTPUT_NAME
-------------------------
-
-* New ``$<TARGET_OUTPUT_NAME:...>``, ``$<TARGET_LINKER_OUTPUT_NAME:...>`` and
- ``$<TARGET_PDB_OUTPUT_NAME:...>``
- :manual:`generator expressions <cmake-generator-expressions(7)>` have been
- added to retrieve the base name of various artifacts.
diff --git a/Help/release/dev/msvc-runtime-library.rst b/Help/release/dev/msvc-runtime-library.rst
new file mode 100644
index 0000000..4dddac2
--- /dev/null
+++ b/Help/release/dev/msvc-runtime-library.rst
@@ -0,0 +1,7 @@
+msvc-runtime-library
+--------------------
+
+* The :variable:`CMAKE_MSVC_RUNTIME_LIBRARY` variable and
+ :prop_tgt:`MSVC_RUNTIME_LIBRARY` target property were introduced to
+ select the runtime library used by compilers targeting the MSVC ABI.
+ See policy :policy:`CMP0091`.
diff --git a/Help/release/dev/string-repeat.rst b/Help/release/dev/string-repeat.rst
new file mode 100644
index 0000000..4be0d5c
--- /dev/null
+++ b/Help/release/dev/string-repeat.rst
@@ -0,0 +1,4 @@
+string-repeat
+--------------
+
+* The :command:`string` learned a new sub-command ``REPEAT``.
diff --git a/Help/release/dev/vs-project-import.rst b/Help/release/dev/vs-project-import.rst
new file mode 100644
index 0000000..de6024d
--- /dev/null
+++ b/Help/release/dev/vs-project-import.rst
@@ -0,0 +1,5 @@
+vs-project-import
+-----------------
+
+* The :prop_tgt:`VS_PROJECT_IMPORT` target property was added which allows
+ to import external .props files in managed Visual Studio targets.
diff --git a/Help/variable/CMAKE_MSVC_RUNTIME_LIBRARY.rst b/Help/variable/CMAKE_MSVC_RUNTIME_LIBRARY.rst
new file mode 100644
index 0000000..6ed68c9
--- /dev/null
+++ b/Help/variable/CMAKE_MSVC_RUNTIME_LIBRARY.rst
@@ -0,0 +1,27 @@
+CMAKE_MSVC_RUNTIME_LIBRARY
+--------------------------
+
+Select the MSVC runtime library for use by compilers targeting the MSVC ABI.
+This variable is used to initialize the :prop_tgt:`MSVC_RUNTIME_LIBRARY`
+property on all targets as they are created. It is also propagated by
+calls to the :command:`try_compile` command into the test project.
+
+The allowed values are:
+
+.. include:: ../prop_tgt/MSVC_RUNTIME_LIBRARY-VALUES.txt
+
+Use :manual:`generator expressions <cmake-generator-expressions(7)>` to
+support per-configuration specification. For example, the code:
+
+.. code-block:: cmake
+
+ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
+
+selects for all following targets a multi-threaded statically-linked runtime
+library with or without debug information depending on the configuration.
+
+.. note::
+
+ This variable has effect only when policy :policy:`CMP0091` is set to ``NEW``
+ prior to the first :command:`project` or :command:`enable_language` command
+ that enables a language using a compiler targeting the MSVC ABI.
diff --git a/Modules/Compiler/Clang-C.cmake b/Modules/Compiler/Clang-C.cmake
index ff51d30..0448965 100644
--- a/Modules/Compiler/Clang-C.cmake
+++ b/Modules/Compiler/Clang-C.cmake
@@ -23,12 +23,15 @@
# clang-cl doesn't have any of these
set(CMAKE_C90_STANDARD_COMPILE_OPTION "")
set(CMAKE_C90_EXTENSION_COMPILE_OPTION "")
+ set(CMAKE_C90_STANDARD__HAS_FULL_SUPPORT ON)
set(CMAKE_C99_STANDARD_COMPILE_OPTION "")
set(CMAKE_C99_EXTENSION_COMPILE_OPTION "")
+ set(CMAKE_C99_STANDARD__HAS_FULL_SUPPORT ON)
set(CMAKE_C11_STANDARD_COMPILE_OPTION "")
set(CMAKE_C11_EXTENSION_COMPILE_OPTION "")
+ set(CMAKE_C11_STANDARD__HAS_FULL_SUPPORT ON)
endif()
endif()
diff --git a/Modules/Compiler/MSVC-C.cmake b/Modules/Compiler/MSVC-C.cmake
index 22c34f8..a722130 100644
--- a/Modules/Compiler/MSVC-C.cmake
+++ b/Modules/Compiler/MSVC-C.cmake
@@ -11,8 +11,8 @@
# There is no meaningful default for this
set(CMAKE_C_STANDARD_DEFAULT "")
-# There are no C compiler modes so we only need to test features once.
-# Override the default macro for this special case. Pretend that
+# There are no C compiler modes so we hard-code the known compiler supported
+# features. Override the default macro for this special case. Pretend that
# all language standards are available so that at least compilation
# can be attempted.
macro(cmake_record_c_compile_features)
@@ -20,6 +20,10 @@
c_std_90
c_std_99
c_std_11
+ c_function_prototypes
+ c_variadic_macros
)
- _record_compiler_features(C "" CMAKE_C_COMPILE_FEATURES)
+ list(APPEND CMAKE_C90_COMPILE_FEATURES c_std_90 c_function_prototypes)
+ list(APPEND CMAKE_C99_COMPILE_FEATURES c_std_99 c_variadic_macros)
+ list(APPEND CMAKE_C11_COMPILE_FEATURES c_std_11)
endmacro()
diff --git a/Modules/FindBoost.cmake b/Modules/FindBoost.cmake
index c30c6c8..7325eca 100644
--- a/Modules/FindBoost.cmake
+++ b/Modules/FindBoost.cmake
@@ -1777,7 +1777,7 @@
list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}${COMPONENT_PYTHON_VERSION_MAJOR}-py${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-py${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
# Gentoo
- list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
+ list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}.${COMPONENT_PYTHON_VERSION_MINOR}")
# RPMs
list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
endif()
diff --git a/Modules/FindOpenGL.cmake b/Modules/FindOpenGL.cmake
index 832dca2..00db033 100644
--- a/Modules/FindOpenGL.cmake
+++ b/Modules/FindOpenGL.cmake
@@ -205,11 +205,13 @@
find_library(OPENGL_glx_LIBRARY
NAMES GLX
PATHS ${_OPENGL_LIB_PATH}
+ PATH_SUFFIXES libglvnd
)
find_library(OPENGL_egl_LIBRARY
NAMES EGL
PATHS ${_OPENGL_LIB_PATH}
+ PATH_SUFFIXES libglvnd
)
find_library(OPENGL_glu_LIBRARY
@@ -264,6 +266,7 @@
/usr/openwin/lib
/usr/shlib
${_OPENGL_LIB_PATH}
+ PATH_SUFFIXES libglvnd
)
endif()
diff --git a/Modules/InstallRequiredSystemLibraries.cmake b/Modules/InstallRequiredSystemLibraries.cmake
index 2f34a7a..0a98895 100644
--- a/Modules/InstallRequiredSystemLibraries.cmake
+++ b/Modules/InstallRequiredSystemLibraries.cmake
@@ -213,10 +213,13 @@
elseif(MSVC_VERSION_VERSION GREATER_EQUAL 143)
message(WARNING "MSVC toolset v${MSVC_VERSION_VERSION} not yet supported.")
elseif(MSVC_TOOLSET_VERSION EQUAL 142)
- # FIXME: VS 2019 RC 4 uses VC141 but an update will fix it to be VC142.
- set(MSVC_REDIST_NAME VC141)
+ set(MSVC_REDIST_NAME VC142)
set(_MSVC_DLL_VERSION 140)
set(_MSVC_IDE_VERSION 16)
+ if(MSVC_VERSION EQUAL 1920)
+ # VS2019 named this differently prior to update 1.
+ set(MSVC_REDIST_NAME VC141)
+ endif()
elseif(MSVC_TOOLSET_VERSION EQUAL 141)
set(MSVC_REDIST_NAME VC141)
set(_MSVC_DLL_VERSION 140)
diff --git a/Modules/Platform/Windows-Intel-Fortran.cmake b/Modules/Platform/Windows-Intel-Fortran.cmake
index 3981a09..f00a8e4 100644
--- a/Modules/Platform/Windows-Intel-Fortran.cmake
+++ b/Modules/Platform/Windows-Intel-Fortran.cmake
@@ -4,8 +4,34 @@
set(CMAKE_Fortran_MODDIR_FLAG "-module:")
set(CMAKE_Fortran_STANDARD_LIBRARIES_INIT "user32.lib")
__windows_compiler_intel(Fortran)
-string(APPEND CMAKE_Fortran_FLAGS_INIT " /W1 /nologo /fpp /libs:dll /threads")
-string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT " /Od /debug:full /dbglibs")
+if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
+ set(_LIBSDLL "")
+ set(_DBGLIBS "")
+ set(_THREADS "")
+else()
+ set(_LIBSDLL " /libs:dll")
+ set(_DBGLIBS " /dbglibs")
+ set(_THREADS " /threads")
+endif()
+string(APPEND CMAKE_Fortran_FLAGS_INIT " /W1 /nologo /fpp${_LIBSDLL}${_THREADS}")
+string(APPEND CMAKE_Fortran_FLAGS_DEBUG_INIT " /Od /debug:full${_DBGLIBS}")
string(APPEND CMAKE_Fortran_FLAGS_MINSIZEREL_INIT " /O1 /DNDEBUG")
string(APPEND CMAKE_Fortran_FLAGS_RELEASE_INIT " /O2 /DNDEBUG")
string(APPEND CMAKE_Fortran_FLAGS_RELWITHDEBINFO_INIT " /O2 /debug:full /DNDEBUG")
+unset(_LIBSDLL)
+unset(_DBGLIBS)
+unset(_THREADS)
+
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded -threads -libs:static)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL -threads -libs:dll)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug -threads -libs:static -dbglibs)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -threads -libs:dll -dbglibs)
+
+# Intel Fortran for Windows supports single-threaded RTL but it is
+# not implemented by the Visual Studio integration.
+if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
+ set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_SingleThreaded -libs:static)
+ set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_SingleThreadedDLL -libs:dll)
+ set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_SingleThreadedDebug -libs:static -dbglibs)
+ set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_SingleThreadedDebugDLL -libs:dll -dbglibs)
+endif()
diff --git a/Modules/Platform/Windows-MSVC.cmake b/Modules/Platform/Windows-MSVC.cmake
index df3bd1e..4279a74 100644
--- a/Modules/Platform/Windows-MSVC.cmake
+++ b/Modules/Platform/Windows-MSVC.cmake
@@ -298,6 +298,14 @@
string(APPEND CMAKE_STATIC_LINKER_FLAGS_INIT " ${_MACHINE_ARCH_FLAG}")
unset(_MACHINE_ARCH_FLAG)
+cmake_policy(GET CMP0091 __WINDOWS_MSVC_CMP0091)
+if(__WINDOWS_MSVC_CMP0091 STREQUAL "NEW")
+ set(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
+else()
+ set(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT "")
+endif()
+unset(__WINDOWS_MSVC_CMP0091)
+
macro(__windows_compiler_msvc lang)
if(NOT MSVC_VERSION LESS 1400)
# for 2005 make sure the manifest is put in the dll with mt
@@ -351,21 +359,35 @@
if("x${lang}" STREQUAL "xC" OR
"x${lang}" STREQUAL "xCXX")
+ if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
+ set(_MDd "")
+ set(_MD "")
+ else()
+ set(_MDd " /MDd")
+ set(_MD " /MD")
+ endif()
if(CMAKE_VS_PLATFORM_TOOLSET MATCHES "v[0-9]+_clang_.*")
# note: MSVC 14 2015 Update 1 sets -fno-ms-compatibility by default, but this does not allow one to compile many projects
# that include MS's own headers. CMake itself is affected project too.
string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_${lang}} -fms-extensions -fms-compatibility -D_WINDOWS -Wall${_FLAGS_${lang}}")
- string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " /MDd -gline-tables-only -fno-inline -O0 ${_RTC1}")
- string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " /MD -O2 -DNDEBUG")
- string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " /MD -gline-tables-only -O2 -fno-inline -DNDEBUG")
- string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " /MD -DNDEBUG") # TODO: Add '-Os' once VS generator maps it properly for Clang
+ string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd} -gline-tables-only -fno-inline -O0 ${_RTC1}")
+ string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT "${_MD} -O2 -DNDEBUG")
+ string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD} -gline-tables-only -O2 -fno-inline -DNDEBUG")
+ string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "${_MD} -DNDEBUG") # TODO: Add '-Os' once VS generator maps it properly for Clang
else()
string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_${lang}} /D_WINDOWS /W3${_FLAGS_${lang}}")
- string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT " /MDd /Zi /Ob0 /Od ${_RTC1}")
- string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " /MD /O2 /Ob2 /DNDEBUG")
- string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " /MD /Zi /O2 /Ob1 /DNDEBUG")
- string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT " /MD /O1 /Ob1 /DNDEBUG")
+ string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd} /Zi /Ob0 /Od ${_RTC1}")
+ string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT "${_MD} /O2 /Ob2 /DNDEBUG")
+ string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD} /Zi /O2 /Ob1 /DNDEBUG")
+ string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "${_MD} /O1 /Ob1 /DNDEBUG")
endif()
+ unset(_MDd)
+ unset(_MD)
+
+ set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded -MT)
+ set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL -MD)
+ set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug -MTd)
+ set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL -MDd)
endif()
set(CMAKE_${lang}_LINKER_SUPPORTS_PDB ON)
set(CMAKE_NINJA_DEPTYPE_${lang} msvc)
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 924d997..49f237f 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -350,8 +350,8 @@
cmQtAutoGenGlobalInitializer.h
cmQtAutoGenInitializer.cxx
cmQtAutoGenInitializer.h
- cmQtAutoGeneratorMocUic.cxx
- cmQtAutoGeneratorMocUic.h
+ cmQtAutoMocUic.cxx
+ cmQtAutoMocUic.h
cmQtAutoRcc.cxx
cmQtAutoRcc.h
cmRST.cxx
@@ -391,6 +391,8 @@
cmVariableWatch.h
cmVersion.cxx
cmVersion.h
+ cmWorkerPool.cxx
+ cmWorkerPool.h
cmWorkingDirectory.cxx
cmWorkingDirectory.h
cmXMLParser.cxx
diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake
index 4e7e400..d4d5da5 100644
--- a/Source/CMakeVersion.cmake
+++ b/Source/CMakeVersion.cmake
@@ -1,5 +1,5 @@
# CMake version number components.
set(CMake_VERSION_MAJOR 3)
set(CMake_VERSION_MINOR 14)
-set(CMake_VERSION_PATCH 20190415)
+set(CMake_VERSION_PATCH 20190418)
#set(CMake_VERSION_RC 1)
diff --git a/Source/cmCoreTryCompile.cxx b/Source/cmCoreTryCompile.cxx
index dcb1ff5..897f7a8 100644
--- a/Source/cmCoreTryCompile.cxx
+++ b/Source/cmCoreTryCompile.cxx
@@ -20,6 +20,7 @@
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmVersion.h"
+#include "cm_static_string_view.hxx"
#include "cmake.h"
static std::string const kCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN =
@@ -42,6 +43,8 @@
"CMAKE_LINK_SEARCH_END_STATIC";
static std::string const kCMAKE_LINK_SEARCH_START_STATIC =
"CMAKE_LINK_SEARCH_START_STATIC";
+static std::string const kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT =
+ "CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT";
static std::string const kCMAKE_OSX_ARCHITECTURES = "CMAKE_OSX_ARCHITECTURES";
static std::string const kCMAKE_OSX_DEPLOYMENT_TARGET =
"CMAKE_OSX_DEPLOYMENT_TARGET";
@@ -500,6 +503,13 @@
fprintf(fout, "set(CMAKE_MODULE_PATH \"%s\")\n", def);
}
+ /* Set MSVC runtime library policy to match our selection. */
+ if (const char* msvcRuntimeLibraryDefault =
+ this->Makefile->GetDefinition(kCMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)) {
+ fprintf(fout, "cmake_policy(SET CMP0091 %s)\n",
+ *msvcRuntimeLibraryDefault ? "NEW" : "OLD");
+ }
+
std::string projectLangs;
for (std::string const& li : testLangs) {
projectLangs += " " + li;
@@ -660,6 +670,7 @@
vars.insert(kCMAKE_SYSROOT_COMPILE);
vars.insert(kCMAKE_SYSROOT_LINK);
vars.insert(kCMAKE_WARN_DEPRECATED);
+ vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s);
if (const char* varListStr = this->Makefile->GetDefinition(
kCMAKE_TRY_COMPILE_PLATFORM_VARIABLES)) {
diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx
index af409e4..8b3d9d6 100644
--- a/Source/cmGeneratorExpressionNode.cxx
+++ b/Source/cmGeneratorExpressionNode.cxx
@@ -2210,8 +2210,8 @@
// The file used to link to the target (.so, .lib, .a).
if (!target->IsLinkable()) {
::reportError(context, content->GetOriginalExpression(),
- "TARGET_LINKER_OUTPUT_NAME is allowed only for libraries "
- "and executables with ENABLE_EXPORTS.");
+ "TARGET_LINKER_FILE_BASE_NAME is allowed only for "
+ "libraries and executables with ENABLE_EXPORTS.");
return std::string();
}
cmStateEnums::ArtifactType artifact =
@@ -2232,7 +2232,7 @@
if (target->IsImported()) {
::reportError(
context, content->GetOriginalExpression(),
- "TARGET_PDB_OUTPUT_NAME not allowed for IMPORTED targets.");
+ "TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets.");
return std::string();
}
@@ -2243,7 +2243,7 @@
if (!context->LG->GetMakefile()->IsOn(pdbSupportVar)) {
::reportError(
context, content->GetOriginalExpression(),
- "TARGET_PDB_OUTPUT_NAME is not supported by the target linker.");
+ "TARGET_PDB_FILE_BASE_NAME is not supported by the target linker.");
return std::string();
}
@@ -2253,7 +2253,7 @@
targetType != cmStateEnums::MODULE_LIBRARY &&
targetType != cmStateEnums::EXECUTABLE) {
::reportError(context, content->GetOriginalExpression(),
- "TARGET_PDB_OUTPUT_NAME is allowed only for "
+ "TARGET_PDB_FILE_BASE_NAME is allowed only for "
"targets with linker created artifacts.");
return std::string();
}
@@ -2263,9 +2263,9 @@
};
template <typename ArtifactT>
-struct TargetOutputNameArtifact : public TargetArtifactBase
+struct TargetFileBaseNameArtifact : public TargetArtifactBase
{
- TargetOutputNameArtifact() {} // NOLINT(modernize-use-equals-default)
+ TargetFileBaseNameArtifact() {} // NOLINT(modernize-use-equals-default)
int NumExpectedParameters() const override { return 1; }
@@ -2290,12 +2290,12 @@
}
};
-static const TargetOutputNameArtifact<ArtifactNameTag> targetOutputNameNode;
-
-static const TargetOutputNameArtifact<ArtifactLinkerTag>
- targetLinkerOutputNameNode;
-
-static const TargetOutputNameArtifact<ArtifactPdbTag> targetPdbOutputNameNode;
+static const TargetFileBaseNameArtifact<ArtifactNameTag>
+ targetFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactLinkerTag>
+ targetLinkerFileBaseNameNode;
+static const TargetFileBaseNameArtifact<ArtifactPdbTag>
+ targetPdbFileBaseNameNode;
class ArtifactFilePrefixTag;
class ArtifactLinkerFilePrefixTag;
@@ -2474,6 +2474,9 @@
{ "TARGET_LINKER_FILE", &targetLinkerNodeGroup.File },
{ "TARGET_SONAME_FILE", &targetSoNameNodeGroup.File },
{ "TARGET_PDB_FILE", &targetPdbNodeGroup.File },
+ { "TARGET_FILE_BASE_NAME", &targetFileBaseNameNode },
+ { "TARGET_LINKER_FILE_BASE_NAME", &targetLinkerFileBaseNameNode },
+ { "TARGET_PDB_FILE_BASE_NAME", &targetPdbFileBaseNameNode },
{ "TARGET_FILE_PREFIX", &targetFilePrefixNode },
{ "TARGET_LINKER_FILE_PREFIX", &targetLinkerFilePrefixNode },
{ "TARGET_FILE_SUFFIX", &targetFileSuffixNode },
@@ -2488,9 +2491,6 @@
{ "TARGET_PDB_FILE_DIR", &targetPdbNodeGroup.FileDir },
{ "TARGET_BUNDLE_DIR", &targetBundleDirNode },
{ "TARGET_BUNDLE_CONTENT_DIR", &targetBundleContentDirNode },
- { "TARGET_OUTPUT_NAME", &targetOutputNameNode },
- { "TARGET_LINKER_OUTPUT_NAME", &targetLinkerOutputNameNode },
- { "TARGET_PDB_OUTPUT_NAME", &targetPdbOutputNameNode },
{ "STREQUAL", &strEqualNode },
{ "EQUAL", &equalNode },
{ "IN_LIST", &inListNode },
diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx
index 67763d4..8b51834 100644
--- a/Source/cmLocalGenerator.cxx
+++ b/Source/cmLocalGenerator.cxx
@@ -6,6 +6,7 @@
#include "cmComputeLinkInformation.h"
#include "cmCustomCommandGenerator.h"
#include "cmGeneratedFileStream.h"
+#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionEvaluationFile.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
@@ -1519,8 +1520,40 @@
flagsVar += "_FLAGS";
this->AddConfigVariableFlags(flags, flagsVar, config);
- // Placeholder for possible future per-target flags.
- static_cast<void>(target);
+ // Add MSVC runtime library flags. This is activated by the presence
+ // of a default selection whether or not it is overridden by a property.
+ const char* msvcRuntimeLibraryDefault =
+ this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT");
+ if (msvcRuntimeLibraryDefault && *msvcRuntimeLibraryDefault) {
+ const char* msvcRuntimeLibraryValue =
+ target->GetProperty("MSVC_RUNTIME_LIBRARY");
+ if (!msvcRuntimeLibraryValue) {
+ msvcRuntimeLibraryValue = msvcRuntimeLibraryDefault;
+ }
+ cmGeneratorExpression ge;
+ std::unique_ptr<cmCompiledGeneratorExpression> cge =
+ ge.Parse(msvcRuntimeLibraryValue);
+ std::string const msvcRuntimeLibrary =
+ cge->Evaluate(this, config, false, target);
+ if (!msvcRuntimeLibrary.empty()) {
+ if (const char* msvcRuntimeLibraryOptions =
+ this->Makefile->GetDefinition(
+ "CMAKE_" + lang + "_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_" +
+ msvcRuntimeLibrary)) {
+ this->AppendCompileOptions(flags, msvcRuntimeLibraryOptions);
+ } else if ((this->Makefile->GetSafeDefinition(
+ "CMAKE_" + lang + "_COMPILER_ID") == "MSVC" ||
+ this->Makefile->GetSafeDefinition(
+ "CMAKE_" + lang + "_SIMULATE_ID") == "MSVC") &&
+ !cmSystemTools::GetErrorOccuredFlag()) {
+ // The compiler uses the MSVC ABI so it needs a known runtime library.
+ this->IssueMessage(MessageType::FATAL_ERROR,
+ "MSVC_RUNTIME_LIBRARY value '" +
+ msvcRuntimeLibrary + "' not known for this " +
+ lang + " compiler.");
+ }
+ }
+ }
}
void cmLocalGenerator::AddLanguageFlagsForLinking(
diff --git a/Source/cmPolicies.h b/Source/cmPolicies.h
index 02a6295..113dd35 100644
--- a/Source/cmPolicies.h
+++ b/Source/cmPolicies.h
@@ -267,7 +267,10 @@
15, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0090, \
"export(PACKAGE) does not populate package registry by default.", 3, \
- 15, 0, cmPolicies::WARN)
+ 15, 0, cmPolicies::WARN) \
+ SELECT(POLICY, CMP0091, \
+ "MSVC runtime library flags are selected by an abstraction.", 3, 15, \
+ 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \
diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
index f115016..6fbea82 100644
--- a/Source/cmQtAutoGenerator.cxx
+++ b/Source/cmQtAutoGenerator.cxx
@@ -14,12 +14,6 @@
#include "cmSystemTools.h"
#include "cmake.h"
-#include <algorithm>
-#include <sstream>
-#include <utility>
-
-// -- Class methods
-
cmQtAutoGenerator::Logger::Logger()
{
// Initialize logger
@@ -431,232 +425,6 @@
return cmQtAutoGenerator::MakeParentDirectory(filename);
}
-int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::init(uv_loop_t* uv_loop,
- ReadOnlyProcessT* process)
-{
- Process_ = process;
- Target_ = nullptr;
- return UVPipe_.init(*uv_loop, 0, this);
-}
-
-int cmQtAutoGenerator::ReadOnlyProcessT::PipeT::startRead(std::string* target)
-{
- Target_ = target;
- return uv_read_start(uv_stream(), &PipeT::UVAlloc, &PipeT::UVData);
-}
-
-void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::reset()
-{
- Process_ = nullptr;
- Target_ = nullptr;
- UVPipe_.reset();
- Buffer_.clear();
- Buffer_.shrink_to_fit();
-}
-
-void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVAlloc(uv_handle_t* handle,
- size_t suggestedSize,
- uv_buf_t* buf)
-{
- auto& pipe = *reinterpret_cast<PipeT*>(handle->data);
- pipe.Buffer_.resize(suggestedSize);
- buf->base = pipe.Buffer_.data();
- buf->len = pipe.Buffer_.size();
-}
-
-void cmQtAutoGenerator::ReadOnlyProcessT::PipeT::UVData(uv_stream_t* stream,
- ssize_t nread,
- const uv_buf_t* buf)
-{
- auto& pipe = *reinterpret_cast<PipeT*>(stream->data);
- if (nread > 0) {
- // Append data to merged output
- if ((buf->base != nullptr) && (pipe.Target_ != nullptr)) {
- pipe.Target_->append(buf->base, nread);
- }
- } else if (nread < 0) {
- // EOF or error
- auto* proc = pipe.Process_;
- // Check it this an unusual error
- if (nread != UV_EOF) {
- if (!proc->Result()->error()) {
- proc->Result()->ErrorMessage =
- "libuv reading from pipe failed with error code ";
- proc->Result()->ErrorMessage += std::to_string(nread);
- }
- }
- // Clear libuv pipe handle and try to finish
- pipe.reset();
- proc->UVTryFinish();
- }
-}
-
-void cmQtAutoGenerator::ProcessResultT::reset()
-{
- ExitStatus = 0;
- TermSignal = 0;
- if (!StdOut.empty()) {
- StdOut.clear();
- StdOut.shrink_to_fit();
- }
- if (!StdErr.empty()) {
- StdErr.clear();
- StdErr.shrink_to_fit();
- }
- if (!ErrorMessage.empty()) {
- ErrorMessage.clear();
- ErrorMessage.shrink_to_fit();
- }
-}
-
-void cmQtAutoGenerator::ReadOnlyProcessT::setup(
- ProcessResultT* result, bool mergedOutput,
- std::vector<std::string> const& command, std::string const& workingDirectory)
-{
- Setup_.WorkingDirectory = workingDirectory;
- Setup_.Command = command;
- Setup_.Result = result;
- Setup_.MergedOutput = mergedOutput;
-}
-
-static std::string getUVError(const char* prefixString, int uvErrorCode)
-{
- std::ostringstream ost;
- ost << prefixString << ": " << uv_strerror(uvErrorCode);
- return ost.str();
-}
-
-bool cmQtAutoGenerator::ReadOnlyProcessT::start(
- uv_loop_t* uv_loop, std::function<void()>&& finishedCallback)
-{
- if (IsStarted() || (Result() == nullptr)) {
- return false;
- }
-
- // Reset result before the start
- Result()->reset();
-
- // Fill command string pointers
- if (!Setup().Command.empty()) {
- CommandPtr_.reserve(Setup().Command.size() + 1);
- for (std::string const& arg : Setup().Command) {
- CommandPtr_.push_back(arg.c_str());
- }
- CommandPtr_.push_back(nullptr);
- } else {
- Result()->ErrorMessage = "Empty command";
- }
-
- if (!Result()->error()) {
- if (UVPipeOut_.init(uv_loop, this) != 0) {
- Result()->ErrorMessage = "libuv stdout pipe initialization failed";
- }
- }
- if (!Result()->error()) {
- if (UVPipeErr_.init(uv_loop, this) != 0) {
- Result()->ErrorMessage = "libuv stderr pipe initialization failed";
- }
- }
- if (!Result()->error()) {
- // -- Setup process stdio options
- // stdin
- UVOptionsStdIO_[0].flags = UV_IGNORE;
- UVOptionsStdIO_[0].data.stream = nullptr;
- // stdout
- UVOptionsStdIO_[1].flags =
- static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
- UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream();
- // stderr
- UVOptionsStdIO_[2].flags =
- static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
- UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream();
-
- // -- Setup process options
- std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0);
- UVOptions_.exit_cb = &ReadOnlyProcessT::UVExit;
- UVOptions_.file = CommandPtr_[0];
- UVOptions_.args = const_cast<char**>(CommandPtr_.data());
- UVOptions_.cwd = Setup_.WorkingDirectory.c_str();
- UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE;
- UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size());
- UVOptions_.stdio = UVOptionsStdIO_.data();
-
- // -- Spawn process
- int uvErrorCode = UVProcess_.spawn(*uv_loop, UVOptions_, this);
- if (uvErrorCode != 0) {
- Result()->ErrorMessage =
- getUVError("libuv process spawn failed ", uvErrorCode);
- }
- }
- // -- Start reading from stdio streams
- if (!Result()->error()) {
- if (UVPipeOut_.startRead(&Result()->StdOut) != 0) {
- Result()->ErrorMessage = "libuv start reading from stdout pipe failed";
- }
- }
- if (!Result()->error()) {
- if (UVPipeErr_.startRead(Setup_.MergedOutput ? &Result()->StdOut
- : &Result()->StdErr) != 0) {
- Result()->ErrorMessage = "libuv start reading from stderr pipe failed";
- }
- }
-
- if (!Result()->error()) {
- IsStarted_ = true;
- FinishedCallback_ = std::move(finishedCallback);
- } else {
- // Clear libuv handles and finish
- UVProcess_.reset();
- UVPipeOut_.reset();
- UVPipeErr_.reset();
- CommandPtr_.clear();
- }
-
- return IsStarted();
-}
-
-void cmQtAutoGenerator::ReadOnlyProcessT::UVExit(uv_process_t* handle,
- int64_t exitStatus,
- int termSignal)
-{
- auto& proc = *reinterpret_cast<ReadOnlyProcessT*>(handle->data);
- if (proc.IsStarted() && !proc.IsFinished()) {
- // Set error message on demand
- proc.Result()->ExitStatus = exitStatus;
- proc.Result()->TermSignal = termSignal;
- if (!proc.Result()->error()) {
- if (termSignal != 0) {
- proc.Result()->ErrorMessage = "Process was terminated by signal ";
- proc.Result()->ErrorMessage +=
- std::to_string(proc.Result()->TermSignal);
- } else if (exitStatus != 0) {
- proc.Result()->ErrorMessage = "Process failed with return value ";
- proc.Result()->ErrorMessage +=
- std::to_string(proc.Result()->ExitStatus);
- }
- }
-
- // Reset process handle and try to finish
- proc.UVProcess_.reset();
- proc.UVTryFinish();
- }
-}
-
-void cmQtAutoGenerator::ReadOnlyProcessT::UVTryFinish()
-{
- // There still might be data in the pipes after the process has finished.
- // Therefore check if the process is finished AND all pipes are closed
- // before signaling the worker thread to continue.
- if (UVProcess_.get() == nullptr) {
- if (UVPipeOut_.uv_pipe() == nullptr) {
- if (UVPipeErr_.uv_pipe() == nullptr) {
- IsFinished_ = true;
- FinishedCallback_();
- }
- }
- }
-}
-
cmQtAutoGenerator::cmQtAutoGenerator() = default;
cmQtAutoGenerator::~cmQtAutoGenerator() = default;
diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h
index 479d357..437fa20 100644
--- a/Source/cmQtAutoGenerator.h
+++ b/Source/cmQtAutoGenerator.h
@@ -7,14 +7,8 @@
#include "cmFilePathChecksum.h"
#include "cmQtAutoGen.h"
-#include "cmUVHandlePtr.h"
-#include "cm_uv.h"
-#include <array>
-#include <functional>
#include <mutex>
-#include <stddef.h>
-#include <stdint.h>
#include <string>
#include <vector>
@@ -137,102 +131,6 @@
cmFilePathChecksum FilePathChecksum_;
};
- /// @brief Return value and output of an external process
- struct ProcessResultT
- {
- void reset();
- bool error() const
- {
- return (ExitStatus != 0) || (TermSignal != 0) || !ErrorMessage.empty();
- }
-
- std::int64_t ExitStatus = 0;
- int TermSignal = 0;
- std::string StdOut;
- std::string StdErr;
- std::string ErrorMessage;
- };
-
- /// @brief External process management class
- struct ReadOnlyProcessT
- {
- // -- Types
-
- /// @brief libuv pipe buffer class
- class PipeT
- {
- public:
- int init(uv_loop_t* uv_loop, ReadOnlyProcessT* process);
- int startRead(std::string* target);
- void reset();
-
- // -- Libuv casts
- uv_pipe_t* uv_pipe() { return UVPipe_.get(); }
- uv_stream_t* uv_stream()
- {
- return reinterpret_cast<uv_stream_t*>(uv_pipe());
- }
- uv_handle_t* uv_handle()
- {
- return reinterpret_cast<uv_handle_t*>(uv_pipe());
- }
-
- // -- Libuv callbacks
- static void UVAlloc(uv_handle_t* handle, size_t suggestedSize,
- uv_buf_t* buf);
- static void UVData(uv_stream_t* stream, ssize_t nread,
- const uv_buf_t* buf);
-
- private:
- ReadOnlyProcessT* Process_ = nullptr;
- std::string* Target_ = nullptr;
- std::vector<char> Buffer_;
- cm::uv_pipe_ptr UVPipe_;
- };
-
- /// @brief Process settings
- struct SetupT
- {
- std::string WorkingDirectory;
- std::vector<std::string> Command;
- ProcessResultT* Result = nullptr;
- bool MergedOutput = false;
- };
-
- // -- Const accessors
- const SetupT& Setup() const { return Setup_; }
- ProcessResultT* Result() const { return Setup_.Result; }
- bool IsStarted() const { return IsStarted_; }
- bool IsFinished() const { return IsFinished_; }
-
- // -- Runtime
- void setup(ProcessResultT* result, bool mergedOutput,
- std::vector<std::string> const& command,
- std::string const& workingDirectory = std::string());
- bool start(uv_loop_t* uv_loop, std::function<void()>&& finishedCallback);
-
- private:
- // -- Friends
- friend class PipeT;
- // -- Libuv callbacks
- static void UVExit(uv_process_t* handle, int64_t exitStatus,
- int termSignal);
- void UVTryFinish();
-
- // -- Setup
- SetupT Setup_;
- // -- Runtime
- bool IsStarted_ = false;
- bool IsFinished_ = false;
- std::function<void()> FinishedCallback_;
- std::vector<const char*> CommandPtr_;
- std::array<uv_stdio_container_t, 3> UVOptionsStdIO_;
- uv_process_options_t UVOptions_;
- cm::uv_process_ptr UVProcess_;
- PipeT UVPipeOut_;
- PipeT UVPipeErr_;
- };
-
public:
// -- Constructors
cmQtAutoGenerator();
diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx
deleted file mode 100644
index ec1a1aa..0000000
--- a/Source/cmQtAutoGeneratorMocUic.cxx
+++ /dev/null
@@ -1,2052 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
-#include "cmQtAutoGeneratorMocUic.h"
-
-#include <algorithm>
-#include <array>
-#include <cstddef>
-#include <list>
-#include <memory>
-#include <set>
-#include <sstream>
-#include <utility>
-
-#include "cmAlgorithms.h"
-#include "cmCryptoHash.h"
-#include "cmMakefile.h"
-#include "cmQtAutoGen.h"
-#include "cmSystemTools.h"
-#include "cmake.h"
-
-#if defined(__APPLE__)
-# include <unistd.h>
-#endif
-
-// -- Class methods
-
-std::string cmQtAutoGeneratorMocUic::BaseSettingsT::AbsoluteBuildPath(
- std::string const& relativePath) const
-{
- return FileSys->CollapseFullPath(relativePath, AutogenBuildDir);
-}
-
-/**
- * @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;
-}
-
-bool cmQtAutoGeneratorMocUic::MocSettingsT::skipped(
- std::string const& fileName) const
-{
- 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;
-}
-
-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->GetRealPath(testPath);
- }
- }
- // Search in include directories
- for (std::string const& path : IncludePaths) {
- std::string fullPath = path;
- fullPath.push_back('/');
- fullPath += includeString;
- if (FileSys->FileExists(fullPath)) {
- return FileSys->GetRealPath(fullPath);
- }
- }
- // Return empty string
- return std::string();
-}
-
-void cmQtAutoGeneratorMocUic::MocSettingsT::FindDependencies(
- std::string const& content, std::set<std::string>& depends) const
-{
- if (!DependFilters.empty() && !content.empty()) {
- for (KeyExpT const& filter : DependFilters) {
- // Run a simple find string check
- if (content.find(filter.Key) != std::string::npos) {
- // Run the expensive regular expression check loop
- const char* contentChars = content.c_str();
- cmsys::RegularExpressionMatch match;
- while (filter.Exp.find(contentChars, match)) {
- {
- std::string dep = match.match(1);
- if (!dep.empty()) {
- depends.emplace(std::move(dep));
- }
- }
- contentChars += match.end();
- }
- }
- }
- }
-}
-
-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 = wrk.FileSys().SubDirPrefix(FileName);
- meta.FileBase =
- wrk.FileSys().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(GenT::GEN, FileName, "The source file is empty");
- }
- } else {
- wrk.LogFileError(GenT::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(2);
- std::string incDir(wrk.FileSys().SubDirPrefix(incString));
- std::string incBase =
- wrk.FileSys().GetFilenameWithoutLastExtension(incString);
- if (cmHasLiteralPrefix(incBase, "moc_")) {
- // moc_<BASE>.cxx
- // Remove the moc_ part from the base name
- mocIncsUsc.emplace_back(MocInclude{
- std::move(incString), std::move(incDir), incBase.substr(4) });
- } else {
- // <BASE>.moc
- mocIncsDot.emplace_back(MocInclude{
- std::move(incString), std::move(incDir), std::move(incBase) });
- }
- // Forward content pointer
- contentChars += match.end();
- }
- }
- }
-
- // 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(GenT::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(GenT::MOC, FileName, emsg);
- } else {
- std::string emsg = "The file includes the moc file ";
- emsg += Quoted(mocInc.Inc);
- emsg += " instead of ";
- emsg += Quoted("moc_" + mocInc.Base + ".cpp");
- emsg += ".\nRunning moc on\n ";
- emsg += Quoted(header);
- emsg += "!\nBetter include ";
- emsg += Quoted("moc_" + mocInc.Base + ".cpp");
- emsg += " for compatibility with strict mode.\n"
- "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
- wrk.LogFileWarning(GenT::MOC, FileName, emsg);
- }
- }
- } else {
- {
- std::string emsg = "The file includes the moc file ";
- emsg += Quoted(mocInc.Inc);
- emsg += ", which seems to be the moc file from a different "
- "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a "
- "matching header ";
- emsg += Quoted(MocStringHeaders(wrk, mocInc.Base));
- emsg += " could not be found.";
- wrk.LogFileError(GenT::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(GenT::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(GenT::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(GenT::MOC, FileName, emsg);
- }
- // Add own source job
- jobs.emplace_back(
- JobPre{ true, false, FileName, uscJobPre.IncludeString });
- } else {
- // Otherwise always error out since it will not compile.
- {
- std::string emsg = "The file contains a ";
- emsg += ownMacro;
- emsg += " macro, but does not include ";
- emsg += Quoted(meta.FileBase + ".moc");
- emsg += "!\nConsider to\n - add #include \"";
- emsg += meta.FileBase;
- emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
- wrk.LogFileError(GenT::MOC, FileName, emsg);
- }
- return false;
- }
- }
-
- // Convert pre jobs to actual jobs
- for (JobPre& jobPre : jobs) {
- JobHandleT jobHandle = cm::make_unique<JobMocT>(
- std::move(jobPre.SourceFile), FileName, std::move(jobPre.IncludeString));
- if (jobPre.self) {
- // Read dependencies from this source
- static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content);
- }
- 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 = cm::make_unique<JobMocT>(
- std::string(FileName), std::string(), std::string());
- // Read dependencies from this source
- static_cast<JobMocT&>(*jobHandle).FindDependencies(wrk, meta.Content);
- success = wrk.Gen().ParallelJobPushMoc(jobHandle);
- }
- 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().GetRealPath(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(2))) {
- 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 = cm::make_unique<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 =
- wrk.FileSys().GetFilenameWithoutLastExtension(includeString).substr(3);
- searchFile += ".ui";
- // Collect search paths list
- std::deque<std::string> testFiles;
- {
- std::string const searchPath = wrk.FileSys().SubDirPrefix(includeString);
-
- std::string 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().GetRealPath(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(GenT::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(GenT::MOC, reason);
- }
- generate = true;
- } else if (wrk.Moc().SettingsChanged) {
- if (wrk.Log().Verbose()) {
- std::string reason = "Generating ";
- reason += Quoted(wrk.Moc().PredefsFileRel);
- reason += " because the settings changed.";
- wrk.LogInfo(GenT::MOC, reason);
- }
- generate = true;
- }
- if (generate) {
- ProcessResultT result;
- {
- // Compose command
- std::vector<std::string> cmd = wrk.Moc().PredefsCmd;
- // Add includes
- cmd.insert(cmd.end(), wrk.Moc().Includes.begin(),
- wrk.Moc().Includes.end());
- // Add definitions
- for (std::string const& def : wrk.Moc().Definitions) {
- cmd.push_back("-D" + def);
- }
- // Execute command
- if (!wrk.RunProcess(GenT::MOC, result, cmd)) {
- std::string emsg = "The content generation command for ";
- emsg += Quoted(wrk.Moc().PredefsFileRel);
- emsg += " failed.\n";
- emsg += result.ErrorMessage;
- wrk.LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
- }
- }
-
- // (Re)write predefs file only on demand
- if (!result.error()) {
- if (!fileExists ||
- wrk.FileSys().FileDiffers(wrk.Moc().PredefsFileAbs, result.StdOut)) {
- std::string error;
- if (wrk.FileSys().FileWrite(wrk.Moc().PredefsFileAbs, result.StdOut,
- &error)) {
- // Success
- } else {
- std::string emsg = "Writing ";
- emsg += Quoted(wrk.Moc().PredefsFileRel);
- emsg += " failed. ";
- emsg += error;
- wrk.LogFileError(GenT::MOC, wrk.Moc().PredefsFileAbs, emsg);
- }
- } else {
- // Touch to update the time stamp
- if (wrk.Log().Verbose()) {
- std::string msg = "Touching ";
- msg += Quoted(wrk.Moc().PredefsFileRel);
- msg += ".";
- wrk.LogInfo(GenT::MOC, msg);
- }
- wrk.FileSys().Touch(wrk.Moc().PredefsFileAbs);
- }
- }
- }
-}
-
-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().AutogenIncludeDir;
- BuildFile += '/';
- BuildFile += IncludeString;
- } else {
- // Relative build path
- std::string relPath = wrk.FileSys().GetFilePathChecksum(SourceFile);
- relPath += "/moc_";
- relPath += wrk.FileSys().GetFilenameWithoutLastExtension(SourceFile);
-
- // Register relative file path with duplication check
- relPath = wrk.Gen().ParallelMocAutoRegister(relPath);
-
- // Absolute build path
- if (wrk.Base().MultiConfig) {
- BuildFile = wrk.Base().AutogenIncludeDir;
- BuildFile += '/';
- BuildFile += relPath;
- } else {
- BuildFile = wrk.Base().AbsoluteBuildPath(relPath);
- }
- }
-
- 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(GenT::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(GenT::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(GenT::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(GenT::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(GenT::MOC, error);
- return false;
- }
- }
- if (isOlder) {
- if (verbose) {
- std::string reason = "Generating ";
- reason += Quoted(BuildFile);
- reason += " because it's older than its source file ";
- reason += Quoted(SourceFile);
- wrk.LogInfo(GenT::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(GenT::MOC, emsg);
- return false;
- }
- }
- FindDependencies(wrk, content);
- }
- // Check dependency timestamps
- std::string error;
- std::string sourceDir = wrk.FileSys().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(GenT::MOC, reason);
- }
- return true;
- }
- if (!error.empty()) {
- wrk.LogError(GenT::MOC, error);
- return false;
- }
- } else {
- std::string message = "Could not find dependency file ";
- message += Quoted(depFileRel);
- wrk.LogFileWarning(GenT::MOC, SourceFile, message);
- }
- }
- }
-
- return false;
-}
-
-void cmQtAutoGeneratorMocUic::JobMocT::GenerateMoc(WorkerT& wrk)
-{
- // Make sure the parent directory exists
- if (!wrk.FileSys().MakeParentDirectory(BuildFile)) {
- wrk.LogFileError(GenT::MOC, BuildFile,
- "Could not create parent directory.");
- return;
- }
- {
- // 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.emplace_back("--include");
- cmd.push_back(wrk.Moc().PredefsFileAbs);
- }
- cmd.emplace_back("-o");
- cmd.push_back(BuildFile);
- cmd.push_back(SourceFile);
-
- // Execute moc command
- ProcessResultT result;
- if (wrk.RunProcess(GenT::MOC, result, cmd)) {
- // Moc command success
- // Print moc output
- if (!result.StdOut.empty()) {
- wrk.LogInfo(GenT::MOC, result.StdOut);
- }
- // Notify the generator that a not included file changed (on demand)
- if (IncludeString.empty()) {
- 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(GenT::MOC, emsg, cmd, result.StdOut);
- }
- wrk.FileSys().FileRemove(BuildFile);
- }
- }
-}
-
-void cmQtAutoGeneratorMocUic::JobUicT::Process(WorkerT& wrk)
-{
- // Compute build file name
- BuildFile = wrk.Base().AutogenIncludeDir;
- BuildFile += '/';
- 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(GenT::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(GenT::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(GenT::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(GenT::UIC, reason);
- }
- return true;
- }
- }
-
- return false;
-}
-
-void cmQtAutoGeneratorMocUic::JobUicT::GenerateUic(WorkerT& wrk)
-{
- // Make sure the parent directory exists
- if (!wrk.FileSys().MakeParentDirectory(BuildFile)) {
- wrk.LogFileError(GenT::UIC, BuildFile,
- "Could not create parent directory.");
- return;
- }
- {
- // 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.emplace_back("-o");
- cmd.push_back(BuildFile);
- cmd.push_back(SourceFile);
-
- ProcessResultT result;
- if (wrk.RunProcess(GenT::UIC, result, cmd)) {
- // Uic command success
- // Print uic output
- if (!result.StdOut.empty()) {
- wrk.LogInfo(GenT::UIC, result.StdOut);
- }
- } else {
- // Uic command failed
- {
- std::string emsg = "The uic process failed to compile\n ";
- emsg += Quoted(SourceFile);
- emsg += "\ninto\n ";
- emsg += Quoted(BuildFile);
- emsg += "\nincluded by\n ";
- emsg += Quoted(IncluderFile);
- emsg += ".\n";
- emsg += result.ErrorMessage;
- wrk.LogCommandError(GenT::UIC, emsg, cmd, result.StdOut);
- }
- wrk.FileSys().FileRemove(BuildFile);
- }
- }
-}
-
-cmQtAutoGeneratorMocUic::WorkerT::WorkerT(cmQtAutoGeneratorMocUic* gen,
- uv_loop_t* uvLoop)
- : Gen_(gen)
-{
- // Initialize uv asynchronous callback for process starting
- ProcessRequest_.init(*uvLoop, &WorkerT::UVProcessStart, this);
- // Start thread
- Thread_ = std::thread(&WorkerT::Loop, this);
-}
-
-cmQtAutoGeneratorMocUic::WorkerT::~WorkerT()
-{
- // Join thread
- if (Thread_.joinable()) {
- Thread_.join();
- }
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::LogInfo(
- GenT genType, std::string const& message) const
-{
- Log().Info(genType, message);
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::LogWarning(
- GenT genType, std::string const& message) const
-{
- Log().Warning(genType, message);
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::LogFileWarning(
- GenT genType, std::string const& filename, std::string const& message) const
-{
- Log().WarningFile(genType, filename, message);
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::LogError(
- GenT genType, std::string const& message) const
-{
- Gen().ParallelRegisterJobError();
- Log().Error(genType, message);
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::LogFileError(
- GenT genType, std::string const& filename, std::string const& message) const
-{
- Gen().ParallelRegisterJobError();
- Log().ErrorFile(genType, filename, message);
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::LogCommandError(
- GenT genType, std::string const& message,
- std::vector<std::string> const& command, std::string const& output) const
-{
- Gen().ParallelRegisterJobError();
- Log().ErrorCommand(genType, message, command, output);
-}
-
-bool cmQtAutoGeneratorMocUic::WorkerT::RunProcess(
- GenT genType, ProcessResultT& result,
- std::vector<std::string> const& command)
-{
- if (command.empty()) {
- return false;
- }
-
- // Create process instance
- {
- std::lock_guard<std::mutex> lock(ProcessMutex_);
- Process_ = cm::make_unique<ReadOnlyProcessT>();
- Process_->setup(&result, true, command, Gen().Base().AutogenBuildDir);
- }
-
- // Send asynchronous process start request to libuv loop
- ProcessRequest_.send();
-
- // Log command
- if (this->Log().Verbose()) {
- std::string msg = "Running command:\n";
- msg += QuotedCommand(command);
- msg += '\n';
- this->LogInfo(genType, msg);
- }
-
- // Wait until the process has been finished and destroyed
- {
- std::unique_lock<std::mutex> ulock(ProcessMutex_);
- while (Process_) {
- ProcessCondition_.wait(ulock);
- }
- }
- return !result.error();
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::Loop()
-{
- while (true) {
- Gen().WorkerSwapJob(JobHandle_);
- if (JobHandle_) {
- JobHandle_->Process(*this);
- } else {
- break;
- }
- }
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::UVProcessStart(uv_async_t* handle)
-{
- auto& wrk = *reinterpret_cast<WorkerT*>(handle->data);
- {
- std::lock_guard<std::mutex> lock(wrk.ProcessMutex_);
- if (wrk.Process_ && !wrk.Process_->IsStarted()) {
- wrk.Process_->start(handle->loop, [&wrk] { wrk.UVProcessFinished(); });
- }
- }
-
- if (!wrk.Process_->IsStarted()) {
- wrk.UVProcessFinished();
- }
-}
-
-void cmQtAutoGeneratorMocUic::WorkerT::UVProcessFinished()
-{
- {
- std::lock_guard<std::mutex> lock(ProcessMutex_);
- if (Process_ && (Process_->IsFinished() || !Process_->IsStarted())) {
- Process_.reset();
- }
- }
- // Notify idling thread
- ProcessCondition_.notify_one();
-}
-
-cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic()
- : Base_(&FileSys())
- , Moc_(&FileSys())
-{
- // Precompile regular expressions
- Moc_.RegExpInclude.compile(
- "(^|\n)[ \t]*#[ \t]*include[ \t]+"
- "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
- Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
- "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
-
- // Initialize libuv loop
- uv_disable_stdio_inheritance();
-#ifdef CMAKE_UV_SIGNAL_HACK
- UVHackRAII_ = cm::make_unique<cmUVSignalHackRAII>();
-#endif
- UVLoop_ = cm::make_unique<uv_loop_t>();
- uv_loop_init(UVLoop());
-
- // Initialize libuv asynchronous iteration request
- UVRequest().init(*UVLoop(), &cmQtAutoGeneratorMocUic::UVPollStage, this);
-}
-
-cmQtAutoGeneratorMocUic::~cmQtAutoGeneratorMocUic()
-{
- // Close libuv loop
- uv_loop_close(UVLoop());
-}
-
-bool cmQtAutoGeneratorMocUic::Init(cmMakefile* makefile)
-{
- // -- Meta
- Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
-
- // Utility lambdas
- auto InfoGet = [makefile](const char* key) {
- return makefile->GetSafeDefinition(key);
- };
- auto InfoGetBool = [makefile](const char* key) {
- return makefile->IsOn(key);
- };
- auto InfoGetList = [makefile](const char* key) -> std::vector<std::string> {
- std::vector<std::string> list;
- cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
- return list;
- };
- auto InfoGetLists =
- [makefile](const char* key) -> std::vector<std::vector<std::string>> {
- std::vector<std::vector<std::string>> lists;
- {
- std::string const value = makefile->GetSafeDefinition(key);
- std::string::size_type pos = 0;
- while (pos < value.size()) {
- 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
- if (length >= 2) {
- std::string::const_iterator itBeg = value.begin() + (pos + 1);
- std::string::const_iterator itEnd = itBeg + (length - 2);
- {
- std::string subValue(itBeg, itEnd);
- std::vector<std::string> list;
- cmSystemTools::ExpandListArgument(subValue, list);
- lists.push_back(std::move(list));
- }
- }
- pos += length;
- pos += ListSep.size();
- }
- }
- return lists;
- };
- auto InfoGetConfig = [makefile, this](const char* key) -> std::string {
- const char* valueConf = nullptr;
- {
- std::string keyConf = key;
- keyConf += '_';
- keyConf += InfoConfig();
- valueConf = makefile->GetDefinition(keyConf);
- }
- if (valueConf == nullptr) {
- return makefile->GetSafeDefinition(key);
- }
- return std::string(valueConf);
- };
- auto InfoGetConfigList =
- [&InfoGetConfig](const char* key) -> std::vector<std::string> {
- std::vector<std::string> list;
- cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
- return list;
- };
-
- // -- Read info file
- if (!makefile->ReadListFile(InfoFile())) {
- Log().ErrorFile(GenT::GEN, InfoFile(), "File processing failed");
- return false;
- }
-
- // -- Meta
- Log().RaiseVerbosity(InfoGet("AM_VERBOSITY"));
- Base_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG");
- {
- unsigned long num = Base_.NumThreads;
- if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) {
- num = std::max<unsigned long>(num, 1);
- num = std::min<unsigned long>(num, ParallelMax);
- Base_.NumThreads = static_cast<unsigned int>(num);
- }
- }
-
- // - Files and directories
- Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
- Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
- Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
- Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
- Base_.IncludeProjectDirsBefore =
- InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
- Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
- if (Base_.AutogenBuildDir.empty()) {
- Log().ErrorFile(GenT::GEN, InfoFile(), "Autogen build directory missing");
- return false;
- }
- // include directory
- Base_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR");
- if (Base_.AutogenIncludeDir.empty()) {
- Log().ErrorFile(GenT::GEN, InfoFile(),
- "Autogen include directory missing");
- return false;
- }
-
- // - Files
- SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
- if (SettingsFile_.empty()) {
- Log().ErrorFile(GenT::GEN, InfoFile(), "Settings file name missing");
- return false;
- }
-
- // - Qt environment
- {
- unsigned long qtv = Base_.QtVersionMajor;
- if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(),
- &qtv)) {
- Base_.QtVersionMajor = static_cast<unsigned int>(qtv);
- }
- }
-
- // - Moc
- Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
- Moc_.Enabled = !Moc().Executable.empty();
- if (Moc().Enabled) {
- for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) {
- Moc_.SkipList.insert(std::move(sfl));
- }
- Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
- Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
- Moc_.Options = InfoGetList("AM_MOC_OPTIONS");
- Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
- for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) {
- Moc_.MacroFilters.emplace_back(
- item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
- }
- {
- auto pushFilter = [this](std::string const& key, std::string const& exp,
- std::string& error) {
- if (!key.empty()) {
- if (!exp.empty()) {
- Moc_.DependFilters.emplace_back();
- KeyExpT& filter(Moc_.DependFilters.back());
- if (filter.Exp.compile(exp)) {
- filter.Key = key;
- } else {
- error = "Regular expression compiling failed";
- }
- } else {
- error = "Regular expression is empty";
- }
- } else {
- error = "Key is empty";
- }
- 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
- {
- std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS");
- if ((flts.size() % 2) == 0) {
- for (std::vector<std::string>::iterator itC = flts.begin(),
- itE = flts.end();
- itC != itE; itC += 2) {
- pushFilter(*itC, *(itC + 1), error);
- if (!error.empty()) {
- break;
- }
- }
- } else {
- Log().ErrorFile(
- GenT::MOC, InfoFile(),
- "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
- return false;
- }
- }
- if (!error.empty()) {
- Log().ErrorFile(GenT::MOC, InfoFile(), error);
- return false;
- }
- }
- Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
- // Install moc predefs job
- if (!Moc().PredefsCmd.empty()) {
- JobQueues_.MocPredefs.emplace_back(cm::make_unique<JobMocPredefsT>());
- }
- }
-
- // - Uic
- Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
- Uic_.Enabled = !Uic().Executable.empty();
- if (Uic().Enabled) {
- for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) {
- Uic_.SkipList.insert(std::move(sfl));
- }
- 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() << "/"
- << options.size() << ")";
- Log().ErrorFile(GenT::UIC, InfoFile(), ost.str());
- return false;
- }
- auto fitEnd = sources.cend();
- auto fit = sources.begin();
- auto oit = options.begin();
- while (fit != fitEnd) {
- Uic_.Options[*fit] = std::move(*oit);
- ++fit;
- ++oit;
- }
- }
- }
-
- // - Headers and sources
- {
- auto addHeader = [this](std::string&& hdr, bool moc, bool uic) {
- this->JobQueues_.Headers.emplace_back(
- cm::make_unique<JobParseT>(std::move(hdr), moc, uic, true));
- };
- auto addSource = [this](std::string&& src, bool moc, bool uic) {
- this->JobQueues_.Sources.emplace_back(
- cm::make_unique<JobParseT>(std::move(src), moc, uic, false));
- };
-
- // Add headers
- for (std::string& hdr : InfoGetList("AM_HEADERS")) {
- addHeader(std::move(hdr), true, true);
- }
- if (Moc().Enabled) {
- for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) {
- addHeader(std::move(hdr), true, false);
- }
- }
- if (Uic().Enabled) {
- for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) {
- addHeader(std::move(hdr), false, true);
- }
- }
-
- // Add sources
- for (std::string& src : InfoGetList("AM_SOURCES")) {
- addSource(std::move(src), true, true);
- }
- if (Moc().Enabled) {
- for (std::string& src : InfoGetList("AM_MOC_SOURCES")) {
- addSource(std::move(src), true, false);
- }
- }
- if (Uic().Enabled) {
- for (std::string& src : InfoGetList("AM_UIC_SOURCES")) {
- addSource(std::move(src), false, true);
- }
- }
- }
-
- // Init derived information
- // ------------------------
-
- // Init file path checksum generator
- FileSys().setupFilePathChecksum(
- Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir,
- Base().ProjectBinaryDir);
-
- // Moc variables
- if (Moc().Enabled) {
- // Mocs compilation file
- Moc_.CompFileAbs = Base().AbsoluteBuildPath("mocs_compilation.cpp");
-
- // Moc predefs file
- if (!Moc_.PredefsCmd.empty()) {
- Moc_.PredefsFileRel = "moc_predefs";
- if (Base_.MultiConfig) {
- Moc_.PredefsFileRel += '_';
- Moc_.PredefsFileRel += InfoConfig();
- }
- Moc_.PredefsFileRel += ".h";
- Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel);
- }
-
- // Sort include directories on demand
- if (Base().IncludeProjectDirsBefore) {
- // Move strings to temporary list
- std::list<std::string> includes;
- includes.insert(includes.end(), Moc().IncludePaths.begin(),
- Moc().IncludePaths.end());
- Moc_.IncludePaths.clear();
- Moc_.IncludePaths.reserve(includes.size());
- // Append project directories only
- {
- std::array<std::string const*, 2> const movePaths = {
- { &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())) {
- Moc_.IncludePaths.push_back(path);
- it = includes.erase(it);
- } else {
- ++it;
- }
- }
- }
- }
- // Append remaining directories
- Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(),
- includes.end());
- }
- // Compose moc includes list
- {
- std::set<std::string> frameworkPaths;
- for (std::string const& path : Moc().IncludePaths) {
- Moc_.Includes.push_back("-I" + path);
- // Extract framework path
- if (cmHasLiteralSuffix(path, ".framework/Headers")) {
- // Go up twice to get to the framework root
- std::vector<std::string> pathComponents;
- FileSys().SplitPath(path, pathComponents);
- std::string frameworkPath = FileSys().JoinPath(
- pathComponents.begin(), pathComponents.end() - 2);
- frameworkPaths.insert(frameworkPath);
- }
- }
- // Append framework includes
- for (std::string const& path : frameworkPaths) {
- Moc_.Includes.emplace_back("-F");
- Moc_.Includes.push_back(path);
- }
- }
- // Setup single list with all options
- {
- // Add includes
- Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(),
- Moc().Includes.end());
- // Add definitions
- for (std::string const& def : Moc().Definitions) {
- Moc_.AllOptions.push_back("-D" + def);
- }
- // Add options
- Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(),
- Moc().Options.end());
- }
- }
-
- return true;
-}
-
-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 (Moc_.Enabled) {
- std::string str;
- str += Moc().Executable;
- str += sep;
- str += cmJoin(Moc().AllOptions, ";");
- str += sep;
- str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE";
- str += sep;
- str += cmJoin(Moc().PredefsCmd, ";");
- str += sep;
- SettingsStringMoc_ = crypt.HashString(str);
- }
- if (Uic().Enabled) {
- std::string str;
- str += Uic().Executable;
- str += sep;
- str += cmJoin(Uic().TargetOptions, ";");
- for (const auto& item : Uic().Options) {
- str += sep;
- str += item.first;
- str += sep;
- str += cmJoin(item.second, ";");
- }
- str += sep;
- SettingsStringUic_ = crypt.HashString(str);
- }
- }
-
- // 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 (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;
- }
- }
- }
-}
-
-void cmQtAutoGeneratorMocUic::SettingsFileWrite()
-{
- std::lock_guard<std::mutex> jobsLock(JobsMutex_);
- // Only write if any setting changed
- if (!JobError_ && (Moc().SettingsChanged || Uic().SettingsChanged)) {
- if (Log().Verbose()) {
- Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_));
- }
- // Compose settings file content
- std::string content;
- {
- auto SettingAppend = [&content](const char* key,
- std::string const& value) {
- if (!value.empty()) {
- content += key;
- content += ':';
- content += value;
- content += '\n';
- }
- };
- SettingAppend("moc", SettingsStringMoc_);
- SettingAppend("uic", SettingsStringUic_);
- }
- // Write settings file
- std::string error;
- if (!FileSys().FileWrite(SettingsFile_, content, &error)) {
- Log().ErrorFile(GenT::GEN, SettingsFile_,
- "Settings file writing failed. " + error);
- // Remove old settings file to trigger a full rebuild on the next run
- FileSys().FileRemove(SettingsFile_);
- RegisterJobError();
- }
- }
-}
-
-void cmQtAutoGeneratorMocUic::CreateDirectories()
-{
- // Create AUTOGEN include directory
- if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) {
- Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir,
- "Could not create directory.");
- RegisterJobError();
- }
-}
-
-bool cmQtAutoGeneratorMocUic::ThreadsStartJobs(JobQueueT& queue)
-{
- bool done = false;
- std::size_t queueSize = queue.size();
-
- // Change the active queue
- {
- std::lock_guard<std::mutex> jobsLock(JobsMutex_);
- // Check if there are still unfinished jobs from the previous queue
- if (JobsRemain_ == 0) {
- if (!JobThreadsAbort_) {
- JobQueue_.swap(queue);
- JobsRemain_ = queueSize;
- } else {
- // Abort requested
- queue.clear();
- queueSize = 0;
- }
- done = true;
- }
- }
-
- if (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 {
- // Notify threads
- if (queueSize == 1) {
- JobsConditionRead_.notify_one();
- } else {
- JobsConditionRead_.notify_all();
- }
- }
- }
-
- return done;
-}
-
-void cmQtAutoGeneratorMocUic::ThreadsStop()
-{
- if (!Workers_.empty()) {
- // Clear all jobs
- {
- std::lock_guard<std::mutex> jobsLock(JobsMutex_);
- JobThreadsAbort_ = true;
- JobsRemain_ -= JobQueue_.size();
- JobQueue_.clear();
-
- JobQueues_.Sources.clear();
- JobQueues_.Headers.clear();
- JobQueues_.MocPredefs.clear();
- JobQueues_.Moc.clear();
- JobQueues_.Uic.clear();
- }
- // Wake threads
- JobsConditionRead_.notify_all();
- // Join and clear threads
- Workers_.clear();
- }
-}
-
-bool cmQtAutoGeneratorMocUic::ThreadsJobsDone()
-{
- std::lock_guard<std::mutex> jobsLock(JobsMutex_);
- return (JobsRemain_ == 0);
-}
-
-void cmQtAutoGeneratorMocUic::WorkerSwapJob(JobHandleT& jobHandle)
-{
- bool const jobProcessed(jobHandle);
- if (jobProcessed) {
- jobHandle.reset();
- }
- {
- 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();
- }
- }
- // Wait for new jobs
- while (!JobThreadsAbort_ && JobQueue_.empty()) {
- JobsConditionRead_.wait(jobsLock);
- }
- // Try to pick up a new job handle
- if (!JobThreadsAbort_ && !JobQueue_.empty()) {
- jobHandle = std::move(JobQueue_.front());
- JobQueue_.pop_front();
- }
- }
-}
-
-void cmQtAutoGeneratorMocUic::ParallelRegisterJobError()
-{
- std::lock_guard<std::mutex> jobsLock(JobsMutex_);
- RegisterJobError();
-}
-
-// Private method that requires cmQtAutoGeneratorMocUic::JobsMutex_ to be
-// locked
-void cmQtAutoGeneratorMocUic::RegisterJobError()
-{
- JobError_ = true;
- if (!JobThreadsAbort_) {
- JobThreadsAbort_ = true;
- // Clear remaining jobs
- if (JobsRemain_ != 0) {
- JobsRemain_ -= JobQueue_.size();
- JobQueue_.clear();
- }
- }
-}
-
-bool cmQtAutoGeneratorMocUic::ParallelJobPushMoc(JobHandleT& jobHandle)
-{
- std::lock_guard<std::mutex> jobsLock(JobsMutex_);
- if (!JobThreadsAbort_) {
- bool pushJobHandle = true;
- // Do additional tests if this is an included moc job
- const JobMocT& mocJob(static_cast<JobMocT&>(*jobHandle));
- if (!mocJob.IncludeString.empty()) {
- // Register included moc file and look for collisions
- MocIncludedFiles_.emplace(mocJob.SourceFile);
- if (!MocIncludedStrings_.emplace(mocJob.IncludeString).second) {
- // Another source file includes the same moc file!
- for (const JobHandleT& otherHandle : JobQueues_.Moc) {
- const JobMocT& otherJob(static_cast<JobMocT&>(*otherHandle));
- if (otherJob.IncludeString == mocJob.IncludeString) {
- // Check if the same moc file would be generated from different
- // source files which is an error.
- if (otherJob.SourceFile != mocJob.SourceFile) {
- // Include string collision
- std::string error = "The two source files\n ";
- error += Quoted(mocJob.IncluderFile);
- error += " and\n ";
- error += Quoted(otherJob.IncluderFile);
- error += "\ncontain the same moc include string ";
- error += Quoted(mocJob.IncludeString);
- error += "\nbut the moc file would be generated from different "
- "source files\n ";
- error += Quoted(mocJob.SourceFile);
- error += " and\n ";
- error += Quoted(otherJob.SourceFile);
- error += ".\nConsider to\n"
- "- not include the \"moc_<NAME>.cpp\" file\n"
- "- add a directory prefix to a \"<NAME>.moc\" include "
- "(e.g \"sub/<NAME>.moc\")\n"
- "- rename the source file(s)\n";
- Log().Error(GenT::MOC, error);
- RegisterJobError();
- }
- // Do not push this job in since the included moc file already
- // gets generated by an other job.
- pushJobHandle = false;
- break;
- }
- }
- }
- }
- // Push job on demand
- if (pushJobHandle) {
- JobQueues_.Moc.emplace_back(std::move(jobHandle));
- }
- }
- return !JobError_;
-}
-
-bool cmQtAutoGeneratorMocUic::ParallelJobPushUic(JobHandleT& jobHandle)
-{
- std::lock_guard<std::mutex> jobsLock(JobsMutex_);
- if (!JobThreadsAbort_) {
- bool pushJobHandle = true;
- // Look for include collisions.
- const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle));
- for (const JobHandleT& otherHandle : JobQueues_.Uic) {
- const JobUicT& otherJob(static_cast<JobUicT&>(*otherHandle));
- if (otherJob.IncludeString == uicJob.IncludeString) {
- // Check if the same uic file would be generated from different
- // source files which would be an error.
- if (otherJob.SourceFile != uicJob.SourceFile) {
- // Include string collision
- std::string error = "The two source files\n ";
- error += Quoted(uicJob.IncluderFile);
- error += " and\n ";
- error += Quoted(otherJob.IncluderFile);
- error += "\ncontain the same uic include string ";
- error += Quoted(uicJob.IncludeString);
- error += "\nbut the uic file would be generated from different "
- "source files\n ";
- error += Quoted(uicJob.SourceFile);
- error += " and\n ";
- error += Quoted(otherJob.SourceFile);
- error +=
- ".\nConsider to\n"
- "- add a directory prefix to a \"ui_<NAME>.h\" include "
- "(e.g \"sub/ui_<NAME>.h\")\n"
- "- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
- "include(s)\n";
- Log().Error(GenT::UIC, error);
- RegisterJobError();
- }
- // Do not push this job in since the uic file already
- // gets generated by an other job.
- pushJobHandle = false;
- break;
- }
- }
- if (pushJobHandle) {
- JobQueues_.Uic.emplace_back(std::move(jobHandle));
- }
- }
- return !JobError_;
-}
-
-bool cmQtAutoGeneratorMocUic::ParallelMocIncluded(
- std::string const& sourceFile)
-{
- std::lock_guard<std::mutex> mocLock(JobsMutex_);
- return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end());
-}
-
-std::string cmQtAutoGeneratorMocUic::ParallelMocAutoRegister(
- std::string const& baseName)
-{
- std::string res;
- {
- std::lock_guard<std::mutex> mocLock(JobsMutex_);
- res = baseName;
- res += ".cpp";
- if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
- MocAutoFiles_.emplace(res);
- } else {
- // Append number suffix to the file name
- for (unsigned int ii = 2; ii != 1024; ++ii) {
- res = baseName;
- res += '_';
- res += std::to_string(ii);
- res += ".cpp";
- if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
- MocAutoFiles_.emplace(res);
- break;
- }
- }
- }
- }
- return res;
-}
-
-void cmQtAutoGeneratorMocUic::ParallelMocAutoUpdated()
-{
- std::lock_guard<std::mutex> mocLock(JobsMutex_);
- MocAutoFileUpdated_ = true;
-}
-
-void cmQtAutoGeneratorMocUic::MocGenerateCompilation()
-{
- std::lock_guard<std::mutex> mocLock(JobsMutex_);
- if (!JobError_ && Moc().Enabled) {
- // Write mocs compilation build file
- {
- // Compose mocs compilation file content
- std::string content =
- "// This file is autogenerated. Changes will be overwritten.\n";
- if (MocAutoFiles_.empty()) {
- // Placeholder content
- content += "// No files found that require moc or the moc files are "
- "included\n";
- content += "enum some_compilers { need_more_than_nothing };\n";
- } else {
- // Valid content
- char const sbeg = Base().MultiConfig ? '<' : '"';
- char const send = Base().MultiConfig ? '>' : '"';
- for (std::string const& mocfile : MocAutoFiles_) {
- content += "#include ";
- content += sbeg;
- content += mocfile;
- content += send;
- content += '\n';
- }
- }
-
- std::string const& compAbs = Moc().CompFileAbs;
- if (FileSys().FileDiffers(compAbs, content)) {
- // Actually write mocs compilation file
- if (Log().Verbose()) {
- Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs);
- }
- std::string error;
- if (!FileSys().FileWrite(compAbs, content, &error)) {
- Log().ErrorFile(GenT::MOC, compAbs,
- "mocs compilation file writing failed. " + error);
- RegisterJobError();
- return;
- }
- } else if (MocAutoFileUpdated_) {
- // Only touch mocs compilation file
- if (Log().Verbose()) {
- Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs);
- }
- FileSys().Touch(compAbs);
- }
- }
- // Write mocs compilation wrapper file
- if (Base().MultiConfig) {
- }
- }
-}
diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoGeneratorMocUic.h
deleted file mode 100644
index 27d73a7..0000000
--- a/Source/cmQtAutoGeneratorMocUic.h
+++ /dev/null
@@ -1,459 +0,0 @@
-/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
-#ifndef cmQtAutoGeneratorMocUic_h
-#define cmQtAutoGeneratorMocUic_h
-
-#include "cmConfigure.h" // IWYU pragma: keep
-
-#include "cmQtAutoGen.h"
-#include "cmQtAutoGenerator.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
-#include "cm_uv.h"
-#include "cmsys/RegularExpression.hxx"
-
-#include <condition_variable>
-#include <cstddef>
-#include <deque>
-#include <map>
-#include <memory> // IWYU pragma: keep
-#include <mutex>
-#include <set>
-#include <string>
-#include <thread>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-class cmMakefile;
-
-// @brief AUTOMOC and AUTOUIC generator
-class cmQtAutoGeneratorMocUic : public cmQtAutoGenerator
-{
-public:
- cmQtAutoGeneratorMocUic();
- ~cmQtAutoGeneratorMocUic() override;
-
- cmQtAutoGeneratorMocUic(cmQtAutoGeneratorMocUic const&) = delete;
- cmQtAutoGeneratorMocUic& operator=(cmQtAutoGeneratorMocUic const&) = delete;
-
-public:
- // -- Types
- class WorkerT;
-
- /// @brief Search key plus regular expression pair
- ///
- struct KeyExpT
- {
- KeyExpT() = default;
-
- KeyExpT(const char* key, const char* exp)
- : Key(key)
- , Exp(exp)
- {
- }
-
- KeyExpT(std::string key, std::string const& exp)
- : Key(std::move(key))
- , Exp(exp)
- {
- }
-
- std::string Key;
- cmsys::RegularExpression Exp;
- };
-
- /// @brief Common settings
- ///
- class BaseSettingsT
- {
- public:
- // -- Volatile methods
- BaseSettingsT(FileSystem* fileSystem)
- : MultiConfig(false)
- , IncludeProjectDirsBefore(false)
- , QtVersionMajor(4)
- , NumThreads(1)
- , FileSys(fileSystem)
- {
- }
-
- BaseSettingsT(BaseSettingsT const&) = delete;
- BaseSettingsT& operator=(BaseSettingsT const&) = delete;
-
- // -- Const methods
- std::string AbsoluteBuildPath(std::string const& relativePath) const;
- bool FindHeader(std::string& header,
- std::string const& testBasePath) const;
-
- // -- Attributes
- // - Config
- bool MultiConfig;
- bool IncludeProjectDirsBefore;
- unsigned int QtVersionMajor;
- unsigned int NumThreads;
- // - Directories
- std::string ProjectSourceDir;
- std::string ProjectBinaryDir;
- std::string CurrentSourceDir;
- std::string CurrentBinaryDir;
- std::string AutogenBuildDir;
- std::string AutogenIncludeDir;
- // - Files
- std::vector<std::string> HeaderExtensions;
- // - File system
- FileSystem* FileSys;
- };
-
- /// @brief Moc settings
- ///
- class MocSettingsT
- {
- public:
- MocSettingsT(FileSystem* fileSys)
- : FileSys(fileSys)
- {
- }
-
- MocSettingsT(MocSettingsT const&) = delete;
- MocSettingsT& operator=(MocSettingsT const&) = delete;
-
- // -- Const methods
- bool skipped(std::string const& fileName) const;
- std::string FindMacro(std::string const& content) const;
- std::string MacrosString() const;
- std::string FindIncludedFile(std::string const& sourcePath,
- std::string const& includeString) const;
- void FindDependencies(std::string const& content,
- std::set<std::string>& depends) const;
-
- // -- Attributes
- bool Enabled = false;
- bool SettingsChanged = false;
- bool RelaxedMode = false;
- std::string Executable;
- std::string CompFileAbs;
- std::string PredefsFileRel;
- std::string PredefsFileAbs;
- std::unordered_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 Uic settings
- ///
- class UicSettingsT
- {
- public:
- UicSettingsT() = default;
-
- UicSettingsT(UicSettingsT const&) = delete;
- UicSettingsT& operator=(UicSettingsT const&) = delete;
-
- // -- Const methods
- bool skipped(std::string const& fileName) const;
-
- // -- Attributes
- bool Enabled = false;
- bool SettingsChanged = false;
- std::string Executable;
- std::unordered_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
- {
- public:
- JobT() = default;
- virtual ~JobT() = default;
-
- JobT(JobT const&) = delete;
- JobT& operator=(JobT const&) = delete;
-
- // -- Abstract processing interface
- virtual void Process(WorkerT& wrk) = 0;
- };
-
- // Job management types
- typedef std::unique_ptr<JobT> 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 includerFile,
- std::string&& includeString)
- : SourceFile(std::move(sourceFile))
- , IncluderFile(std::move(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 a file job
- ///
- class JobUicT : public JobT
- {
- public:
- JobUicT(std::string&& sourceFile, std::string includerFile,
- std::string&& includeString)
- : SourceFile(std::move(sourceFile))
- , IncluderFile(std::move(includerFile))
- , IncludeString(std::move(includeString))
- {
- }
-
- private:
- void Process(WorkerT& wrk) override;
- bool UpdateRequired(WorkerT& wrk);
- void GenerateUic(WorkerT& wrk);
-
- public:
- std::string SourceFile;
- std::string IncluderFile;
- std::string IncludeString;
- std::string BuildFile;
- };
-
- /// @brief Worker Thread
- ///
- class WorkerT
- {
- public:
- WorkerT(cmQtAutoGeneratorMocUic* gen, uv_loop_t* uvLoop);
- ~WorkerT();
-
- WorkerT(WorkerT const&) = delete;
- WorkerT& operator=(WorkerT const&) = delete;
-
- // -- Const accessors
- cmQtAutoGeneratorMocUic& Gen() const { return *Gen_; }
- Logger& Log() const { return Gen_->Log(); }
- FileSystem& FileSys() const { return Gen_->FileSys(); }
- const BaseSettingsT& Base() const { return Gen_->Base(); }
- const MocSettingsT& Moc() const { return Gen_->Moc(); }
- const UicSettingsT& Uic() const { return Gen_->Uic(); }
-
- // -- Log info
- void LogInfo(GenT genType, std::string const& message) const;
- // -- Log warning
- void LogWarning(GenT genType, std::string const& message) const;
- void LogFileWarning(GenT genType, std::string const& filename,
- std::string const& message) const;
- // -- Log error
- void LogError(GenT genType, std::string const& message) const;
- void LogFileError(GenT genType, std::string const& filename,
- std::string const& message) const;
- void LogCommandError(GenT genType, std::string const& message,
- std::vector<std::string> const& command,
- std::string const& output) const;
-
- // -- External processes
- /// @brief Verbose logging version
- bool RunProcess(GenT genType, ProcessResultT& result,
- std::vector<std::string> const& command);
-
- private:
- /// @brief Thread main loop
- void Loop();
-
- // -- Libuv callbacks
- static void UVProcessStart(uv_async_t* handle);
- void UVProcessFinished();
-
- private:
- // -- Generator
- cmQtAutoGeneratorMocUic* Gen_;
- // -- Job handle
- JobHandleT JobHandle_;
- // -- Process management
- std::mutex ProcessMutex_;
- cm::uv_async_ptr ProcessRequest_;
- std::condition_variable ProcessCondition_;
- std::unique_ptr<ReadOnlyProcessT> Process_;
- // -- System thread
- std::thread Thread_;
- };
-
- /// @brief Processing stage
- enum class StageT
- {
- SETTINGS_READ,
- CREATE_DIRECTORIES,
- PARSE_SOURCES,
- PARSE_HEADERS,
- MOC_PREDEFS,
- MOC_PROCESS,
- MOCS_COMPILATION,
- UIC_PROCESS,
- SETTINGS_WRITE,
- FINISH,
- END
- };
-
- // -- 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);
- std::string ParallelMocAutoRegister(std::string const& baseName);
- void ParallelMocAutoUpdated();
-
-private:
- // -- Utility accessors
- Logger& Log() { return Logger_; }
- FileSystem& FileSys() { return FileSys_; }
- // -- libuv loop accessors
- uv_loop_t* UVLoop() { return UVLoop_.get(); }
- cm::uv_async_ptr& UVRequest() { return UVRequest_; }
- // -- Abstract processing interface
- bool Init(cmMakefile* makefile) override;
- bool Process() override;
- // -- Process stage
- static void UVPollStage(uv_async_t* handle);
- void PollStage();
- void SetStage(StageT stage);
- // -- Settings file
- void SettingsFileRead();
- void SettingsFileWrite();
- // -- Thread processing
- bool ThreadsStartJobs(JobQueueT& queue);
- bool ThreadsJobsDone();
- void ThreadsStop();
- void RegisterJobError();
- // -- Generation
- void CreateDirectories();
- void MocGenerateCompilation();
-
-private:
- // -- Utility
- Logger Logger_;
- FileSystem FileSys_;
- // -- Settings
- BaseSettingsT Base_;
- MocSettingsT Moc_;
- UicSettingsT Uic_;
- // -- libuv loop
-#ifdef CMAKE_UV_SIGNAL_HACK
- std::unique_ptr<cmUVSignalHackRAII> UVHackRAII_;
-#endif
- std::unique_ptr<uv_loop_t> UVLoop_;
- cm::uv_async_ptr UVRequest_;
- StageT Stage_ = StageT::SETTINGS_READ;
- // -- Job queues
- std::mutex JobsMutex_;
- struct
- {
- JobQueueT Sources;
- JobQueueT Headers;
- JobQueueT MocPredefs;
- JobQueueT Moc;
- JobQueueT Uic;
- } JobQueues_;
- JobQueueT JobQueue_;
- std::size_t volatile JobsRemain_ = 0;
- bool volatile JobError_ = false;
- bool volatile JobThreadsAbort_ = false;
- std::condition_variable JobsConditionRead_;
- // -- Moc meta
- std::set<std::string> MocIncludedStrings_;
- std::set<std::string> MocIncludedFiles_;
- std::set<std::string> MocAutoFiles_;
- bool volatile MocAutoFileUpdated_ = false;
- // -- 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/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx
new file mode 100644
index 0000000..75c5d8a
--- /dev/null
+++ b/Source/cmQtAutoMocUic.cxx
@@ -0,0 +1,1747 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmQtAutoMocUic.h"
+
+#include <algorithm>
+#include <array>
+#include <deque>
+#include <list>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <utility>
+
+#include "cmAlgorithms.h"
+#include "cmCryptoHash.h"
+#include "cmMakefile.h"
+#include "cmQtAutoGen.h"
+#include "cmSystemTools.h"
+#include "cmake.h"
+
+#if defined(__APPLE__)
+# include <unistd.h>
+#endif
+
+// -- Class methods
+
+std::string cmQtAutoMocUic::BaseSettingsT::AbsoluteBuildPath(
+ std::string const& relativePath) const
+{
+ return FileSys->CollapseFullPath(relativePath, AutogenBuildDir);
+}
+
+/**
+ * @brief Tries to find the header file to the given file base path by
+ * appending different header extensions
+ * @return True on success
+ */
+bool cmQtAutoMocUic::BaseSettingsT::FindHeader(
+ std::string& header, std::string const& testBasePath) const
+{
+ 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;
+}
+
+bool cmQtAutoMocUic::MocSettingsT::skipped(std::string const& fileName) const
+{
+ 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 cmQtAutoMocUic::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 cmQtAutoMocUic::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;
+}
+
+std::string cmQtAutoMocUic::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->GetRealPath(testPath);
+ }
+ }
+ // Search in include directories
+ for (std::string const& path : IncludePaths) {
+ std::string fullPath = path;
+ fullPath.push_back('/');
+ fullPath += includeString;
+ if (FileSys->FileExists(fullPath)) {
+ return FileSys->GetRealPath(fullPath);
+ }
+ }
+ // Return empty string
+ return std::string();
+}
+
+void cmQtAutoMocUic::MocSettingsT::FindDependencies(
+ std::string const& content, std::set<std::string>& depends) const
+{
+ if (!DependFilters.empty() && !content.empty()) {
+ for (KeyExpT const& filter : DependFilters) {
+ // Run a simple find string check
+ if (content.find(filter.Key) != std::string::npos) {
+ // Run the expensive regular expression check loop
+ const char* contentChars = content.c_str();
+ cmsys::RegularExpressionMatch match;
+ while (filter.Exp.find(contentChars, match)) {
+ {
+ std::string dep = match.match(1);
+ if (!dep.empty()) {
+ depends.emplace(std::move(dep));
+ }
+ }
+ contentChars += match.end();
+ }
+ }
+ }
+ }
+}
+
+bool cmQtAutoMocUic::UicSettingsT::skipped(std::string const& fileName) const
+{
+ return (!Enabled || (SkipList.find(fileName) != SkipList.end()));
+}
+
+void cmQtAutoMocUic::JobT::LogError(GenT genType,
+ std::string const& message) const
+{
+ Gen()->AbortError();
+ Gen()->Log().Error(genType, message);
+}
+
+void cmQtAutoMocUic::JobT::LogFileError(GenT genType,
+ std::string const& filename,
+ std::string const& message) const
+{
+ Gen()->AbortError();
+ Gen()->Log().ErrorFile(genType, filename, message);
+}
+
+void cmQtAutoMocUic::JobT::LogCommandError(
+ GenT genType, std::string const& message,
+ std::vector<std::string> const& command, std::string const& output) const
+{
+ Gen()->AbortError();
+ Gen()->Log().ErrorCommand(genType, message, command, output);
+}
+
+bool cmQtAutoMocUic::JobT::RunProcess(GenT genType,
+ cmWorkerPool::ProcessResultT& result,
+ std::vector<std::string> const& command)
+{
+ // Log command
+ if (Log().Verbose()) {
+ std::string msg = "Running command:\n";
+ msg += QuotedCommand(command);
+ msg += '\n';
+ Log().Info(genType, msg);
+ }
+ return cmWorkerPool::JobT::RunProcess(result, command,
+ Gen()->Base().AutogenBuildDir);
+}
+
+void cmQtAutoMocUic::JobMocPredefsT::Process()
+{
+ // (Re)generate moc_predefs.h on demand
+ bool generate(false);
+ bool fileExists(FileSys().FileExists(Gen()->Moc().PredefsFileAbs));
+ if (!fileExists) {
+ if (Log().Verbose()) {
+ std::string reason = "Generating ";
+ reason += Quoted(Gen()->Moc().PredefsFileRel);
+ reason += " because it doesn't exist";
+ Log().Info(GenT::MOC, reason);
+ }
+ generate = true;
+ } else if (Gen()->Moc().SettingsChanged) {
+ if (Log().Verbose()) {
+ std::string reason = "Generating ";
+ reason += Quoted(Gen()->Moc().PredefsFileRel);
+ reason += " because the settings changed.";
+ Log().Info(GenT::MOC, reason);
+ }
+ generate = true;
+ }
+ if (generate) {
+ cmWorkerPool::ProcessResultT result;
+ {
+ // Compose command
+ std::vector<std::string> cmd = Gen()->Moc().PredefsCmd;
+ // Add includes
+ cmd.insert(cmd.end(), Gen()->Moc().Includes.begin(),
+ Gen()->Moc().Includes.end());
+ // Add definitions
+ for (std::string const& def : Gen()->Moc().Definitions) {
+ cmd.push_back("-D" + def);
+ }
+ // Execute command
+ if (!RunProcess(GenT::MOC, result, cmd)) {
+ std::string emsg = "The content generation command for ";
+ emsg += Quoted(Gen()->Moc().PredefsFileRel);
+ emsg += " failed.\n";
+ emsg += result.ErrorMessage;
+ LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
+ }
+ }
+
+ // (Re)write predefs file only on demand
+ if (!result.error()) {
+ if (!fileExists ||
+ FileSys().FileDiffers(Gen()->Moc().PredefsFileAbs, result.StdOut)) {
+ if (FileSys().FileWrite(Gen()->Moc().PredefsFileAbs, result.StdOut)) {
+ // Success
+ } else {
+ std::string emsg = "Writing ";
+ emsg += Quoted(Gen()->Moc().PredefsFileRel);
+ emsg += " failed.";
+ LogFileError(GenT::MOC, Gen()->Moc().PredefsFileAbs, emsg);
+ }
+ } else {
+ // Touch to update the time stamp
+ if (Log().Verbose()) {
+ std::string msg = "Touching ";
+ msg += Quoted(Gen()->Moc().PredefsFileRel);
+ msg += ".";
+ Log().Info(GenT::MOC, msg);
+ }
+ FileSys().Touch(Gen()->Moc().PredefsFileAbs);
+ }
+ }
+ }
+}
+
+void cmQtAutoMocUic::JobParseT::Process()
+{
+ if (AutoMoc && Header) {
+ // Don't parse header for moc if the file is included by a source already
+ if (Gen()->ParallelMocIncluded(FileName)) {
+ AutoMoc = false;
+ }
+ }
+
+ if (AutoMoc || AutoUic) {
+ std::string error;
+ MetaT meta;
+ if (FileSys().FileRead(meta.Content, FileName, &error)) {
+ if (!meta.Content.empty()) {
+ meta.FileDir = FileSys().SubDirPrefix(FileName);
+ meta.FileBase = FileSys().GetFilenameWithoutLastExtension(FileName);
+
+ bool success = true;
+ if (AutoMoc) {
+ if (Header) {
+ success = ParseMocHeader(meta);
+ } else {
+ success = ParseMocSource(meta);
+ }
+ }
+ if (AutoUic && success) {
+ ParseUic(meta);
+ }
+ } else {
+ Log().WarningFile(GenT::GEN, FileName, "The source file is empty");
+ }
+ } else {
+ LogFileError(GenT::GEN, FileName, "Could not read the file: " + error);
+ }
+ }
+}
+
+bool cmQtAutoMocUic::JobParseT::ParseMocSource(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 = Gen()->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 (Gen()->Moc().RegExpInclude.find(contentChars, match)) {
+ std::string incString = match.match(2);
+ std::string incDir(FileSys().SubDirPrefix(incString));
+ std::string incBase =
+ FileSys().GetFilenameWithoutLastExtension(incString);
+ if (cmHasLiteralPrefix(incBase, "moc_")) {
+ // moc_<BASE>.cxx
+ // Remove the moc_ part from the base name
+ mocIncsUsc.emplace_back(MocInclude{
+ std::move(incString), std::move(incDir), incBase.substr(4) });
+ } else {
+ // <BASE>.moc
+ mocIncsDot.emplace_back(MocInclude{
+ std::move(incString), std::move(incDir), std::move(incBase) });
+ }
+ // Forward content pointer
+ contentChars += match.end();
+ }
+ }
+ }
+
+ // 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(meta.FileDir, mocInc.Dir + mocInc.Base);
+ if (!header.empty()) {
+ // Check if header is skipped
+ if (Gen()->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(mocInc.Base));
+ emsg += " could not be found.";
+ LogFileError(GenT::MOC, FileName, emsg);
+ }
+ return false;
+ }
+ }
+
+ // Process <BASE>.moc includes
+ for (const MocInclude& mocInc : mocIncsDot) {
+ const bool ownMoc = (mocInc.Base == meta.FileBase);
+ if (Gen()->Moc().RelaxedMode) {
+ // Relaxed mode
+ if (!ownMacro.empty() && ownMoc) {
+ // Add self
+ jobs.emplace_back(JobPre{ ownMoc, false, FileName, mocInc.Inc });
+ ownDotMocIncluded = true;
+ } else {
+ // In relaxed mode try to find a header instead but issue a warning.
+ // This is for KDE4 compatibility
+ std::string const header =
+ MocFindIncludedHeader(meta.FileDir, mocInc.Dir + mocInc.Base);
+ if (!header.empty()) {
+ // Check if header is skipped
+ if (Gen()->Moc().skipped(header)) {
+ continue;
+ }
+ // Register moc job
+ jobs.emplace_back(JobPre{ ownMoc, false, header, mocInc.Inc });
+ if (ownMacro.empty()) {
+ if (ownMoc) {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", but does not contain a ";
+ emsg += Gen()->Moc().MacrosString();
+ emsg += " macro.\nRunning moc on\n ";
+ emsg += Quoted(header);
+ emsg += "!\nBetter include ";
+ emsg += Quoted("moc_" + mocInc.Base + ".cpp");
+ emsg += " for a compatibility with strict mode.\n"
+ "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
+ Log().WarningFile(GenT::MOC, FileName, emsg);
+ } else {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += " instead of ";
+ emsg += Quoted("moc_" + mocInc.Base + ".cpp");
+ emsg += ".\nRunning moc on\n ";
+ emsg += Quoted(header);
+ emsg += "!\nBetter include ";
+ emsg += Quoted("moc_" + mocInc.Base + ".cpp");
+ emsg += " for compatibility with strict mode.\n"
+ "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n";
+ Log().WarningFile(GenT::MOC, FileName, emsg);
+ }
+ }
+ } else {
+ {
+ std::string emsg = "The file includes the moc file ";
+ emsg += Quoted(mocInc.Inc);
+ emsg += ", which seems to be the moc file from a different "
+ "source file.\nCMAKE_AUTOMOC_RELAXED_MODE: Also a "
+ "matching header ";
+ emsg += Quoted(MocStringHeaders(mocInc.Base));
+ emsg += " could not be found.";
+ LogFileError(GenT::MOC, FileName, emsg);
+ }
+ 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 += Gen()->Moc().MacrosString();
+ emsg += " macro.";
+ Log().WarningFile(GenT::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.";
+ LogFileError(GenT::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 (Gen()->Moc().RelaxedMode && ownMocUscIncluded) {
+ JobPre uscJobPre;
+ // Remove underscore job request
+ {
+ auto itC = jobs.begin();
+ auto itE = jobs.end();
+ for (; itC != itE; ++itC) {
+ JobPre& job(*itC);
+ if (job.self && job.underscore) {
+ uscJobPre = std::move(job);
+ jobs.erase(itC);
+ break;
+ }
+ }
+ }
+ // Issue a warning
+ {
+ std::string emsg = "The file contains a ";
+ emsg += ownMacro;
+ emsg += " macro, but does not include ";
+ emsg += Quoted(meta.FileBase + ".moc");
+ emsg += ". Instead it includes ";
+ emsg += Quoted(uscJobPre.IncludeString);
+ emsg += ".\nRunning moc on\n ";
+ emsg += Quoted(FileName);
+ emsg += "!\nBetter include ";
+ emsg += Quoted(meta.FileBase + ".moc");
+ emsg += " for compatibility with strict mode.\n"
+ "(CMAKE_AUTOMOC_RELAXED_MODE warning)";
+ Log().WarningFile(GenT::MOC, FileName, emsg);
+ }
+ // Add own source job
+ jobs.emplace_back(
+ JobPre{ true, false, FileName, uscJobPre.IncludeString });
+ } else {
+ // Otherwise always error out since it will not compile.
+ {
+ std::string emsg = "The file contains a ";
+ emsg += ownMacro;
+ emsg += " macro, but does not include ";
+ emsg += Quoted(meta.FileBase + ".moc");
+ emsg += "!\nConsider to\n - add #include \"";
+ emsg += meta.FileBase;
+ emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file";
+ LogFileError(GenT::MOC, FileName, emsg);
+ }
+ return false;
+ }
+ }
+
+ // Convert pre jobs to actual jobs
+ for (JobPre& jobPre : jobs) {
+ cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>(
+ std::move(jobPre.SourceFile), FileName, std::move(jobPre.IncludeString));
+ if (jobPre.self) {
+ // Read dependencies from this source
+ JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle);
+ Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends);
+ jobMoc.DependsValid = true;
+ }
+ if (!Gen()->ParallelJobPushMoc(std::move(jobHandle))) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool cmQtAutoMocUic::JobParseT::ParseMocHeader(MetaT const& meta)
+{
+ bool success = true;
+ std::string const macroName = Gen()->Moc().FindMacro(meta.Content);
+ if (!macroName.empty()) {
+ cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobMocT>(
+ std::string(FileName), std::string(), std::string());
+ // Read dependencies from this source
+ {
+ JobMocT& jobMoc = static_cast<JobMocT&>(*jobHandle);
+ Gen()->Moc().FindDependencies(meta.Content, jobMoc.Depends);
+ jobMoc.DependsValid = true;
+ }
+ success = Gen()->ParallelJobPushMoc(std::move(jobHandle));
+ }
+ return success;
+}
+
+std::string cmQtAutoMocUic::JobParseT::MocStringHeaders(
+ std::string const& fileBase) const
+{
+ std::string res = fileBase;
+ res += ".{";
+ res += cmJoin(Gen()->Base().HeaderExtensions, ",");
+ res += "}";
+ return res;
+}
+
+std::string cmQtAutoMocUic::JobParseT::MocFindIncludedHeader(
+ std::string const& includerDir, std::string const& includeBase)
+{
+ std::string header;
+ // Search in vicinity of the source
+ if (!Gen()->Base().FindHeader(header, includerDir + includeBase)) {
+ // Search in include directories
+ for (std::string const& path : Gen()->Moc().IncludePaths) {
+ std::string fullPath = path;
+ fullPath.push_back('/');
+ fullPath += includeBase;
+ if (Gen()->Base().FindHeader(header, fullPath)) {
+ break;
+ }
+ }
+ }
+ // Sanitize
+ if (!header.empty()) {
+ header = FileSys().GetRealPath(header);
+ }
+ return header;
+}
+
+bool cmQtAutoMocUic::JobParseT::ParseUic(MetaT const& meta)
+{
+ bool success = true;
+ if (meta.Content.find("ui_") != std::string::npos) {
+ const char* contentChars = meta.Content.c_str();
+ cmsys::RegularExpressionMatch match;
+ while (Gen()->Uic().RegExpInclude.find(contentChars, match)) {
+ if (!ParseUicInclude(meta, match.match(2))) {
+ success = false;
+ break;
+ }
+ contentChars += match.end();
+ }
+ }
+ return success;
+}
+
+bool cmQtAutoMocUic::JobParseT::ParseUicInclude(MetaT const& meta,
+ std::string&& includeString)
+{
+ bool success = false;
+ std::string uiInputFile = UicFindIncludedFile(meta, includeString);
+ if (!uiInputFile.empty()) {
+ if (!Gen()->Uic().skipped(uiInputFile)) {
+ cmWorkerPool::JobHandleT jobHandle = cm::make_unique<JobUicT>(
+ std::move(uiInputFile), FileName, std::move(includeString));
+ success = Gen()->ParallelJobPushUic(std::move(jobHandle));
+ } else {
+ // A skipped file is successful
+ success = true;
+ }
+ }
+ return success;
+}
+
+std::string cmQtAutoMocUic::JobParseT::UicFindIncludedFile(
+ MetaT const& meta, std::string const& includeString)
+{
+ std::string res;
+ std::string searchFile =
+ FileSys().GetFilenameWithoutLastExtension(includeString).substr(3);
+ searchFile += ".ui";
+ // Collect search paths list
+ std::deque<std::string> testFiles;
+ {
+ std::string const searchPath = FileSys().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 (!Gen()->Uic().SearchPaths.empty()) {
+ for (std::string const& sPath : Gen()->Uic().SearchPaths) {
+ testFiles.push_back((sPath + "/").append(searchFile));
+ }
+ if (!searchPath.empty()) {
+ for (std::string const& sPath : Gen()->Uic().SearchPaths) {
+ testFiles.push_back((sPath + "/").append(searchFileFull));
+ }
+ }
+ }
+ }
+
+ // Search for the .ui file!
+ for (std::string const& testFile : testFiles) {
+ if (FileSys().FileExists(testFile)) {
+ res = FileSys().GetRealPath(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";
+ }
+ LogFileError(GenT::UIC, FileName, emsg);
+ }
+
+ return res;
+}
+
+void cmQtAutoMocUic::JobPostParseT::Process()
+{
+ if (Gen()->Moc().Enabled) {
+ // Add mocs compilations fence job
+ Gen()->WorkerPool().EmplaceJob<JobMocsCompilationT>();
+ }
+ // Add finish job
+ Gen()->WorkerPool().EmplaceJob<JobFinishT>();
+}
+
+void cmQtAutoMocUic::JobMocsCompilationT::Process()
+{
+ // Compose mocs compilation file content
+ std::string content =
+ "// This file is autogenerated. Changes will be overwritten.\n";
+ if (Gen()->MocAutoFiles().empty()) {
+ // Placeholder content
+ content += "// No files found that require moc or the moc files are "
+ "included\n";
+ content += "enum some_compilers { need_more_than_nothing };\n";
+ } else {
+ // Valid content
+ char const sbeg = Gen()->Base().MultiConfig ? '<' : '"';
+ char const send = Gen()->Base().MultiConfig ? '>' : '"';
+ for (std::string const& mocfile : Gen()->MocAutoFiles()) {
+ content += "#include ";
+ content += sbeg;
+ content += mocfile;
+ content += send;
+ content += '\n';
+ }
+ }
+
+ std::string const& compAbs = Gen()->Moc().CompFileAbs;
+ if (FileSys().FileDiffers(compAbs, content)) {
+ // Actually write mocs compilation file
+ if (Log().Verbose()) {
+ Log().Info(GenT::MOC, "Generating MOC compilation " + compAbs);
+ }
+ if (!FileSys().FileWrite(compAbs, content)) {
+ LogFileError(GenT::MOC, compAbs,
+ "mocs compilation file writing failed.");
+ }
+ } else if (Gen()->MocAutoFileUpdated()) {
+ // Only touch mocs compilation file
+ if (Log().Verbose()) {
+ Log().Info(GenT::MOC, "Touching mocs compilation " + compAbs);
+ }
+ FileSys().Touch(compAbs);
+ }
+}
+
+void cmQtAutoMocUic::JobMocT::FindDependencies(std::string const& content)
+{
+ Gen()->Moc().FindDependencies(content, Depends);
+ DependsValid = true;
+}
+
+void cmQtAutoMocUic::JobMocT::Process()
+{
+ // Compute build file name
+ if (!IncludeString.empty()) {
+ BuildFile = Gen()->Base().AutogenIncludeDir;
+ BuildFile += '/';
+ BuildFile += IncludeString;
+ } else {
+ // Relative build path
+ std::string relPath = FileSys().GetFilePathChecksum(SourceFile);
+ relPath += "/moc_";
+ relPath += FileSys().GetFilenameWithoutLastExtension(SourceFile);
+
+ // Register relative file path with duplication check
+ relPath = Gen()->ParallelMocAutoRegister(relPath);
+
+ // Absolute build path
+ if (Gen()->Base().MultiConfig) {
+ BuildFile = Gen()->Base().AutogenIncludeDir;
+ BuildFile += '/';
+ BuildFile += relPath;
+ } else {
+ BuildFile = Gen()->Base().AbsoluteBuildPath(relPath);
+ }
+ }
+
+ if (UpdateRequired()) {
+ GenerateMoc();
+ }
+}
+
+bool cmQtAutoMocUic::JobMocT::UpdateRequired()
+{
+ bool const verbose = Log().Verbose();
+
+ // Test if the build file exists
+ if (!FileSys().FileExists(BuildFile)) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from its source file ";
+ reason += Quoted(SourceFile);
+ reason += " because it doesn't exist";
+ Log().Info(GenT::MOC, reason);
+ }
+ return true;
+ }
+
+ // Test if any setting changed
+ if (Gen()->Moc().SettingsChanged) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from ";
+ reason += Quoted(SourceFile);
+ reason += " because the MOC settings changed";
+ Log().Info(GenT::MOC, reason);
+ }
+ return true;
+ }
+
+ // Test if the moc_predefs file is newer
+ if (!Gen()->Moc().PredefsFileAbs.empty()) {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = FileSys().FileIsOlderThan(BuildFile,
+ Gen()->Moc().PredefsFileAbs, &error);
+ if (!isOlder && !error.empty()) {
+ LogError(GenT::MOC, error);
+ return false;
+ }
+ }
+ if (isOlder) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " because it's older than: ";
+ reason += Quoted(Gen()->Moc().PredefsFileAbs);
+ Log().Info(GenT::MOC, reason);
+ }
+ return true;
+ }
+ }
+
+ // Test if the source file is newer
+ {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
+ if (!isOlder && !error.empty()) {
+ LogError(GenT::MOC, error);
+ return false;
+ }
+ }
+ if (isOlder) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " because it's older than its source file ";
+ reason += Quoted(SourceFile);
+ Log().Info(GenT::MOC, reason);
+ }
+ return true;
+ }
+ }
+
+ // Test if a dependency file is newer
+ {
+ // Read dependencies on demand
+ if (!DependsValid) {
+ std::string content;
+ {
+ std::string error;
+ if (!FileSys().FileRead(content, SourceFile, &error)) {
+ std::string emsg = "Could not read file\n ";
+ emsg += Quoted(SourceFile);
+ emsg += "\nrequired by moc include ";
+ emsg += Quoted(IncludeString);
+ emsg += " in\n ";
+ emsg += Quoted(IncluderFile);
+ emsg += ".\n";
+ emsg += error;
+ LogError(GenT::MOC, emsg);
+ return false;
+ }
+ }
+ FindDependencies(content);
+ }
+ // Check dependency timestamps
+ std::string error;
+ std::string sourceDir = FileSys().SubDirPrefix(SourceFile);
+ for (std::string const& depFileRel : Depends) {
+ std::string depFileAbs =
+ Gen()->Moc().FindIncludedFile(sourceDir, depFileRel);
+ if (!depFileAbs.empty()) {
+ if (FileSys().FileIsOlderThan(BuildFile, depFileAbs, &error)) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from ";
+ reason += Quoted(SourceFile);
+ reason += " because it is older than it's dependency file ";
+ reason += Quoted(depFileAbs);
+ Log().Info(GenT::MOC, reason);
+ }
+ return true;
+ }
+ if (!error.empty()) {
+ LogError(GenT::MOC, error);
+ return false;
+ }
+ } else {
+ std::string message = "Could not find dependency file ";
+ message += Quoted(depFileRel);
+ Log().WarningFile(GenT::MOC, SourceFile, message);
+ }
+ }
+ }
+
+ return false;
+}
+
+void cmQtAutoMocUic::JobMocT::GenerateMoc()
+{
+ // Make sure the parent directory exists
+ if (!FileSys().MakeParentDirectory(BuildFile)) {
+ LogFileError(GenT::MOC, BuildFile, "Could not create parent directory.");
+ return;
+ }
+ {
+ // Compose moc command
+ std::vector<std::string> cmd;
+ cmd.push_back(Gen()->Moc().Executable);
+ // Add options
+ cmd.insert(cmd.end(), Gen()->Moc().AllOptions.begin(),
+ Gen()->Moc().AllOptions.end());
+ // Add predefs include
+ if (!Gen()->Moc().PredefsFileAbs.empty()) {
+ cmd.emplace_back("--include");
+ cmd.push_back(Gen()->Moc().PredefsFileAbs);
+ }
+ cmd.emplace_back("-o");
+ cmd.push_back(BuildFile);
+ cmd.push_back(SourceFile);
+
+ // Execute moc command
+ cmWorkerPool::ProcessResultT result;
+ if (RunProcess(GenT::MOC, result, cmd)) {
+ // Moc command success
+ // Print moc output
+ if (!result.StdOut.empty()) {
+ Log().Info(GenT::MOC, result.StdOut);
+ }
+ // Notify the generator that a not included file changed (on demand)
+ if (IncludeString.empty()) {
+ Gen()->ParallelMocAutoUpdated();
+ }
+ } else {
+ // Moc command failed
+ {
+ std::string emsg = "The moc process failed to compile\n ";
+ emsg += Quoted(SourceFile);
+ emsg += "\ninto\n ";
+ emsg += Quoted(BuildFile);
+ emsg += ".\n";
+ emsg += result.ErrorMessage;
+ LogCommandError(GenT::MOC, emsg, cmd, result.StdOut);
+ }
+ FileSys().FileRemove(BuildFile);
+ }
+ }
+}
+
+void cmQtAutoMocUic::JobUicT::Process()
+{
+ // Compute build file name
+ BuildFile = Gen()->Base().AutogenIncludeDir;
+ BuildFile += '/';
+ BuildFile += IncludeString;
+
+ if (UpdateRequired()) {
+ GenerateUic();
+ }
+}
+
+bool cmQtAutoMocUic::JobUicT::UpdateRequired()
+{
+ bool const verbose = Log().Verbose();
+
+ // Test if the build file exists
+ if (!FileSys().FileExists(BuildFile)) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from its source file ";
+ reason += Quoted(SourceFile);
+ reason += " because it doesn't exist";
+ Log().Info(GenT::UIC, reason);
+ }
+ return true;
+ }
+
+ // Test if the uic settings changed
+ if (Gen()->Uic().SettingsChanged) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " from ";
+ reason += Quoted(SourceFile);
+ reason += " because the UIC settings changed";
+ Log().Info(GenT::UIC, reason);
+ }
+ return true;
+ }
+
+ // Test if the source file is newer
+ {
+ bool isOlder = false;
+ {
+ std::string error;
+ isOlder = FileSys().FileIsOlderThan(BuildFile, SourceFile, &error);
+ if (!isOlder && !error.empty()) {
+ LogError(GenT::UIC, error);
+ return false;
+ }
+ }
+ if (isOlder) {
+ if (verbose) {
+ std::string reason = "Generating ";
+ reason += Quoted(BuildFile);
+ reason += " because it's older than its source file ";
+ reason += Quoted(SourceFile);
+ Log().Info(GenT::UIC, reason);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void cmQtAutoMocUic::JobUicT::GenerateUic()
+{
+ // Make sure the parent directory exists
+ if (!FileSys().MakeParentDirectory(BuildFile)) {
+ LogFileError(GenT::UIC, BuildFile, "Could not create parent directory.");
+ return;
+ }
+ {
+ // Compose uic command
+ std::vector<std::string> cmd;
+ cmd.push_back(Gen()->Uic().Executable);
+ {
+ std::vector<std::string> allOpts = Gen()->Uic().TargetOptions;
+ auto optionIt = Gen()->Uic().Options.find(SourceFile);
+ if (optionIt != Gen()->Uic().Options.end()) {
+ UicMergeOptions(allOpts, optionIt->second,
+ (Gen()->Base().QtVersionMajor == 5));
+ }
+ cmd.insert(cmd.end(), allOpts.begin(), allOpts.end());
+ }
+ cmd.emplace_back("-o");
+ cmd.emplace_back(BuildFile);
+ cmd.emplace_back(SourceFile);
+
+ cmWorkerPool::ProcessResultT result;
+ if (RunProcess(GenT::UIC, result, cmd)) {
+ // Uic command success
+ // Print uic output
+ if (!result.StdOut.empty()) {
+ Log().Info(GenT::UIC, result.StdOut);
+ }
+ } else {
+ // Uic command failed
+ {
+ std::string emsg = "The uic process failed to compile\n ";
+ emsg += Quoted(SourceFile);
+ emsg += "\ninto\n ";
+ emsg += Quoted(BuildFile);
+ emsg += "\nincluded by\n ";
+ emsg += Quoted(IncluderFile);
+ emsg += ".\n";
+ emsg += result.ErrorMessage;
+ LogCommandError(GenT::UIC, emsg, cmd, result.StdOut);
+ }
+ FileSys().FileRemove(BuildFile);
+ }
+ }
+}
+
+void cmQtAutoMocUic::JobFinishT::Process()
+{
+ Gen()->AbortSuccess();
+}
+
+cmQtAutoMocUic::cmQtAutoMocUic()
+ : Base_(&FileSys())
+ , Moc_(&FileSys())
+{
+ // Precompile regular expressions
+ Moc_.RegExpInclude.compile(
+ "(^|\n)[ \t]*#[ \t]*include[ \t]+"
+ "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]");
+ Uic_.RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+"
+ "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]");
+}
+
+cmQtAutoMocUic::~cmQtAutoMocUic() = default;
+
+bool cmQtAutoMocUic::Init(cmMakefile* makefile)
+{
+ // -- Meta
+ Base_.HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions();
+
+ // Utility lambdas
+ auto InfoGet = [makefile](const char* key) {
+ return makefile->GetSafeDefinition(key);
+ };
+ auto InfoGetBool = [makefile](const char* key) {
+ return makefile->IsOn(key);
+ };
+ auto InfoGetList = [makefile](const char* key) -> std::vector<std::string> {
+ std::vector<std::string> list;
+ cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
+ return list;
+ };
+ auto InfoGetLists =
+ [makefile](const char* key) -> std::vector<std::vector<std::string>> {
+ std::vector<std::vector<std::string>> lists;
+ {
+ std::string const value = makefile->GetSafeDefinition(key);
+ std::string::size_type pos = 0;
+ while (pos < value.size()) {
+ 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
+ if (length >= 2) {
+ std::string::const_iterator itBeg = value.begin() + (pos + 1);
+ std::string::const_iterator itEnd = itBeg + (length - 2);
+ {
+ std::string subValue(itBeg, itEnd);
+ std::vector<std::string> list;
+ cmSystemTools::ExpandListArgument(subValue, list);
+ lists.push_back(std::move(list));
+ }
+ }
+ pos += length;
+ pos += ListSep.size();
+ }
+ }
+ return lists;
+ };
+ auto InfoGetConfig = [makefile, this](const char* key) -> std::string {
+ const char* valueConf = nullptr;
+ {
+ std::string keyConf = key;
+ keyConf += '_';
+ keyConf += InfoConfig();
+ valueConf = makefile->GetDefinition(keyConf);
+ }
+ if (valueConf == nullptr) {
+ return makefile->GetSafeDefinition(key);
+ }
+ return std::string(valueConf);
+ };
+ auto InfoGetConfigList =
+ [&InfoGetConfig](const char* key) -> std::vector<std::string> {
+ std::vector<std::string> list;
+ cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
+ return list;
+ };
+
+ // -- Read info file
+ if (!makefile->ReadListFile(InfoFile())) {
+ Log().ErrorFile(GenT::GEN, InfoFile(), "File processing failed");
+ return false;
+ }
+
+ // -- Meta
+ Log().RaiseVerbosity(InfoGet("AM_VERBOSITY"));
+ Base_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG");
+ {
+ unsigned long num = Base_.NumThreads;
+ if (cmSystemTools::StringToULong(InfoGet("AM_PARALLEL").c_str(), &num)) {
+ num = std::max<unsigned long>(num, 1);
+ num = std::min<unsigned long>(num, ParallelMax);
+ Base_.NumThreads = static_cast<unsigned int>(num);
+ }
+ }
+
+ // - Files and directories
+ Base_.ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR");
+ Base_.ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR");
+ Base_.CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR");
+ Base_.CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR");
+ Base_.IncludeProjectDirsBefore =
+ InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE");
+ Base_.AutogenBuildDir = InfoGet("AM_BUILD_DIR");
+ if (Base_.AutogenBuildDir.empty()) {
+ Log().ErrorFile(GenT::GEN, InfoFile(), "Autogen build directory missing");
+ return false;
+ }
+ // include directory
+ Base_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR");
+ if (Base_.AutogenIncludeDir.empty()) {
+ Log().ErrorFile(GenT::GEN, InfoFile(),
+ "Autogen include directory missing");
+ return false;
+ }
+
+ // - Files
+ SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE");
+ if (SettingsFile_.empty()) {
+ Log().ErrorFile(GenT::GEN, InfoFile(), "Settings file name missing");
+ return false;
+ }
+
+ // - Qt environment
+ {
+ unsigned long qtv = Base_.QtVersionMajor;
+ if (cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR").c_str(),
+ &qtv)) {
+ Base_.QtVersionMajor = static_cast<unsigned int>(qtv);
+ }
+ }
+
+ // - Moc
+ Moc_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE");
+ Moc_.Enabled = !Moc().Executable.empty();
+ if (Moc().Enabled) {
+ for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) {
+ Moc_.SkipList.insert(std::move(sfl));
+ }
+ Moc_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS");
+ Moc_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES");
+ Moc_.Options = InfoGetList("AM_MOC_OPTIONS");
+ Moc_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE");
+ for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) {
+ Moc_.MacroFilters.emplace_back(
+ item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]"));
+ }
+ {
+ auto pushFilter = [this](std::string const& key, std::string const& exp,
+ std::string& error) {
+ if (!key.empty()) {
+ if (!exp.empty()) {
+ Moc_.DependFilters.emplace_back();
+ KeyExpT& filter(Moc_.DependFilters.back());
+ if (filter.Exp.compile(exp)) {
+ filter.Key = key;
+ } else {
+ error = "Regular expression compiling failed";
+ }
+ } else {
+ error = "Regular expression is empty";
+ }
+ } else {
+ error = "Key is empty";
+ }
+ 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
+ {
+ std::vector<std::string> flts = InfoGetList("AM_MOC_DEPEND_FILTERS");
+ if ((flts.size() % 2) == 0) {
+ for (std::vector<std::string>::iterator itC = flts.begin(),
+ itE = flts.end();
+ itC != itE; itC += 2) {
+ pushFilter(*itC, *(itC + 1), error);
+ if (!error.empty()) {
+ break;
+ }
+ }
+ } else {
+ Log().ErrorFile(
+ GenT::MOC, InfoFile(),
+ "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2");
+ return false;
+ }
+ }
+ if (!error.empty()) {
+ Log().ErrorFile(GenT::MOC, InfoFile(), error);
+ return false;
+ }
+ }
+ Moc_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD");
+ // Install moc predefs job
+ if (!Moc().PredefsCmd.empty()) {
+ WorkerPool().EmplaceJob<JobMocPredefsT>();
+ }
+ }
+
+ // - Uic
+ Uic_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE");
+ Uic_.Enabled = !Uic().Executable.empty();
+ if (Uic().Enabled) {
+ for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) {
+ Uic_.SkipList.insert(std::move(sfl));
+ }
+ 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() << "/"
+ << options.size() << ")";
+ Log().ErrorFile(GenT::UIC, InfoFile(), ost.str());
+ return false;
+ }
+ auto fitEnd = sources.cend();
+ auto fit = sources.begin();
+ auto oit = options.begin();
+ while (fit != fitEnd) {
+ Uic_.Options[*fit] = std::move(*oit);
+ ++fit;
+ ++oit;
+ }
+ }
+ }
+
+ // - Headers and sources
+ // Add sources
+ {
+ auto addSource = [this](std::string&& src, bool moc, bool uic) {
+ WorkerPool().EmplaceJob<JobParseT>(std::move(src), moc, uic, false);
+ };
+ for (std::string& src : InfoGetList("AM_SOURCES")) {
+ addSource(std::move(src), true, true);
+ }
+ if (Moc().Enabled) {
+ for (std::string& src : InfoGetList("AM_MOC_SOURCES")) {
+ addSource(std::move(src), true, false);
+ }
+ }
+ if (Uic().Enabled) {
+ for (std::string& src : InfoGetList("AM_UIC_SOURCES")) {
+ addSource(std::move(src), false, true);
+ }
+ }
+ }
+ // Add Fence job
+ WorkerPool().EmplaceJob<JobFenceT>();
+ // Add headers
+ {
+ auto addHeader = [this](std::string&& hdr, bool moc, bool uic) {
+ WorkerPool().EmplaceJob<JobParseT>(std::move(hdr), moc, uic, true);
+ };
+ for (std::string& hdr : InfoGetList("AM_HEADERS")) {
+ addHeader(std::move(hdr), true, true);
+ }
+ if (Moc().Enabled) {
+ for (std::string& hdr : InfoGetList("AM_MOC_HEADERS")) {
+ addHeader(std::move(hdr), true, false);
+ }
+ }
+ if (Uic().Enabled) {
+ for (std::string& hdr : InfoGetList("AM_UIC_HEADERS")) {
+ addHeader(std::move(hdr), false, true);
+ }
+ }
+ }
+ // Addpost parse fence job
+ WorkerPool().EmplaceJob<JobPostParseT>();
+
+ // Init derived information
+ // ------------------------
+
+ // Init file path checksum generator
+ FileSys().setupFilePathChecksum(
+ Base().CurrentSourceDir, Base().CurrentBinaryDir, Base().ProjectSourceDir,
+ Base().ProjectBinaryDir);
+
+ // Moc variables
+ if (Moc().Enabled) {
+ // Mocs compilation file
+ Moc_.CompFileAbs = Base().AbsoluteBuildPath("mocs_compilation.cpp");
+
+ // Moc predefs file
+ if (!Moc_.PredefsCmd.empty()) {
+ Moc_.PredefsFileRel = "moc_predefs";
+ if (Base_.MultiConfig) {
+ Moc_.PredefsFileRel += '_';
+ Moc_.PredefsFileRel += InfoConfig();
+ }
+ Moc_.PredefsFileRel += ".h";
+ Moc_.PredefsFileAbs = Base_.AbsoluteBuildPath(Moc().PredefsFileRel);
+ }
+
+ // Sort include directories on demand
+ if (Base().IncludeProjectDirsBefore) {
+ // Move strings to temporary list
+ std::list<std::string> includes;
+ includes.insert(includes.end(), Moc().IncludePaths.begin(),
+ Moc().IncludePaths.end());
+ Moc_.IncludePaths.clear();
+ Moc_.IncludePaths.reserve(includes.size());
+ // Append project directories only
+ {
+ std::array<std::string const*, 2> const movePaths = {
+ { &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())) {
+ Moc_.IncludePaths.push_back(path);
+ it = includes.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+ }
+ // Append remaining directories
+ Moc_.IncludePaths.insert(Moc_.IncludePaths.end(), includes.begin(),
+ includes.end());
+ }
+ // Compose moc includes list
+ {
+ std::set<std::string> frameworkPaths;
+ for (std::string const& path : Moc().IncludePaths) {
+ Moc_.Includes.push_back("-I" + path);
+ // Extract framework path
+ if (cmHasLiteralSuffix(path, ".framework/Headers")) {
+ // Go up twice to get to the framework root
+ std::vector<std::string> pathComponents;
+ FileSys().SplitPath(path, pathComponents);
+ std::string frameworkPath = FileSys().JoinPath(
+ pathComponents.begin(), pathComponents.end() - 2);
+ frameworkPaths.insert(frameworkPath);
+ }
+ }
+ // Append framework includes
+ for (std::string const& path : frameworkPaths) {
+ Moc_.Includes.emplace_back("-F");
+ Moc_.Includes.push_back(path);
+ }
+ }
+ // Setup single list with all options
+ {
+ // Add includes
+ Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Includes.begin(),
+ Moc().Includes.end());
+ // Add definitions
+ for (std::string const& def : Moc().Definitions) {
+ Moc_.AllOptions.push_back("-D" + def);
+ }
+ // Add options
+ Moc_.AllOptions.insert(Moc_.AllOptions.end(), Moc().Options.begin(),
+ Moc().Options.end());
+ }
+ }
+
+ return true;
+}
+
+bool cmQtAutoMocUic::Process()
+{
+ SettingsFileRead();
+ if (!CreateDirectories()) {
+ return false;
+ }
+
+ if (!WorkerPool_.Process(Base().NumThreads, this)) {
+ return false;
+ }
+
+ if (JobError_) {
+ return false;
+ }
+
+ return SettingsFileWrite();
+}
+
+void cmQtAutoMocUic::SettingsFileRead()
+{
+ // Compose current settings strings
+ {
+ cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
+ std::string const sep(" ~~~ ");
+ if (Moc_.Enabled) {
+ std::string str;
+ str += Moc().Executable;
+ str += sep;
+ str += cmJoin(Moc().AllOptions, ";");
+ str += sep;
+ str += Base().IncludeProjectDirsBefore ? "TRUE" : "FALSE";
+ str += sep;
+ str += cmJoin(Moc().PredefsCmd, ";");
+ str += sep;
+ SettingsStringMoc_ = crypt.HashString(str);
+ }
+ if (Uic().Enabled) {
+ std::string str;
+ str += Uic().Executable;
+ str += sep;
+ str += cmJoin(Uic().TargetOptions, ";");
+ for (const auto& item : Uic().Options) {
+ str += sep;
+ str += item.first;
+ str += sep;
+ str += cmJoin(item.second, ";");
+ }
+ str += sep;
+ SettingsStringUic_ = crypt.HashString(str);
+ }
+ }
+
+ // 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 (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;
+ }
+ }
+ }
+}
+
+bool cmQtAutoMocUic::SettingsFileWrite()
+{
+ // Only write if any setting changed
+ if (Moc().SettingsChanged || Uic().SettingsChanged) {
+ if (Log().Verbose()) {
+ Log().Info(GenT::GEN, "Writing settings file " + Quoted(SettingsFile_));
+ }
+ // Compose settings file content
+ std::string content;
+ {
+ auto SettingAppend = [&content](const char* key,
+ std::string const& value) {
+ if (!value.empty()) {
+ content += key;
+ content += ':';
+ content += value;
+ content += '\n';
+ }
+ };
+ SettingAppend("moc", SettingsStringMoc_);
+ SettingAppend("uic", SettingsStringUic_);
+ }
+ // Write settings file
+ std::string error;
+ if (!FileSys().FileWrite(SettingsFile_, content, &error)) {
+ Log().ErrorFile(GenT::GEN, SettingsFile_,
+ "Settings file writing failed. " + error);
+ // Remove old settings file to trigger a full rebuild on the next run
+ FileSys().FileRemove(SettingsFile_);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool cmQtAutoMocUic::CreateDirectories()
+{
+ // Create AUTOGEN include directory
+ if (!FileSys().MakeDirectory(Base().AutogenIncludeDir)) {
+ Log().ErrorFile(GenT::GEN, Base().AutogenIncludeDir,
+ "Could not create directory.");
+ return false;
+ }
+ return true;
+}
+
+// Private method that requires cmQtAutoMocUic::JobsMutex_ to be
+// locked
+void cmQtAutoMocUic::Abort(bool error)
+{
+ if (error) {
+ JobError_.store(true);
+ }
+ WorkerPool_.Abort();
+}
+
+bool cmQtAutoMocUic::ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle)
+{
+ JobMocT const& mocJob(static_cast<JobMocT&>(*jobHandle));
+ // Do additional tests if this is an included moc job
+ if (!mocJob.IncludeString.empty()) {
+ std::lock_guard<std::mutex> guard(MocMetaMutex_);
+ // Register included moc file
+ MocIncludedFiles_.emplace(mocJob.SourceFile);
+
+ // Check if the same moc file would be generated from a different
+ // source file.
+ auto const range = MocIncludes_.equal_range(mocJob.IncludeString);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->second[0] == mocJob.SourceFile) {
+ // The output file already gets generated
+ return true;
+ }
+ {
+ // The output file already gets generated - from a different source
+ // file!
+ std::string error = "The two source files\n ";
+ error += Quoted(mocJob.IncluderFile);
+ error += " and\n ";
+ error += Quoted(it->second[1]);
+ error += "\ncontain the same moc include string ";
+ error += Quoted(mocJob.IncludeString);
+ error += "\nbut the moc file would be generated from different "
+ "source files\n ";
+ error += Quoted(mocJob.SourceFile);
+ error += " and\n ";
+ error += Quoted(it->second[0]);
+ error += ".\nConsider to\n"
+ "- not include the \"moc_<NAME>.cpp\" file\n"
+ "- add a directory prefix to a \"<NAME>.moc\" include "
+ "(e.g \"sub/<NAME>.moc\")\n"
+ "- rename the source file(s)\n";
+ Log().Error(GenT::MOC, error);
+ AbortError();
+ return false;
+ }
+ }
+
+ // We're still here so register this job
+ MocIncludes_.emplace_hint(range.first, mocJob.IncludeString,
+ std::array<std::string, 2>{
+ { mocJob.SourceFile, mocJob.IncluderFile } });
+ }
+ return WorkerPool_.PushJob(std::move(jobHandle));
+}
+
+bool cmQtAutoMocUic::ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle)
+{
+ const JobUicT& uicJob(static_cast<JobUicT&>(*jobHandle));
+ {
+ std::lock_guard<std::mutex> guard(UicMetaMutex_);
+ // Check if the same uic file would be generated from a different
+ // source file.
+ auto const range = UicIncludes_.equal_range(uicJob.IncludeString);
+ for (auto it = range.first; it != range.second; ++it) {
+ if (it->second[0] == uicJob.SourceFile) {
+ // The output file already gets generated
+ return true;
+ }
+ {
+ // The output file already gets generated - from a different .ui
+ // file!
+ std::string error = "The two source files\n ";
+ error += Quoted(uicJob.IncluderFile);
+ error += " and\n ";
+ error += Quoted(it->second[1]);
+ error += "\ncontain the same uic include string ";
+ error += Quoted(uicJob.IncludeString);
+ error += "\nbut the uic file would be generated from different "
+ "source files\n ";
+ error += Quoted(uicJob.SourceFile);
+ error += " and\n ";
+ error += Quoted(it->second[0]);
+ error +=
+ ".\nConsider to\n"
+ "- add a directory prefix to a \"ui_<NAME>.h\" include "
+ "(e.g \"sub/ui_<NAME>.h\")\n"
+ "- rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.h\" "
+ "include(s)\n";
+ Log().Error(GenT::UIC, error);
+ AbortError();
+ return false;
+ }
+ }
+
+ // We're still here so register this job
+ UicIncludes_.emplace_hint(range.first, uicJob.IncludeString,
+ std::array<std::string, 2>{
+ { uicJob.SourceFile, uicJob.IncluderFile } });
+ }
+ return WorkerPool_.PushJob(std::move(jobHandle));
+}
+
+bool cmQtAutoMocUic::ParallelMocIncluded(std::string const& sourceFile)
+{
+ std::lock_guard<std::mutex> guard(MocMetaMutex_);
+ return (MocIncludedFiles_.find(sourceFile) != MocIncludedFiles_.end());
+}
+
+std::string cmQtAutoMocUic::ParallelMocAutoRegister(
+ std::string const& baseName)
+{
+ std::string res;
+ {
+ std::lock_guard<std::mutex> mocLock(MocMetaMutex_);
+ res = baseName;
+ res += ".cpp";
+ if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
+ MocAutoFiles_.emplace(res);
+ } else {
+ // Append number suffix to the file name
+ for (unsigned int ii = 2; ii != 1024; ++ii) {
+ res = baseName;
+ res += '_';
+ res += std::to_string(ii);
+ res += ".cpp";
+ if (MocAutoFiles_.find(res) == MocAutoFiles_.end()) {
+ MocAutoFiles_.emplace(res);
+ break;
+ }
+ }
+ }
+ }
+ return res;
+}
diff --git a/Source/cmQtAutoMocUic.h b/Source/cmQtAutoMocUic.h
new file mode 100644
index 0000000..3902abb
--- /dev/null
+++ b/Source/cmQtAutoMocUic.h
@@ -0,0 +1,413 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmQtAutoMocUic_h
+#define cmQtAutoMocUic_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmQtAutoGen.h"
+#include "cmQtAutoGenerator.h"
+#include "cmWorkerPool.h"
+#include "cmsys/RegularExpression.hxx"
+
+#include <array>
+#include <atomic>
+#include <map>
+#include <memory> // IWYU pragma: keep
+#include <mutex>
+#include <set>
+#include <string>
+#include <unordered_set>
+#include <utility>
+#include <vector>
+
+class cmMakefile;
+
+// @brief AUTOMOC and AUTOUIC generator
+class cmQtAutoMocUic : public cmQtAutoGenerator
+{
+public:
+ cmQtAutoMocUic();
+ ~cmQtAutoMocUic() override;
+
+ cmQtAutoMocUic(cmQtAutoMocUic const&) = delete;
+ cmQtAutoMocUic& operator=(cmQtAutoMocUic const&) = delete;
+
+public:
+ // -- Types
+ typedef std::multimap<std::string, std::array<std::string, 2>> IncludesMap;
+
+ /// @brief Search key plus regular expression pair
+ ///
+ struct KeyExpT
+ {
+ KeyExpT() = default;
+
+ KeyExpT(const char* key, const char* exp)
+ : Key(key)
+ , Exp(exp)
+ {
+ }
+
+ KeyExpT(std::string key, std::string const& exp)
+ : Key(std::move(key))
+ , Exp(exp)
+ {
+ }
+
+ std::string Key;
+ cmsys::RegularExpression Exp;
+ };
+
+ /// @brief Common settings
+ ///
+ class BaseSettingsT
+ {
+ public:
+ // -- Volatile methods
+ BaseSettingsT(FileSystem* fileSystem)
+ : MultiConfig(false)
+ , IncludeProjectDirsBefore(false)
+ , QtVersionMajor(4)
+ , NumThreads(1)
+ , FileSys(fileSystem)
+ {
+ }
+
+ BaseSettingsT(BaseSettingsT const&) = delete;
+ BaseSettingsT& operator=(BaseSettingsT const&) = delete;
+
+ // -- Const methods
+ std::string AbsoluteBuildPath(std::string const& relativePath) const;
+ bool FindHeader(std::string& header,
+ std::string const& testBasePath) const;
+
+ // -- Attributes
+ // - Config
+ bool MultiConfig;
+ bool IncludeProjectDirsBefore;
+ unsigned int QtVersionMajor;
+ unsigned int NumThreads;
+ // - Directories
+ std::string ProjectSourceDir;
+ std::string ProjectBinaryDir;
+ std::string CurrentSourceDir;
+ std::string CurrentBinaryDir;
+ std::string AutogenBuildDir;
+ std::string AutogenIncludeDir;
+ // - Files
+ std::vector<std::string> HeaderExtensions;
+ // - File system
+ FileSystem* FileSys;
+ };
+
+ /// @brief Moc settings
+ ///
+ class MocSettingsT
+ {
+ public:
+ MocSettingsT(FileSystem* fileSys)
+ : FileSys(fileSys)
+ {
+ }
+
+ MocSettingsT(MocSettingsT const&) = delete;
+ MocSettingsT& operator=(MocSettingsT const&) = delete;
+
+ // -- Const methods
+ bool skipped(std::string const& fileName) const;
+ std::string FindMacro(std::string const& content) const;
+ std::string MacrosString() const;
+ std::string FindIncludedFile(std::string const& sourcePath,
+ std::string const& includeString) const;
+ void FindDependencies(std::string const& content,
+ std::set<std::string>& depends) const;
+
+ // -- Attributes
+ bool Enabled = false;
+ bool SettingsChanged = false;
+ bool RelaxedMode = false;
+ std::string Executable;
+ std::string CompFileAbs;
+ std::string PredefsFileRel;
+ std::string PredefsFileAbs;
+ std::unordered_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 Uic settings
+ ///
+ class UicSettingsT
+ {
+ public:
+ UicSettingsT() = default;
+
+ UicSettingsT(UicSettingsT const&) = delete;
+ UicSettingsT& operator=(UicSettingsT const&) = delete;
+
+ // -- Const methods
+ bool skipped(std::string const& fileName) const;
+
+ // -- Attributes
+ bool Enabled = false;
+ bool SettingsChanged = false;
+ std::string Executable;
+ std::unordered_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 concurrent job processing
+ ///
+ class JobT : public cmWorkerPool::JobT
+ {
+ protected:
+ /**
+ * @brief Protected default constructor
+ */
+ JobT(bool fence = false)
+ : cmWorkerPool::JobT(fence)
+ {
+ }
+
+ //! Get the generator. Only valid during Process() call!
+ cmQtAutoMocUic* Gen() const
+ {
+ return static_cast<cmQtAutoMocUic*>(UserData());
+ };
+
+ //! Get the file system interface. Only valid during Process() call!
+ FileSystem& FileSys() { return Gen()->FileSys(); }
+ //! Get the logger. Only valid during Process() call!
+ Logger& Log() { return Gen()->Log(); }
+
+ // -- Error logging with automatic abort
+ void LogError(GenT genType, std::string const& message) const;
+ void LogFileError(GenT genType, std::string const& filename,
+ std::string const& message) const;
+ void LogCommandError(GenT genType, std::string const& message,
+ std::vector<std::string> const& command,
+ std::string const& output) const;
+
+ /**
+ * @brief Run an external process. Use only during Process() call!
+ */
+ bool RunProcess(GenT genType, cmWorkerPool::ProcessResultT& result,
+ std::vector<std::string> const& command);
+ };
+
+ /// @brief Fence job utility class
+ ///
+ class JobFenceT : public JobT
+ {
+ public:
+ JobFenceT()
+ : JobT(true)
+ {
+ }
+ void Process() override{};
+ };
+
+ /// @brief Generate moc_predefs.h
+ ///
+ class JobMocPredefsT : public JobT
+ {
+ private:
+ void Process() override;
+ };
+
+ /// @brief Parses a source file
+ ///
+ 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() override;
+ bool ParseMocSource(MetaT const& meta);
+ bool ParseMocHeader(MetaT const& meta);
+ std::string MocStringHeaders(std::string const& fileBase) const;
+ std::string MocFindIncludedHeader(std::string const& includerDir,
+ std::string const& includeBase);
+ bool ParseUic(MetaT const& meta);
+ bool ParseUicInclude(MetaT const& meta, std::string&& includeString);
+ std::string UicFindIncludedFile(MetaT const& meta,
+ std::string const& includeString);
+
+ private:
+ std::string FileName;
+ bool AutoMoc = false;
+ bool AutoUic = false;
+ bool Header = false;
+ };
+
+ /// @brief Generates additional jobs after all files have been parsed
+ ///
+ class JobPostParseT : public JobFenceT
+ {
+ private:
+ void Process() override;
+ };
+
+ /// @brief Generate mocs_compilation.cpp
+ ///
+ class JobMocsCompilationT : public JobFenceT
+ {
+ private:
+ void Process() override;
+ };
+
+ /// @brief Moc a file job
+ ///
+ class JobMocT : public JobT
+ {
+ public:
+ JobMocT(std::string sourceFile, std::string includerFile,
+ std::string includeString)
+ : SourceFile(std::move(sourceFile))
+ , IncluderFile(std::move(includerFile))
+ , IncludeString(std::move(includeString))
+ {
+ }
+
+ void FindDependencies(std::string const& content);
+
+ private:
+ void Process() override;
+ bool UpdateRequired();
+ void GenerateMoc();
+
+ public:
+ std::string SourceFile;
+ std::string IncluderFile;
+ std::string IncludeString;
+ std::string BuildFile;
+ bool DependsValid = false;
+ std::set<std::string> Depends;
+ };
+
+ /// @brief Uic a file job
+ ///
+ class JobUicT : public JobT
+ {
+ public:
+ JobUicT(std::string sourceFile, std::string includerFile,
+ std::string includeString)
+ : SourceFile(std::move(sourceFile))
+ , IncluderFile(std::move(includerFile))
+ , IncludeString(std::move(includeString))
+ {
+ }
+
+ private:
+ void Process() override;
+ bool UpdateRequired();
+ void GenerateUic();
+
+ public:
+ std::string SourceFile;
+ std::string IncluderFile;
+ std::string IncludeString;
+ std::string BuildFile;
+ };
+
+ /// @brief The last job
+ ///
+ class JobFinishT : public JobFenceT
+ {
+ private:
+ void Process() override;
+ };
+
+ // -- Const settings interface
+ const BaseSettingsT& Base() const { return this->Base_; }
+ const MocSettingsT& Moc() const { return this->Moc_; }
+ const UicSettingsT& Uic() const { return this->Uic_; }
+
+ // -- Parallel job processing interface
+ cmWorkerPool& WorkerPool() { return WorkerPool_; }
+ void AbortError() { Abort(true); }
+ void AbortSuccess() { Abort(false); }
+ bool ParallelJobPushMoc(cmWorkerPool::JobHandleT&& jobHandle);
+ bool ParallelJobPushUic(cmWorkerPool::JobHandleT&& jobHandle);
+
+ // -- Mocs compilation include file updated flag
+ void ParallelMocAutoUpdated() { MocAutoFileUpdated_.store(true); }
+ bool MocAutoFileUpdated() const { return MocAutoFileUpdated_.load(); }
+
+ // -- Mocs compilation file register
+ std::string ParallelMocAutoRegister(std::string const& baseName);
+ bool ParallelMocIncluded(std::string const& sourceFile);
+ std::set<std::string> const& MocAutoFiles() const
+ {
+ return this->MocAutoFiles_;
+ }
+
+private:
+ // -- Utility accessors
+ Logger& Log() { return Logger_; }
+ FileSystem& FileSys() { return FileSys_; }
+ // -- Abstract processing interface
+ bool Init(cmMakefile* makefile) override;
+ bool Process() override;
+ // -- Settings file
+ void SettingsFileRead();
+ bool SettingsFileWrite();
+ // -- Thread processing
+ void Abort(bool error);
+ // -- Generation
+ bool CreateDirectories();
+
+private:
+ // -- Utility
+ Logger Logger_;
+ FileSystem FileSys_;
+ // -- Settings
+ BaseSettingsT Base_;
+ MocSettingsT Moc_;
+ UicSettingsT Uic_;
+ // -- Moc meta
+ std::mutex MocMetaMutex_;
+ std::set<std::string> MocIncludedFiles_;
+ IncludesMap MocIncludes_;
+ std::set<std::string> MocAutoFiles_;
+ std::atomic<bool> MocAutoFileUpdated_ = ATOMIC_VAR_INIT(false);
+ // -- Uic meta
+ std::mutex UicMetaMutex_;
+ IncludesMap UicIncludes_;
+ // -- Settings file
+ std::string SettingsFile_;
+ std::string SettingsStringMoc_;
+ std::string SettingsStringUic_;
+ // -- Thread pool and job queue
+ std::atomic<bool> JobError_ = ATOMIC_VAR_INIT(false);
+ cmWorkerPool WorkerPool_;
+};
+
+#endif
diff --git a/Source/cmStringCommand.cxx b/Source/cmStringCommand.cxx
index 252d985..998f904 100644
--- a/Source/cmStringCommand.cxx
+++ b/Source/cmStringCommand.cxx
@@ -1,9 +1,13 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
+#define _SCL_SECURE_NO_WARNINGS
+
#include "cmStringCommand.h"
#include "cmsys/RegularExpression.hxx"
+#include <algorithm>
#include <ctype.h>
+#include <iterator>
#include <memory> // IWYU pragma: keep
#include <sstream>
#include <stdio.h>
@@ -13,6 +17,7 @@
#include "cmCryptoHash.h"
#include "cmGeneratorExpression.h"
#include "cmMakefile.h"
+#include "cmMessageType.h"
#include "cmRange.h"
#include "cmStringReplaceHelper.h"
#include "cmSystemTools.h"
@@ -79,6 +84,9 @@
if (subCommand == "STRIP") {
return this->HandleStripCommand(args);
}
+ if (subCommand == "REPEAT") {
+ return this->HandleRepeatCommand(args);
+ }
if (subCommand == "RANDOM") {
return this->HandleRandomCommand(args);
}
@@ -709,6 +717,59 @@
return true;
}
+bool cmStringCommand::HandleRepeatCommand(std::vector<std::string> const& args)
+{
+ // `string(REPEAT "<str>" <times> OUTPUT_VARIABLE)`
+ enum ArgPos : std::size_t
+ {
+ SUB_COMMAND,
+ VALUE,
+ TIMES,
+ OUTPUT_VARIABLE,
+ TOTAL_ARGS
+ };
+
+ if (args.size() != ArgPos::TOTAL_ARGS) {
+ this->Makefile->IssueMessage(
+ MessageType::FATAL_ERROR,
+ "sub-command REPEAT requires three arguments.");
+ return true;
+ }
+
+ unsigned long times;
+ if (!cmSystemTools::StringToULong(args[ArgPos::TIMES].c_str(), ×)) {
+ this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
+ "repeat count is not a positive number.");
+ return true;
+ }
+
+ const auto& stringValue = args[ArgPos::VALUE];
+ const auto& variableName = args[ArgPos::OUTPUT_VARIABLE];
+ const auto inStringLength = stringValue.size();
+
+ std::string result;
+ switch (inStringLength) {
+ case 0u:
+ // Nothing to do for zero length input strings
+ break;
+ case 1u:
+ // NOTE If the string to repeat consists of the only character,
+ // use the appropriate constructor.
+ result = std::string(times, stringValue[0]);
+ break;
+ default:
+ result = std::string(inStringLength * times, char{});
+ for (auto i = 0u; i < times; ++i) {
+ std::copy(cm::cbegin(stringValue), cm::cend(stringValue),
+ &result[i * inStringLength]);
+ }
+ break;
+ }
+
+ this->Makefile->AddDefinition(variableName, result.c_str());
+ return true;
+}
+
bool cmStringCommand::HandleRandomCommand(std::vector<std::string> const& args)
{
if (args.size() < 2 || args.size() == 3 || args.size() == 5) {
diff --git a/Source/cmStringCommand.h b/Source/cmStringCommand.h
index cbff73e..acde605 100644
--- a/Source/cmStringCommand.h
+++ b/Source/cmStringCommand.h
@@ -51,6 +51,7 @@
bool HandleConcatCommand(std::vector<std::string> const& args);
bool HandleJoinCommand(std::vector<std::string> const& args);
bool HandleStripCommand(std::vector<std::string> const& args);
+ bool HandleRepeatCommand(std::vector<std::string> const& args);
bool HandleRandomCommand(std::vector<std::string> const& args);
bool HandleFindCommand(std::vector<std::string> const& args);
bool HandleTimestampCommand(std::vector<std::string> const& args);
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index dc9b6d2..9598a3f 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -304,6 +304,7 @@
InitProperty("AUTORCC_OPTIONS", nullptr);
InitProperty("LINK_DEPENDS_NO_SHARED", nullptr);
InitProperty("LINK_INTERFACE_LIBRARIES", nullptr);
+ InitProperty("MSVC_RUNTIME_LIBRARY", nullptr);
InitProperty("WIN32_EXECUTABLE", nullptr);
InitProperty("MACOSX_BUNDLE", nullptr);
InitProperty("MACOSX_RPATH", nullptr);
diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx
index 0cec2fb..c60706d 100644
--- a/Source/cmVisualStudio10TargetGenerator.cxx
+++ b/Source/cmVisualStudio10TargetGenerator.cxx
@@ -663,6 +663,7 @@
this->WriteCustomCommands(e0);
this->WriteAllSources(e0);
this->WriteDotNetReferences(e0);
+ this->WriteImports(e0);
this->WriteEmbeddedResourceGroup(e0);
this->WriteXamlFilesGroup(e0);
this->WriteWinRTReferences(e0);
@@ -811,6 +812,24 @@
this->WriteDotNetReferenceCustomTags(e2, ref);
}
+void cmVisualStudio10TargetGenerator::WriteImports(Elem& e0)
+{
+ const char* imports =
+ this->GeneratorTarget->Target->GetProperty("VS_PROJECT_IMPORT");
+ if (imports) {
+ std::vector<std::string> argsSplit;
+ cmSystemTools::ExpandListArgument(std::string(imports), argsSplit, false);
+ for (auto& path : argsSplit) {
+ if (!cmsys::SystemTools::FileIsFullPath(path)) {
+ path = this->Makefile->GetCurrentSourceDirectory() + "/" + path;
+ }
+ ConvertToWindowsSlash(path);
+ Elem e1(e0, "Import");
+ e1.Attribute("Project", path);
+ }
+ }
+}
+
void cmVisualStudio10TargetGenerator::WriteDotNetReferenceCustomTags(
Elem& e2, std::string const& ref)
{
diff --git a/Source/cmVisualStudio10TargetGenerator.h b/Source/cmVisualStudio10TargetGenerator.h
index 6a1ee1d..1dea8e9 100644
--- a/Source/cmVisualStudio10TargetGenerator.h
+++ b/Source/cmVisualStudio10TargetGenerator.h
@@ -76,6 +76,7 @@
void WriteDotNetReference(Elem& e1, std::string const& ref,
std::string const& hint,
std::string const& config);
+ void WriteImports(Elem& e0);
void WriteDotNetReferenceCustomTags(Elem& e2, std::string const& ref);
void WriteEmbeddedResourceGroup(Elem& e0);
void WriteWinRTReferences(Elem& e0);
diff --git a/Source/cmVisualStudioGeneratorOptions.cxx b/Source/cmVisualStudioGeneratorOptions.cxx
index e8b2668..e1b0c70 100644
--- a/Source/cmVisualStudioGeneratorOptions.cxx
+++ b/Source/cmVisualStudioGeneratorOptions.cxx
@@ -374,19 +374,19 @@
{
// Look for Intel Fortran flags that do not map well in the flag table.
if (this->CurrentTool == FortranCompiler) {
- if (flag == "/dbglibs") {
+ if (flag == "/dbglibs" || flag == "-dbglibs") {
this->FortranRuntimeDebug = true;
return;
}
- if (flag == "/threads") {
+ if (flag == "/threads" || flag == "-threads") {
this->FortranRuntimeMT = true;
return;
}
- if (flag == "/libs:dll") {
+ if (flag == "/libs:dll" || flag == "-libs:dll") {
this->FortranRuntimeDLL = true;
return;
}
- if (flag == "/libs:static") {
+ if (flag == "/libs:static" || flag == "-libs:static") {
this->FortranRuntimeDLL = false;
return;
}
diff --git a/Source/cmWorkerPool.cxx b/Source/cmWorkerPool.cxx
new file mode 100644
index 0000000..464182c
--- /dev/null
+++ b/Source/cmWorkerPool.cxx
@@ -0,0 +1,770 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#include "cmWorkerPool.h"
+
+#include "cmRange.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVSignalHackRAII.h" // IWYU pragma: keep
+#include "cm_uv.h"
+
+#include <algorithm>
+#include <array>
+#include <condition_variable>
+#include <deque>
+#include <functional>
+#include <mutex>
+#include <stddef.h>
+#include <thread>
+
+/**
+ * @brief libuv pipe buffer class
+ */
+class cmUVPipeBuffer
+{
+public:
+ typedef cmRange<char const*> DataRange;
+ typedef std::function<void(DataRange)> DataFunction;
+ /// On error the ssize_t argument is a non zero libuv error code
+ typedef std::function<void(ssize_t)> EndFunction;
+
+public:
+ /**
+ * Reset to construction state
+ */
+ void reset();
+
+ /**
+ * Initializes uv_pipe(), uv_stream() and uv_handle()
+ * @return true on success
+ */
+ bool init(uv_loop_t* uv_loop);
+
+ /**
+ * Start reading
+ * @return true on success
+ */
+ bool startRead(DataFunction dataFunction, EndFunction endFunction);
+
+ //! libuv pipe
+ uv_pipe_t* uv_pipe() const { return UVPipe_.get(); }
+ //! uv_pipe() casted to libuv stream
+ uv_stream_t* uv_stream() const { return static_cast<uv_stream_t*>(UVPipe_); }
+ //! uv_pipe() casted to libuv handle
+ uv_handle_t* uv_handle() { return static_cast<uv_handle_t*>(UVPipe_); }
+
+private:
+ // -- Libuv callbacks
+ static void UVAlloc(uv_handle_t* handle, size_t suggestedSize,
+ uv_buf_t* buf);
+ static void UVData(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf);
+
+private:
+ cm::uv_pipe_ptr UVPipe_;
+ std::vector<char> Buffer_;
+ DataFunction DataFunction_;
+ EndFunction EndFunction_;
+};
+
+void cmUVPipeBuffer::reset()
+{
+ if (UVPipe_.get() != nullptr) {
+ EndFunction_ = nullptr;
+ DataFunction_ = nullptr;
+ Buffer_.clear();
+ Buffer_.shrink_to_fit();
+ UVPipe_.reset();
+ }
+}
+
+bool cmUVPipeBuffer::init(uv_loop_t* uv_loop)
+{
+ reset();
+ if (uv_loop == nullptr) {
+ return false;
+ }
+ int ret = UVPipe_.init(*uv_loop, 0, this);
+ return (ret == 0);
+}
+
+bool cmUVPipeBuffer::startRead(DataFunction dataFunction,
+ EndFunction endFunction)
+{
+ if (UVPipe_.get() == nullptr) {
+ return false;
+ }
+ if (!dataFunction || !endFunction) {
+ return false;
+ }
+ DataFunction_ = std::move(dataFunction);
+ EndFunction_ = std::move(endFunction);
+ int ret = uv_read_start(uv_stream(), &cmUVPipeBuffer::UVAlloc,
+ &cmUVPipeBuffer::UVData);
+ return (ret == 0);
+}
+
+void cmUVPipeBuffer::UVAlloc(uv_handle_t* handle, size_t suggestedSize,
+ uv_buf_t* buf)
+{
+ auto& pipe = *reinterpret_cast<cmUVPipeBuffer*>(handle->data);
+ pipe.Buffer_.resize(suggestedSize);
+ buf->base = pipe.Buffer_.data();
+ buf->len = static_cast<unsigned long>(pipe.Buffer_.size());
+}
+
+void cmUVPipeBuffer::UVData(uv_stream_t* stream, ssize_t nread,
+ const uv_buf_t* buf)
+{
+ auto& pipe = *reinterpret_cast<cmUVPipeBuffer*>(stream->data);
+ if (nread > 0) {
+ if (buf->base != nullptr) {
+ // Call data function
+ pipe.DataFunction_(DataRange(buf->base, buf->base + nread));
+ }
+ } else if (nread < 0) {
+ // Save the end function on the stack before resetting the pipe
+ EndFunction efunc;
+ efunc.swap(pipe.EndFunction_);
+ // Reset pipe before calling the end function
+ pipe.reset();
+ // Call end function
+ efunc((nread == UV_EOF) ? 0 : nread);
+ }
+}
+
+/**
+ * @brief External process management class
+ */
+class cmUVReadOnlyProcess
+{
+public:
+ // -- Types
+ //! @brief Process settings
+ struct SetupT
+ {
+ std::string WorkingDirectory;
+ std::vector<std::string> Command;
+ cmWorkerPool::ProcessResultT* Result = nullptr;
+ bool MergedOutput = false;
+ };
+
+public:
+ // -- Const accessors
+ SetupT const& Setup() const { return Setup_; }
+ cmWorkerPool::ProcessResultT* Result() const { return Setup_.Result; }
+ bool IsStarted() const { return IsStarted_; }
+ bool IsFinished() const { return IsFinished_; }
+
+ // -- Runtime
+ void setup(cmWorkerPool::ProcessResultT* result, bool mergedOutput,
+ std::vector<std::string> const& command,
+ std::string const& workingDirectory = std::string());
+ bool start(uv_loop_t* uv_loop, std::function<void()> finishedCallback);
+
+private:
+ // -- Libuv callbacks
+ static void UVExit(uv_process_t* handle, int64_t exitStatus, int termSignal);
+ void UVPipeOutData(cmUVPipeBuffer::DataRange data);
+ void UVPipeOutEnd(ssize_t error);
+ void UVPipeErrData(cmUVPipeBuffer::DataRange data);
+ void UVPipeErrEnd(ssize_t error);
+ void UVTryFinish();
+
+private:
+ // -- Setup
+ SetupT Setup_;
+ // -- Runtime
+ bool IsStarted_ = false;
+ bool IsFinished_ = false;
+ std::function<void()> FinishedCallback_;
+ std::vector<const char*> CommandPtr_;
+ std::array<uv_stdio_container_t, 3> UVOptionsStdIO_;
+ uv_process_options_t UVOptions_;
+ cm::uv_process_ptr UVProcess_;
+ cmUVPipeBuffer UVPipeOut_;
+ cmUVPipeBuffer UVPipeErr_;
+};
+
+void cmUVReadOnlyProcess::setup(cmWorkerPool::ProcessResultT* result,
+ bool mergedOutput,
+ std::vector<std::string> const& command,
+ std::string const& workingDirectory)
+{
+ Setup_.WorkingDirectory = workingDirectory;
+ Setup_.Command = command;
+ Setup_.Result = result;
+ Setup_.MergedOutput = mergedOutput;
+}
+
+bool cmUVReadOnlyProcess::start(uv_loop_t* uv_loop,
+ std::function<void()> finishedCallback)
+{
+ if (IsStarted() || (Result() == nullptr)) {
+ return false;
+ }
+
+ // Reset result before the start
+ Result()->reset();
+
+ // Fill command string pointers
+ if (!Setup().Command.empty()) {
+ CommandPtr_.reserve(Setup().Command.size() + 1);
+ for (std::string const& arg : Setup().Command) {
+ CommandPtr_.push_back(arg.c_str());
+ }
+ CommandPtr_.push_back(nullptr);
+ } else {
+ Result()->ErrorMessage = "Empty command";
+ }
+
+ if (!Result()->error()) {
+ if (!UVPipeOut_.init(uv_loop)) {
+ Result()->ErrorMessage = "libuv stdout pipe initialization failed";
+ }
+ }
+ if (!Result()->error()) {
+ if (!UVPipeErr_.init(uv_loop)) {
+ Result()->ErrorMessage = "libuv stderr pipe initialization failed";
+ }
+ }
+ if (!Result()->error()) {
+ // -- Setup process stdio options
+ // stdin
+ UVOptionsStdIO_[0].flags = UV_IGNORE;
+ UVOptionsStdIO_[0].data.stream = nullptr;
+ // stdout
+ UVOptionsStdIO_[1].flags =
+ static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
+ UVOptionsStdIO_[1].data.stream = UVPipeOut_.uv_stream();
+ // stderr
+ UVOptionsStdIO_[2].flags =
+ static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
+ UVOptionsStdIO_[2].data.stream = UVPipeErr_.uv_stream();
+
+ // -- Setup process options
+ std::fill_n(reinterpret_cast<char*>(&UVOptions_), sizeof(UVOptions_), 0);
+ UVOptions_.exit_cb = &cmUVReadOnlyProcess::UVExit;
+ UVOptions_.file = CommandPtr_[0];
+ UVOptions_.args = const_cast<char**>(CommandPtr_.data());
+ UVOptions_.cwd = Setup_.WorkingDirectory.c_str();
+ UVOptions_.flags = UV_PROCESS_WINDOWS_HIDE;
+ UVOptions_.stdio_count = static_cast<int>(UVOptionsStdIO_.size());
+ UVOptions_.stdio = UVOptionsStdIO_.data();
+
+ // -- Spawn process
+ int uvErrorCode = UVProcess_.spawn(*uv_loop, UVOptions_, this);
+ if (uvErrorCode != 0) {
+ Result()->ErrorMessage = "libuv process spawn failed";
+ if (const char* uvErr = uv_strerror(uvErrorCode)) {
+ Result()->ErrorMessage += ": ";
+ Result()->ErrorMessage += uvErr;
+ }
+ }
+ }
+ // -- Start reading from stdio streams
+ if (!Result()->error()) {
+ if (!UVPipeOut_.startRead(
+ [this](cmUVPipeBuffer::DataRange range) {
+ this->UVPipeOutData(range);
+ },
+ [this](ssize_t error) { this->UVPipeOutEnd(error); })) {
+ Result()->ErrorMessage = "libuv start reading from stdout pipe failed";
+ }
+ }
+ if (!Result()->error()) {
+ if (!UVPipeErr_.startRead(
+ [this](cmUVPipeBuffer::DataRange range) {
+ this->UVPipeErrData(range);
+ },
+ [this](ssize_t error) { this->UVPipeErrEnd(error); })) {
+ Result()->ErrorMessage = "libuv start reading from stderr pipe failed";
+ }
+ }
+
+ if (!Result()->error()) {
+ IsStarted_ = true;
+ FinishedCallback_ = std::move(finishedCallback);
+ } else {
+ // Clear libuv handles and finish
+ UVProcess_.reset();
+ UVPipeOut_.reset();
+ UVPipeErr_.reset();
+ CommandPtr_.clear();
+ }
+
+ return IsStarted();
+}
+
+void cmUVReadOnlyProcess::UVExit(uv_process_t* handle, int64_t exitStatus,
+ int termSignal)
+{
+ auto& proc = *reinterpret_cast<cmUVReadOnlyProcess*>(handle->data);
+ if (proc.IsStarted() && !proc.IsFinished()) {
+ // Set error message on demand
+ proc.Result()->ExitStatus = exitStatus;
+ proc.Result()->TermSignal = termSignal;
+ if (!proc.Result()->error()) {
+ if (termSignal != 0) {
+ proc.Result()->ErrorMessage = "Process was terminated by signal ";
+ proc.Result()->ErrorMessage +=
+ std::to_string(proc.Result()->TermSignal);
+ } else if (exitStatus != 0) {
+ proc.Result()->ErrorMessage = "Process failed with return value ";
+ proc.Result()->ErrorMessage +=
+ std::to_string(proc.Result()->ExitStatus);
+ }
+ }
+
+ // Reset process handle
+ proc.UVProcess_.reset();
+ // Try finish
+ proc.UVTryFinish();
+ }
+}
+
+void cmUVReadOnlyProcess::UVPipeOutData(cmUVPipeBuffer::DataRange data)
+{
+ Result()->StdOut.append(data.begin(), data.end());
+}
+
+void cmUVReadOnlyProcess::UVPipeOutEnd(ssize_t error)
+{
+ // Process pipe error
+ if ((error != 0) && !Result()->error()) {
+ Result()->ErrorMessage =
+ "Reading from stdout pipe failed with libuv error code ";
+ Result()->ErrorMessage += std::to_string(error);
+ }
+ // Try finish
+ UVTryFinish();
+}
+
+void cmUVReadOnlyProcess::UVPipeErrData(cmUVPipeBuffer::DataRange data)
+{
+ std::string* str =
+ Setup_.MergedOutput ? &Result()->StdOut : &Result()->StdErr;
+ str->append(data.begin(), data.end());
+}
+
+void cmUVReadOnlyProcess::UVPipeErrEnd(ssize_t error)
+{
+ // Process pipe error
+ if ((error != 0) && !Result()->error()) {
+ Result()->ErrorMessage =
+ "Reading from stderr pipe failed with libuv error code ";
+ Result()->ErrorMessage += std::to_string(error);
+ }
+ // Try finish
+ UVTryFinish();
+}
+
+void cmUVReadOnlyProcess::UVTryFinish()
+{
+ // There still might be data in the pipes after the process has finished.
+ // Therefore check if the process is finished AND all pipes are closed
+ // before signaling the worker thread to continue.
+ if ((UVProcess_.get() != nullptr) || (UVPipeOut_.uv_pipe() != nullptr) ||
+ (UVPipeErr_.uv_pipe() != nullptr)) {
+ return;
+ }
+ IsFinished_ = true;
+ FinishedCallback_();
+}
+
+/**
+ * @brief Private worker pool internals
+ */
+class cmWorkerPoolInternal
+{
+public:
+ // -- Types
+
+ /**
+ * @brief Worker thread
+ */
+ class WorkerT
+ {
+ public:
+ WorkerT(unsigned int index);
+ ~WorkerT();
+
+ WorkerT(WorkerT const&) = delete;
+ WorkerT& operator=(WorkerT const&) = delete;
+
+ /**
+ * Start the thread
+ */
+ void Start(cmWorkerPoolInternal* internal);
+
+ /**
+ * @brief Run an external process
+ */
+ bool RunProcess(cmWorkerPool::ProcessResultT& result,
+ std::vector<std::string> const& command,
+ std::string const& workingDirectory);
+
+ // -- Accessors
+ unsigned int Index() const { return Index_; }
+ cmWorkerPool::JobHandleT& JobHandle() { return JobHandle_; }
+
+ private:
+ // -- Libuv callbacks
+ static void UVProcessStart(uv_async_t* handle);
+ void UVProcessFinished();
+
+ private:
+ //! @brief Job handle
+ cmWorkerPool::JobHandleT JobHandle_;
+ //! @brief Worker index
+ unsigned int Index_;
+ // -- Process management
+ struct
+ {
+ std::mutex Mutex;
+ cm::uv_async_ptr Request;
+ std::condition_variable Condition;
+ std::unique_ptr<cmUVReadOnlyProcess> ROP;
+ } Proc_;
+ // -- System thread
+ std::thread Thread_;
+ };
+
+public:
+ // -- Constructors
+ cmWorkerPoolInternal(cmWorkerPool* pool);
+ ~cmWorkerPoolInternal();
+
+ /**
+ * @brief Runs the libuv loop
+ */
+ bool Process();
+
+ /**
+ * @brief Clear queue and abort threads
+ */
+ void Abort();
+
+ /**
+ * @brief Push a job to the queue and notify a worker
+ */
+ bool PushJob(cmWorkerPool::JobHandleT&& jobHandle);
+
+ /**
+ * @brief Worker thread main loop method
+ */
+ void Work(WorkerT* worker);
+
+ // -- Request slots
+ static void UVSlotBegin(uv_async_t* handle);
+ static void UVSlotEnd(uv_async_t* handle);
+
+public:
+ // -- UV loop
+#ifdef CMAKE_UV_SIGNAL_HACK
+ std::unique_ptr<cmUVSignalHackRAII> UVHackRAII;
+#endif
+ std::unique_ptr<uv_loop_t> UVLoop;
+ cm::uv_async_ptr UVRequestBegin;
+ cm::uv_async_ptr UVRequestEnd;
+
+ // -- Thread pool and job queue
+ std::mutex Mutex;
+ bool Aborting = false;
+ bool FenceProcessing = false;
+ unsigned int WorkersRunning = 0;
+ unsigned int WorkersIdle = 0;
+ unsigned int JobsProcessing = 0;
+ std::deque<cmWorkerPool::JobHandleT> Queue;
+ std::condition_variable Condition;
+ std::vector<std::unique_ptr<WorkerT>> Workers;
+
+ // -- References
+ cmWorkerPool* Pool = nullptr;
+};
+
+cmWorkerPoolInternal::WorkerT::WorkerT(unsigned int index)
+ : Index_(index)
+{
+}
+
+cmWorkerPoolInternal::WorkerT::~WorkerT()
+{
+ if (Thread_.joinable()) {
+ Thread_.join();
+ }
+}
+
+void cmWorkerPoolInternal::WorkerT::Start(cmWorkerPoolInternal* internal)
+{
+ Proc_.Request.init(*(internal->UVLoop), &WorkerT::UVProcessStart, this);
+ Thread_ = std::thread(&cmWorkerPoolInternal::Work, internal, this);
+}
+
+bool cmWorkerPoolInternal::WorkerT::RunProcess(
+ cmWorkerPool::ProcessResultT& result,
+ std::vector<std::string> const& command, std::string const& workingDirectory)
+{
+ if (command.empty()) {
+ return false;
+ }
+ // Create process instance
+ {
+ std::lock_guard<std::mutex> lock(Proc_.Mutex);
+ Proc_.ROP = cm::make_unique<cmUVReadOnlyProcess>();
+ Proc_.ROP->setup(&result, true, command, workingDirectory);
+ }
+ // Send asynchronous process start request to libuv loop
+ Proc_.Request.send();
+ // Wait until the process has been finished and destroyed
+ {
+ std::unique_lock<std::mutex> ulock(Proc_.Mutex);
+ while (Proc_.ROP) {
+ Proc_.Condition.wait(ulock);
+ }
+ }
+ return !result.error();
+}
+
+void cmWorkerPoolInternal::WorkerT::UVProcessStart(uv_async_t* handle)
+{
+ auto* wrk = reinterpret_cast<WorkerT*>(handle->data);
+ bool startFailed = false;
+ {
+ auto& Proc = wrk->Proc_;
+ std::lock_guard<std::mutex> lock(Proc.Mutex);
+ if (Proc.ROP && !Proc.ROP->IsStarted()) {
+ startFailed =
+ !Proc.ROP->start(handle->loop, [wrk] { wrk->UVProcessFinished(); });
+ }
+ }
+ // Clean up if starting of the process failed
+ if (startFailed) {
+ wrk->UVProcessFinished();
+ }
+}
+
+void cmWorkerPoolInternal::WorkerT::UVProcessFinished()
+{
+ {
+ std::lock_guard<std::mutex> lock(Proc_.Mutex);
+ if (Proc_.ROP && (Proc_.ROP->IsFinished() || !Proc_.ROP->IsStarted())) {
+ Proc_.ROP.reset();
+ }
+ }
+ // Notify idling thread
+ Proc_.Condition.notify_one();
+}
+
+void cmWorkerPool::ProcessResultT::reset()
+{
+ ExitStatus = 0;
+ TermSignal = 0;
+ if (!StdOut.empty()) {
+ StdOut.clear();
+ StdOut.shrink_to_fit();
+ }
+ if (!StdErr.empty()) {
+ StdErr.clear();
+ StdErr.shrink_to_fit();
+ }
+ if (!ErrorMessage.empty()) {
+ ErrorMessage.clear();
+ ErrorMessage.shrink_to_fit();
+ }
+}
+
+cmWorkerPoolInternal::cmWorkerPoolInternal(cmWorkerPool* pool)
+ : Pool(pool)
+{
+ // Initialize libuv loop
+ uv_disable_stdio_inheritance();
+#ifdef CMAKE_UV_SIGNAL_HACK
+ UVHackRAII = cm::make_unique<cmUVSignalHackRAII>();
+#endif
+ UVLoop = cm::make_unique<uv_loop_t>();
+ uv_loop_init(UVLoop.get());
+}
+
+cmWorkerPoolInternal::~cmWorkerPoolInternal()
+{
+ uv_loop_close(UVLoop.get());
+}
+
+bool cmWorkerPoolInternal::Process()
+{
+ // Reset state
+ Aborting = false;
+ // Initialize libuv asynchronous request
+ UVRequestBegin.init(*UVLoop, &cmWorkerPoolInternal::UVSlotBegin, this);
+ UVRequestEnd.init(*UVLoop, &cmWorkerPoolInternal::UVSlotEnd, this);
+ // Send begin request
+ UVRequestBegin.send();
+ // Run libuv loop
+ return (uv_run(UVLoop.get(), UV_RUN_DEFAULT) == 0);
+}
+
+void cmWorkerPoolInternal::Abort()
+{
+ bool firstCall = false;
+ // Clear all jobs and set abort flag
+ {
+ std::lock_guard<std::mutex> guard(Mutex);
+ if (!Aborting) {
+ // Register abort and clear queue
+ Aborting = true;
+ Queue.clear();
+ firstCall = true;
+ }
+ }
+ if (firstCall) {
+ // Wake threads
+ Condition.notify_all();
+ }
+}
+
+inline bool cmWorkerPoolInternal::PushJob(cmWorkerPool::JobHandleT&& jobHandle)
+{
+ std::lock_guard<std::mutex> guard(Mutex);
+ if (Aborting) {
+ return false;
+ }
+
+ // Append the job to the queue
+ Queue.emplace_back(std::move(jobHandle));
+
+ // Notify an idle worker if there's one
+ if (WorkersIdle != 0) {
+ Condition.notify_one();
+ }
+
+ return true;
+}
+
+void cmWorkerPoolInternal::UVSlotBegin(uv_async_t* handle)
+{
+ auto& gint = *reinterpret_cast<cmWorkerPoolInternal*>(handle->data);
+ // Create worker threads
+ {
+ unsigned int const num = gint.Pool->ThreadCount();
+ // Create workers
+ gint.Workers.reserve(num);
+ for (unsigned int ii = 0; ii != num; ++ii) {
+ gint.Workers.emplace_back(cm::make_unique<WorkerT>(ii));
+ }
+ // Start workers
+ for (auto& wrk : gint.Workers) {
+ wrk->Start(&gint);
+ }
+ }
+ // Destroy begin request
+ gint.UVRequestBegin.reset();
+}
+
+void cmWorkerPoolInternal::UVSlotEnd(uv_async_t* handle)
+{
+ auto& gint = *reinterpret_cast<cmWorkerPoolInternal*>(handle->data);
+ // Join and destroy worker threads
+ gint.Workers.clear();
+ // Destroy end request
+ gint.UVRequestEnd.reset();
+}
+
+void cmWorkerPoolInternal::Work(WorkerT* worker)
+{
+ std::unique_lock<std::mutex> uLock(Mutex);
+ // Increment running workers count
+ ++WorkersRunning;
+ // Enter worker main loop
+ while (true) {
+ // Abort on request
+ if (Aborting) {
+ break;
+ }
+ // Wait for new jobs
+ if (Queue.empty()) {
+ ++WorkersIdle;
+ Condition.wait(uLock);
+ --WorkersIdle;
+ continue;
+ }
+
+ // Check for fence jobs
+ if (FenceProcessing || Queue.front()->IsFence()) {
+ if (JobsProcessing != 0) {
+ Condition.wait(uLock);
+ continue;
+ }
+ // No jobs get processed. Set the fence job processing flag.
+ FenceProcessing = true;
+ }
+
+ // Pop next job from queue
+ worker->JobHandle() = std::move(Queue.front());
+ Queue.pop_front();
+
+ // Unlocked scope for job processing
+ ++JobsProcessing;
+ {
+ uLock.unlock();
+ worker->JobHandle()->Work(Pool, worker->Index()); // Process job
+ worker->JobHandle().reset(); // Destroy job
+ uLock.lock();
+ }
+ --JobsProcessing;
+
+ // Was this a fence job?
+ if (FenceProcessing) {
+ FenceProcessing = false;
+ Condition.notify_all();
+ }
+ }
+
+ // Decrement running workers count
+ if (--WorkersRunning == 0) {
+ // Last worker thread about to finish. Send libuv event.
+ UVRequestEnd.send();
+ }
+}
+
+cmWorkerPool::JobT::~JobT() = default;
+
+bool cmWorkerPool::JobT::RunProcess(ProcessResultT& result,
+ std::vector<std::string> const& command,
+ std::string const& workingDirectory)
+{
+ // Get worker by index
+ auto* wrk = Pool_->Int_->Workers.at(WorkerIndex_).get();
+ return wrk->RunProcess(result, command, workingDirectory);
+}
+
+cmWorkerPool::cmWorkerPool()
+ : Int_(cm::make_unique<cmWorkerPoolInternal>(this))
+{
+}
+
+cmWorkerPool::~cmWorkerPool() = default;
+
+bool cmWorkerPool::Process(unsigned int threadCount, void* userData)
+{
+ // Setup user data
+ UserData_ = userData;
+ ThreadCount_ = (threadCount > 0) ? threadCount : 1u;
+
+ // Run libuv loop
+ bool success = Int_->Process();
+
+ // Clear user data
+ UserData_ = nullptr;
+ ThreadCount_ = 0;
+
+ return success;
+}
+
+bool cmWorkerPool::PushJob(JobHandleT&& jobHandle)
+{
+ return Int_->PushJob(std::move(jobHandle));
+}
+
+void cmWorkerPool::Abort()
+{
+ Int_->Abort();
+}
diff --git a/Source/cmWorkerPool.h b/Source/cmWorkerPool.h
new file mode 100644
index 0000000..71c7d84
--- /dev/null
+++ b/Source/cmWorkerPool.h
@@ -0,0 +1,219 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+#ifndef cmWorkerPool_h
+#define cmWorkerPool_h
+
+#include "cmConfigure.h" // IWYU pragma: keep
+
+#include "cmAlgorithms.h" // IWYU pragma: keep
+
+#include <memory> // IWYU pragma: keep
+#include <stdint.h>
+#include <string>
+#include <utility>
+#include <vector>
+
+// -- Types
+class cmWorkerPoolInternal;
+
+/** @class cmWorkerPool
+ * @brief Thread pool with job queue
+ */
+class cmWorkerPool
+{
+public:
+ /**
+ * Return value and output of an external process.
+ */
+ struct ProcessResultT
+ {
+ void reset();
+ bool error() const
+ {
+ return (ExitStatus != 0) || (TermSignal != 0) || !ErrorMessage.empty();
+ }
+
+ std::int64_t ExitStatus = 0;
+ int TermSignal = 0;
+ std::string StdOut;
+ std::string StdErr;
+ std::string ErrorMessage;
+ };
+
+ /**
+ * Abstract job class for concurrent job processing.
+ */
+ class JobT
+ {
+ public:
+ JobT(JobT const&) = delete;
+ JobT& operator=(JobT const&) = delete;
+
+ /**
+ * @brief Virtual destructor.
+ */
+ virtual ~JobT();
+
+ /**
+ * @brief Fence job flag
+ *
+ * Fence jobs require that:
+ * - all jobs before in the queue have been processed
+ * - no jobs later in the queue will be processed before this job was
+ * processed
+ */
+ bool IsFence() const { return Fence_; }
+
+ protected:
+ /**
+ * @brief Protected default constructor
+ */
+ JobT(bool fence = false)
+ : Fence_(fence)
+ {
+ }
+
+ /**
+ * Abstract processing interface that must be implement in derived classes.
+ */
+ virtual void Process() = 0;
+
+ /**
+ * Get the worker pool.
+ * Only valid during the JobT::Process() call!
+ */
+ cmWorkerPool* Pool() const { return Pool_; }
+
+ /**
+ * Get the user data.
+ * Only valid during the JobT::Process() call!
+ */
+ void* UserData() const { return Pool_->UserData(); };
+
+ /**
+ * Get the worker index.
+ * This is the index of the thread processing this job and is in the range
+ * [0..ThreadCount).
+ * Concurrently processing jobs will never have the same WorkerIndex().
+ * Only valid during the JobT::Process() call!
+ */
+ unsigned int WorkerIndex() const { return WorkerIndex_; }
+
+ /**
+ * Run an external read only process.
+ * Use only during JobT::Process() call!
+ */
+ bool RunProcess(ProcessResultT& result,
+ std::vector<std::string> const& command,
+ std::string const& workingDirectory);
+
+ private:
+ //! Needs access to Work()
+ friend class cmWorkerPoolInternal;
+ //! Worker thread entry method.
+ void Work(cmWorkerPool* pool, unsigned int workerIndex)
+ {
+ Pool_ = pool;
+ WorkerIndex_ = workerIndex;
+ this->Process();
+ }
+
+ private:
+ cmWorkerPool* Pool_ = nullptr;
+ unsigned int WorkerIndex_ = 0;
+ bool Fence_ = false;
+ };
+
+ /**
+ * @brief Job handle type
+ */
+ typedef std::unique_ptr<JobT> JobHandleT;
+
+ /**
+ * @brief Fence job base class
+ */
+ class JobFenceT : public JobT
+ {
+ public:
+ JobFenceT()
+ : JobT(true)
+ {
+ }
+ //! Does nothing
+ void Process() override{};
+ };
+
+ /**
+ * @brief Fence job that aborts the worker pool.
+ * This class is useful as the last job in the job queue.
+ */
+ class JobEndT : JobFenceT
+ {
+ public:
+ //! Does nothing
+ void Process() override { Pool()->Abort(); }
+ };
+
+public:
+ // -- Methods
+ cmWorkerPool();
+ ~cmWorkerPool();
+
+ /**
+ * @brief Blocking function that starts threads to process all Jobs in
+ * the queue.
+ *
+ * This method blocks until a job calls the Abort() method.
+ * @arg threadCount Number of threads to process jobs.
+ * @arg userData Common user data pointer available in all Jobs.
+ */
+ bool Process(unsigned int threadCount, void* userData = nullptr);
+
+ /**
+ * Number of worker threads passed to Process().
+ * Only valid during Process().
+ */
+ unsigned int ThreadCount() const { return ThreadCount_; }
+
+ /**
+ * User data reference passed to Process().
+ * Only valid during Process().
+ */
+ void* UserData() const { return UserData_; }
+
+ // -- Job processing interface
+
+ /**
+ * @brief Clears the job queue and aborts all worker threads.
+ *
+ * This method is thread safe and can be called from inside a job.
+ */
+ void Abort();
+
+ /**
+ * @brief Push job to the queue.
+ *
+ * This method is thread safe and can be called from inside a job or before
+ * Process().
+ */
+ bool PushJob(JobHandleT&& jobHandle);
+
+ /**
+ * @brief Push job to the queue
+ *
+ * This method is thread safe and can be called from inside a job or before
+ * Process().
+ */
+ template <class T, typename... Args>
+ bool EmplaceJob(Args&&... args)
+ {
+ return PushJob(cm::make_unique<T>(std::forward<Args>(args)...));
+ }
+
+private:
+ void* UserData_ = nullptr;
+ unsigned int ThreadCount_ = 0;
+ std::unique_ptr<cmWorkerPoolInternal> Int_;
+};
+
+#endif
diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx
index c18c256..3c75957 100644
--- a/Source/cmcmd.cxx
+++ b/Source/cmcmd.cxx
@@ -7,7 +7,7 @@
#include "cmGlobalGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
-#include "cmQtAutoGeneratorMocUic.h"
+#include "cmQtAutoMocUic.h"
#include "cmQtAutoRcc.h"
#include "cmRange.h"
#include "cmState.h"
@@ -1018,7 +1018,7 @@
#ifdef CMAKE_BUILD_WITH_CMAKE
if ((args[1] == "cmake_autogen") && (args.size() >= 4)) {
- cmQtAutoGeneratorMocUic autoGen;
+ cmQtAutoMocUic autoGen;
std::string const& infoDir = args[2];
std::string const& config = args[3];
return autoGen.Run(infoDir, config) ? 0 : 1;
diff --git a/Source/kwsys/CMakeLists.txt b/Source/kwsys/CMakeLists.txt
index db4ef90..1302c64 100644
--- a/Source/kwsys/CMakeLists.txt
+++ b/Source/kwsys/CMakeLists.txt
@@ -465,6 +465,14 @@
ENDIF()
IF(KWSYS_USE_SystemTools)
+ if (NOT DEFINED KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP)
+ set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 1)
+ endif ()
+ if (KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP)
+ set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 1)
+ else ()
+ set(KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP 0)
+ endif ()
KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_SETENV
"Checking whether CXX compiler has setenv" DIRECT)
KWSYS_PLATFORM_CXX_TEST(KWSYS_CXX_HAS_UNSETENV
diff --git a/Source/kwsys/Configure.hxx.in b/Source/kwsys/Configure.hxx.in
index d1e7464..92ffea3 100644
--- a/Source/kwsys/Configure.hxx.in
+++ b/Source/kwsys/Configure.hxx.in
@@ -11,6 +11,9 @@
/* Whether <ext/stdio_filebuf.h> is available. */
#define @KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H \
@KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H@
+/* Whether the translation map is available or not. */
+#define @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP \
+ @KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP@
#if defined(__SUNPRO_CC) && __SUNPRO_CC > 0x5130 && defined(__has_attribute)
# define @KWSYS_NAMESPACE@__has_cpp_attribute(x) __has_attribute(x)
@@ -56,6 +59,8 @@
@KWSYS_NAMESPACE@_CXX_HAS_EXT_STDIO_FILEBUF_H
# define KWSYS_FALLTHROUGH @KWSYS_NAMESPACE@_FALLTHROUGH
# define KWSYS_NULLPTR @KWSYS_NAMESPACE@_NULLPTR
+# define KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP \
+ @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP
#endif
#endif
diff --git a/Source/kwsys/SystemTools.cxx b/Source/kwsys/SystemTools.cxx
index 87da80e..e756cdc 100644
--- a/Source/kwsys/SystemTools.cxx
+++ b/Source/kwsys/SystemTools.cxx
@@ -453,11 +453,13 @@
{
public:
typedef std::map<std::string, std::string> StringMap;
+#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
/**
* Path translation table from dir to refdir
* Each time 'dir' will be found it will be replace by 'refdir'
*/
StringMap TranslationMap;
+#endif
#ifdef _WIN32
static std::string GetCasePathName(std::string const& pathIn);
static std::string GetActualCaseForPathCached(std::string const& path);
@@ -623,7 +625,9 @@
std::string env;
if (SystemTools::GetEnv(key, env)) {
std::string& menv = SystemTools::Statics->EnvMap[key];
- menv = std::move(env);
+ if (menv != env) {
+ menv = std::move(env);
+ }
return menv.c_str();
}
return KWSYS_NULLPTR;
@@ -2810,27 +2814,15 @@
SystemTools::GetPath(path);
}
// now add the additional paths
- {
- for (std::vector<std::string>::const_iterator i = userPaths.begin();
- i != userPaths.end(); ++i) {
- path.push_back(*i);
- }
- }
- // Add a trailing slash to all paths to aid the search process.
- {
- for (std::vector<std::string>::iterator i = path.begin(); i != path.end();
- ++i) {
- std::string& p = *i;
- if (p.empty() || p.back() != '/') {
- p += "/";
- }
- }
- }
+ path.reserve(path.size() + userPaths.size());
+ path.insert(path.end(), userPaths.begin(), userPaths.end());
// now look for the file
std::string tryPath;
- for (std::vector<std::string>::const_iterator p = path.begin();
- p != path.end(); ++p) {
- tryPath = *p;
+ for (std::string const& p : path) {
+ tryPath = p;
+ if (tryPath.empty() || tryPath.back() != '/') {
+ tryPath += '/';
+ }
tryPath += name;
if (SystemTools::FileExists(tryPath)) {
return tryPath;
@@ -2904,14 +2896,13 @@
// the end of it
// on windows try .com then .exe
if (name.size() <= 3 || name[name.size() - 4] != '.') {
- extensions.push_back(".com");
- extensions.push_back(".exe");
+ extensions.emplace_back(".com");
+ extensions.emplace_back(".exe");
// first try with extensions if the os supports them
- for (std::vector<std::string>::iterator i = extensions.begin();
- i != extensions.end(); ++i) {
+ for (std::string const& ext : extensions) {
tryPath = name;
- tryPath += *i;
+ tryPath += ext;
if (SystemTools::FileExists(tryPath, true)) {
return SystemTools::CollapseFullPath(tryPath);
}
@@ -2930,43 +2921,33 @@
SystemTools::GetPath(path);
}
// now add the additional paths
- {
- for (std::vector<std::string>::const_iterator i = userPaths.begin();
- i != userPaths.end(); ++i) {
- path.push_back(*i);
- }
- }
+ path.reserve(path.size() + userPaths.size());
+ path.insert(path.end(), userPaths.begin(), userPaths.end());
// Add a trailing slash to all paths to aid the search process.
- {
- for (std::vector<std::string>::iterator i = path.begin(); i != path.end();
- ++i) {
- std::string& p = *i;
- if (p.empty() || p.back() != '/') {
- p += "/";
- }
+ for (std::string& p : path) {
+ if (p.empty() || p.back() != '/') {
+ p += '/';
}
}
// Try each path
- for (std::vector<std::string>::iterator p = path.begin(); p != path.end();
- ++p) {
+ for (std::string& p : path) {
#ifdef _WIN32
// Remove double quotes from the path on windows
- SystemTools::ReplaceString(*p, "\"", "");
+ SystemTools::ReplaceString(p, "\"", "");
#endif
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__)
// first try with extensions
- for (std::vector<std::string>::iterator ext = extensions.begin();
- ext != extensions.end(); ++ext) {
- tryPath = *p;
+ for (std::string const& ext : extensions) {
+ tryPath = p;
tryPath += name;
- tryPath += *ext;
+ tryPath += ext;
if (SystemTools::FileExists(tryPath, true)) {
return SystemTools::CollapseFullPath(tryPath);
}
}
#endif
// now try it without them
- tryPath = *p;
+ tryPath = p;
tryPath += name;
if (SystemTools::FileExists(tryPath, true)) {
return SystemTools::CollapseFullPath(tryPath);
@@ -2980,10 +2961,9 @@
const std::vector<std::string>& path,
bool noSystemPath)
{
- for (std::vector<std::string>::const_iterator it = names.begin();
- it != names.end(); ++it) {
+ for (std::string const& name : names) {
// Try to find the program.
- std::string result = SystemTools::FindProgram(*it, path, noSystemPath);
+ std::string result = SystemTools::FindProgram(name, path, noSystemPath);
if (!result.empty()) {
return result;
}
@@ -3008,27 +2988,18 @@
std::vector<std::string> path;
SystemTools::GetPath(path);
// now add the additional paths
- {
- for (std::vector<std::string>::const_iterator i = userPaths.begin();
- i != userPaths.end(); ++i) {
- path.push_back(*i);
- }
- }
+ path.reserve(path.size() + userPaths.size());
+ path.insert(path.end(), userPaths.begin(), userPaths.end());
// Add a trailing slash to all paths to aid the search process.
- {
- for (std::vector<std::string>::iterator i = path.begin(); i != path.end();
- ++i) {
- std::string& p = *i;
- if (p.empty() || p.back() != '/') {
- p += "/";
- }
+ for (std::string& p : path) {
+ if (p.empty() || p.back() != '/') {
+ p += '/';
}
}
std::string tryPath;
- for (std::vector<std::string>::const_iterator p = path.begin();
- p != path.end(); ++p) {
+ for (std::string const& p : path) {
#if defined(__APPLE__)
- tryPath = *p;
+ tryPath = p;
tryPath += name;
tryPath += ".framework";
if (SystemTools::FileIsDirectory(tryPath)) {
@@ -3036,42 +3007,42 @@
}
#endif
#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
- tryPath = *p;
+ tryPath = p;
tryPath += name;
tryPath += ".lib";
if (SystemTools::FileExists(tryPath, true)) {
return SystemTools::CollapseFullPath(tryPath);
}
#else
- tryPath = *p;
+ tryPath = p;
tryPath += "lib";
tryPath += name;
tryPath += ".so";
if (SystemTools::FileExists(tryPath, true)) {
return SystemTools::CollapseFullPath(tryPath);
}
- tryPath = *p;
+ tryPath = p;
tryPath += "lib";
tryPath += name;
tryPath += ".a";
if (SystemTools::FileExists(tryPath, true)) {
return SystemTools::CollapseFullPath(tryPath);
}
- tryPath = *p;
+ tryPath = p;
tryPath += "lib";
tryPath += name;
tryPath += ".sl";
if (SystemTools::FileExists(tryPath, true)) {
return SystemTools::CollapseFullPath(tryPath);
}
- tryPath = *p;
+ tryPath = p;
tryPath += "lib";
tryPath += name;
tryPath += ".dylib";
if (SystemTools::FileExists(tryPath, true)) {
return SystemTools::CollapseFullPath(tryPath);
}
- tryPath = *p;
+ tryPath = p;
tryPath += "lib";
tryPath += name;
tryPath += ".dll";
@@ -3333,9 +3304,8 @@
msg << " argv[0] = \"" << argv0 << "\"\n";
}
msg << " Attempted paths:\n";
- std::vector<std::string>::iterator i;
- for (i = failures.begin(); i != failures.end(); ++i) {
- msg << " \"" << *i << "\"\n";
+ for (std::string const& ff : failures) {
+ msg << " \"" << ff << "\"\n";
}
errorMsg = msg.str();
return false;
@@ -3349,6 +3319,7 @@
return SystemTools::CollapseFullPath(in_relative, KWSYS_NULLPTR);
}
+#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
void SystemTools::AddTranslationPath(const std::string& a,
const std::string& b)
{
@@ -3412,6 +3383,7 @@
// Remove the trailing slash we added before.
path.pop_back();
}
+#endif
static void SystemToolsAppendComponents(
std::vector<std::string>& out_components,
@@ -3482,6 +3454,7 @@
// Transform the path back to a string.
std::string newPath = SystemTools::JoinPath(out_components);
+#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
// Update the translation table with this potentially new path. I am not
// sure why this line is here, it seems really questionable, but yet I
// would put good money that if I remove it something will break, basically
@@ -3497,6 +3470,7 @@
// SystemTools::AddTranslationPath(newPath, in_path);
SystemTools::CheckTranslationPath(newPath);
+#endif
#ifdef _WIN32
newPath = SystemTools::Statics->GetActualCaseForPathCached(newPath);
SystemTools::ConvertToUnixSlashes(newPath);
@@ -3558,28 +3532,26 @@
// for each entry that is not common in the local path
// add a ../ to the finalpath array, this gets us out of the local
// path into the remote dir
- for (unsigned int i = 0; i < localSplit.size(); ++i) {
- if (!localSplit[i].empty()) {
- finalPath.push_back("../");
+ for (std::string const& lp : localSplit) {
+ if (!lp.empty()) {
+ finalPath.emplace_back("../");
}
}
// for each entry that is not common in the remote path add it
// to the final path.
- for (std::vector<std::string>::iterator vit = remoteSplit.begin();
- vit != remoteSplit.end(); ++vit) {
- if (!vit->empty()) {
- finalPath.push_back(*vit);
+ for (std::string const& rp : remoteSplit) {
+ if (!rp.empty()) {
+ finalPath.push_back(rp);
}
}
std::string relativePath; // result string
// now turn the array of directories into a unix path by puttint /
// between each entry that does not already have one
- for (std::vector<std::string>::iterator vit1 = finalPath.begin();
- vit1 != finalPath.end(); ++vit1) {
+ for (std::string const& fp : finalPath) {
if (!relativePath.empty() && relativePath.back() != '/') {
- relativePath += "/";
+ relativePath += '/';
}
- relativePath += *vit1;
+ relativePath += fp;
}
return relativePath;
}
@@ -3727,8 +3699,7 @@
// Construct result in a single string.
std::string result;
size_t len = 0;
- std::vector<std::string>::const_iterator i;
- for (i = first; i != last; ++i) {
+ for (std::vector<std::string>::const_iterator i = first; i != last; ++i) {
len += 1 + i->size();
}
result.reserve(len);
@@ -4737,10 +4708,11 @@
// Create statics singleton instance
SystemTools::Statics = new SystemToolsStatic;
+#if KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP
// Add some special translation paths for unix. These are not added
// for windows because drive letters need to be maintained. Also,
// there are not sym-links and mount points on windows anyway.
-#if !defined(_WIN32) || defined(__CYGWIN__)
+# if !defined(_WIN32) || defined(__CYGWIN__)
// The tmp path is frequently a logical path so always keep it:
SystemTools::AddKeepPath("/tmp/");
@@ -4778,6 +4750,7 @@
}
}
}
+# endif
#endif
}
diff --git a/Source/kwsys/SystemTools.hxx.in b/Source/kwsys/SystemTools.hxx.in
index cdc9483..dd1266b 100644
--- a/Source/kwsys/SystemTools.hxx.in
+++ b/Source/kwsys/SystemTools.hxx.in
@@ -891,6 +891,7 @@
*/
static int GetTerminalWidth();
+#if @KWSYS_NAMESPACE@_SYSTEMTOOLS_USE_TRANSLATION_MAP
/**
* Add an entry in the path translation table.
*/
@@ -907,6 +908,7 @@
* Update path by going through the Path Translation table;
*/
static void CheckTranslationPath(std::string& path);
+#endif
/**
* Delay the execution for a specified amount of time specified
diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt
index 2e0902c..bc4812b 100644
--- a/Tests/CMakeLists.txt
+++ b/Tests/CMakeLists.txt
@@ -1995,6 +1995,10 @@
if(NOT CMAKE_C_COMPILER_ID STREQUAL "Clang")
ADD_TEST_MACRO(PrecompiledHeader foo)
endif()
+ ADD_TEST_MACRO(MSVCRuntimeLibrary)
+ if(CMAKE_Fortran_COMPILER)
+ ADD_TEST_MACRO(MSVCRuntimeLibrary.Fortran)
+ endif()
endif()
if(MSVC OR
"${CMAKE_GENERATOR}" MATCHES "(MSYS|MinGW) Makefiles")
diff --git a/Tests/FindBoost/CMakeLists.txt b/Tests/FindBoost/CMakeLists.txt
index 17a8ec7..58d795b 100644
--- a/Tests/FindBoost/CMakeLists.txt
+++ b/Tests/FindBoost/CMakeLists.txt
@@ -33,3 +33,16 @@
--build-options ${build_options}
--test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
)
+
+if (CMake_TEST_FindBoost_Python)
+ add_test(NAME FindBoost.TestPython COMMAND
+ ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+ --build-and-test
+ "${CMake_SOURCE_DIR}/Tests/FindBoost/TestPython"
+ "${CMake_BINARY_DIR}/Tests/FindBoost/TestPython"
+ ${build_generator_args}
+ --build-project TestFindBoostPython
+ --build-options ${build_options}
+ --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+ )
+endif ()
diff --git a/Tests/FindBoost/TestPython/CMakeLists.txt b/Tests/FindBoost/TestPython/CMakeLists.txt
new file mode 100644
index 0000000..4d137ca
--- /dev/null
+++ b/Tests/FindBoost/TestPython/CMakeLists.txt
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.14)
+project(TestFindBoostPython CXX)
+include(CTest)
+
+find_package(Boost OPTIONAL_COMPONENTS python27 python34 python35 python36 python37)
+
+set(FAILTEST TRUE)
+foreach (v IN ITEMS 27 34 35 36 37)
+ if (Boost_PYTHON${v}_FOUND)
+ set(FAILTEST FALSE)
+ break()
+ endif ()
+endforeach ()
+
+if (FAILTEST)
+ message(FATAL_ERROR "No Boost Python module found")
+endif ()
diff --git a/Tests/MSVCRuntimeLibrary/CMakeLists.txt b/Tests/MSVCRuntimeLibrary/CMakeLists.txt
new file mode 100644
index 0000000..b7a6e86
--- /dev/null
+++ b/Tests/MSVCRuntimeLibrary/CMakeLists.txt
@@ -0,0 +1,49 @@
+cmake_minimum_required(VERSION 3.14)
+cmake_policy(SET CMP0091 NEW)
+project(MSVCRuntimeLibrary)
+
+function(verify_combinations threads lang src)
+ set(verify_tc_config_ Release)
+ set(verify_tc_config_Debug Debug)
+ set(verify_def_MultiThreaded -DVERIFY_MT)
+ set(verify_def_Debug -DVERIFY_DEBUG)
+ set(verify_def_DLL -DVERIFY_DLL)
+ foreach(dbg "" Debug)
+ foreach(dll "" DLL)
+ # Construct the name of this runtime library combination.
+ set(rtl "${threads}${dbg}${dll}")
+
+ # Test that try_compile builds with this RTL.
+ set(CMAKE_MSVC_RUNTIME_LIBRARY "${rtl}")
+ set(CMAKE_TRY_COMPILE_CONFIGURATION "${verify_tc_config_${dbg}}")
+ set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
+ try_compile(${rtl}_COMPILES
+ ${CMAKE_CURRENT_BINARY_DIR}/try_compile/${rtl}
+ ${CMAKE_CURRENT_SOURCE_DIR}/${src}
+ COMPILE_DEFINITIONS ${verify_def_${threads}} ${verify_def_${dbg}} ${verify_def_${dll}}
+ OUTPUT_VARIABLE ${rtl}_OUTPUT
+ )
+ if(${rtl}_COMPILES)
+ message(STATUS "try_compile with ${rtl} worked")
+ else()
+ string(REPLACE "\n" "\n " ${rtl}_OUTPUT " ${${rtl}_OUTPUT}")
+ message(SEND_ERROR "try_compile with ${rtl} failed:\n${${rtl}_OUTPUT}")
+ endif()
+
+ # Test that targets build with this RTL.
+ set(CMAKE_MSVC_RUNTIME_LIBRARY "$<$<BOOL:$<TARGET_PROPERTY:BOOL_TRUE>>:${rtl}>$<$<BOOL:$<TARGET_PROPERTY:BOOL_FALSE>>:BadContent>")
+ add_library(${rtl}-${lang} ${src})
+ set_property(TARGET ${rtl}-${lang} PROPERTY BOOL_TRUE TRUE)
+ target_compile_definitions(${rtl}-${lang} PRIVATE ${verify_def_${threads}} ${verify_def_${dbg}} ${verify_def_${dll}})
+ endforeach()
+ endforeach()
+endfunction()
+
+function(verify lang src)
+ add_library(default-${lang} ${src})
+ target_compile_definitions(default-${lang} PRIVATE VERIFY_MT VERIFY_DLL "$<$<CONFIG:Debug>:VERIFY_DEBUG>")
+ verify_combinations(MultiThreaded ${lang} ${src})
+endfunction()
+
+verify(C verify.c)
+verify(CXX verify.cxx)
diff --git a/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt b/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt
new file mode 100644
index 0000000..169ba07
--- /dev/null
+++ b/Tests/MSVCRuntimeLibrary/Fortran/CMakeLists.txt
@@ -0,0 +1,50 @@
+cmake_minimum_required(VERSION 3.14)
+cmake_policy(SET CMP0091 NEW)
+project(MSVCRuntimeLibraryFortran Fortran)
+
+foreach(t MultiThreaded SingleThreaded)
+ foreach(dbg "" Debug)
+ foreach(dll "" DLL)
+ set(var "CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_${t}${dbg}${dll}")
+ # ifort does not actually define these, so inject them
+ string(REPLACE "-threads" "-threads;-D_MT" "${var}" "${${var}}")
+ string(REPLACE "-dbglibs" "-dbglibs;-D_DEBUG" "${var}" "${${var}}")
+ endforeach()
+ endforeach()
+endforeach()
+string(APPEND CMAKE_Fortran_FLAGS " -w")
+
+function(verify_combinations threads lang src)
+ set(verify_tc_config_ Release)
+ set(verify_tc_config_Debug Debug)
+ set(verify_def_MultiThreaded -DVERIFY_MT)
+ set(verify_def_Debug -DVERIFY_DEBUG)
+ set(verify_def_DLL -DVERIFY_DLL)
+ foreach(dbg "" Debug)
+ foreach(dll "" DLL)
+ # Construct the name of this runtime library combination.
+ set(rtl "${threads}${dbg}${dll}")
+
+ # Test that targets build with this RTL.
+ set(CMAKE_MSVC_RUNTIME_LIBRARY "$<$<BOOL:$<TARGET_PROPERTY:BOOL_TRUE>>:${rtl}>$<$<BOOL:$<TARGET_PROPERTY:BOOL_FALSE>>:BadContent>")
+ add_library(${rtl}-${lang} ${src})
+ set_property(TARGET ${rtl}-${lang} PROPERTY BOOL_TRUE TRUE)
+ target_compile_definitions(${rtl}-${lang} PRIVATE ${verify_def_${threads}} ${verify_def_${dbg}} ${verify_def_${dll}})
+ endforeach()
+ endforeach()
+endfunction()
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+function(verify lang src)
+ add_library(default-${lang} ${src})
+ target_compile_definitions(default-${lang} PRIVATE VERIFY_MT VERIFY_DLL "$<$<CONFIG:Debug>:VERIFY_DEBUG>")
+ verify_combinations(MultiThreaded ${lang} ${src})
+endfunction()
+
+verify(Fortran verify.F90)
+# Intel Fortran for Windows supports single-threaded RTL but it is
+# not implemented by the Visual Studio integration.
+if(NOT CMAKE_GENERATOR MATCHES "Visual Studio")
+ verify_combinations(SingleThreaded Fortran verify.F90)
+endif()
diff --git a/Tests/MSVCRuntimeLibrary/Fortran/verify.F90 b/Tests/MSVCRuntimeLibrary/Fortran/verify.F90
new file mode 100644
index 0000000..6fe5e05
--- /dev/null
+++ b/Tests/MSVCRuntimeLibrary/Fortran/verify.F90
@@ -0,0 +1 @@
+#include "../verify.h"
diff --git a/Tests/MSVCRuntimeLibrary/verify.c b/Tests/MSVCRuntimeLibrary/verify.c
new file mode 100644
index 0000000..741bca6
--- /dev/null
+++ b/Tests/MSVCRuntimeLibrary/verify.c
@@ -0,0 +1 @@
+#include "verify.h"
diff --git a/Tests/MSVCRuntimeLibrary/verify.cxx b/Tests/MSVCRuntimeLibrary/verify.cxx
new file mode 100644
index 0000000..741bca6
--- /dev/null
+++ b/Tests/MSVCRuntimeLibrary/verify.cxx
@@ -0,0 +1 @@
+#include "verify.h"
diff --git a/Tests/MSVCRuntimeLibrary/verify.h b/Tests/MSVCRuntimeLibrary/verify.h
new file mode 100644
index 0000000..58d65fe
--- /dev/null
+++ b/Tests/MSVCRuntimeLibrary/verify.h
@@ -0,0 +1,29 @@
+#ifdef VERIFY_DEBUG
+# ifndef _DEBUG
+# error "_DEBUG not defined by debug runtime library selection"
+# endif
+#else
+# ifdef _DEBUG
+# error "_DEBUG defined by non-debug runtime library selection"
+# endif
+#endif
+
+#ifdef VERIFY_DLL
+# ifndef _DLL
+# error "_DLL not defined by DLL runtime library selection"
+# endif
+#else
+# ifdef _DLL
+# error "_DLL defined by non-DLL runtime library selection"
+# endif
+#endif
+
+#ifdef VERIFY_MT
+# ifndef _MT
+# error "_MT not defined by multi-threaded runtime library selection"
+# endif
+#else
+# ifdef _MT
+# error "_MT defined by single-threaded runtime library selection"
+# endif
+#endif
diff --git a/Tests/QtAutogen/ManySources/CMakeLists.txt b/Tests/QtAutogen/ManySources/CMakeLists.txt
new file mode 100644
index 0000000..df8a2a6
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/CMakeLists.txt
@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 3.10)
+project(ManySources)
+include("../AutogenGuiTest.cmake")
+
+# Test AUTOMOC and AUTOUIC on many source files to stress the concurrent
+# parsing and processing framework.
+
+set(CSD ${CMAKE_CURRENT_SOURCE_DIR})
+set(CBD ${CMAKE_CURRENT_BINARY_DIR})
+
+set(SRCS main.cpp)
+set(MAIN_INCLUDES "\n// Item includes\n")
+set(MAIN_ITEMS "\n// Items\n")
+
+set(NUM 24)
+foreach(III RANGE 1 ${NUM})
+ configure_file(${CSD}/object.h.in ${CBD}/object_${III}.h)
+ configure_file(${CSD}/item.h.in ${CBD}/item_${III}.h)
+ configure_file(${CSD}/item.cpp.in ${CBD}/item_${III}.cpp)
+ configure_file(${CSD}/view.ui.in ${CBD}/view_${III}.ui)
+ configure_file(${CSD}/data.qrc.in ${CBD}/data_${III}.qrc)
+
+ list(APPEND SRCS ${CBD}/item_${III}.cpp)
+ list(APPEND SRCS ${CBD}/data_${III}.qrc)
+
+ string(APPEND MAIN_INCLUDES "#include \"item_${III}.h\"\n")
+ string(APPEND MAIN_ITEMS "Item_${III} item_${III};\n")
+ string(APPEND MAIN_ITEMS "item_${III}.TheSlot();\n")
+endforeach()
+
+configure_file(${CSD}/main.cpp.in ${CBD}/main.cpp)
+
+add_executable(manySources ${SRCS} ${CBD}/main.cpp)
+target_link_libraries(manySources ${QT_LIBRARIES})
+set_target_properties(manySources PROPERTIES AUTOMOC 1 AUTOUIC 1 AUTORCC 1)
diff --git a/Tests/QtAutogen/ManySources/data.qrc.in b/Tests/QtAutogen/ManySources/data.qrc.in
new file mode 100644
index 0000000..870d486
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/data.qrc.in
@@ -0,0 +1,7 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file>object_@III@.h</file>
+ <file>item_@III@.h</file>
+ <file>item_@III@.cpp</file>
+</qresource>
+</RCC>
diff --git a/Tests/QtAutogen/ManySources/item.cpp.in b/Tests/QtAutogen/ManySources/item.cpp.in
new file mode 100644
index 0000000..c34ad16
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/item.cpp.in
@@ -0,0 +1,27 @@
+#include "item_@III@.h"
+#include "object_@III@.h"
+// AUTOUIC include
+#include <ui_view_@III@.h>
+
+class LocalObject_@III@ : public QObject
+{
+ Q_OBJECT;
+
+public:
+ LocalObject_@III@() = default;
+ ~LocalObject_@III@() = default;
+};
+
+void Item_@III@ ::TheSlot()
+{
+ LocalObject_@III@ localObject;
+ Object_@III@ obj;
+ obj.ObjectSlot();
+
+ Ui_View_@III@ ui_view;
+}
+
+// AUTOMOC includes
+#include "item_@III@.moc"
+#include "moc_item_@III@.cpp"
+#include "moc_object_@III@.cpp"
diff --git a/Tests/QtAutogen/ManySources/item.h.in b/Tests/QtAutogen/ManySources/item.h.in
new file mode 100644
index 0000000..67ad794
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/item.h.in
@@ -0,0 +1,15 @@
+#ifndef ITEM_@III@HPP
+#define ITEM_@III@HPP
+
+#include <QObject>
+
+class Item_@III@ : public QObject
+{
+ Q_OBJECT
+
+public:
+ Q_SLOT
+ void TheSlot();
+};
+
+#endif
diff --git a/Tests/QtAutogen/ManySources/main.cpp.in b/Tests/QtAutogen/ManySources/main.cpp.in
new file mode 100644
index 0000000..e1dda40
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/main.cpp.in
@@ -0,0 +1,7 @@
+@MAIN_INCLUDES@
+
+int main(int argv, char** args)
+{
+ @MAIN_ITEMS@
+ return 0;
+}
diff --git a/Tests/QtAutogen/ManySources/object.h.in b/Tests/QtAutogen/ManySources/object.h.in
new file mode 100644
index 0000000..a747cbc
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/object.h.in
@@ -0,0 +1,15 @@
+#ifndef OBJECT_@III@H
+#define OBJECT_@III@H
+
+#include <QObject>
+
+class Object_@III@ : public QObject
+{
+ Q_OBJECT
+
+public:
+ Q_SLOT
+ void ObjectSlot(){};
+};
+
+#endif
diff --git a/Tests/QtAutogen/ManySources/view.ui.in b/Tests/QtAutogen/ManySources/view.ui.in
new file mode 100644
index 0000000..6901fe3
--- /dev/null
+++ b/Tests/QtAutogen/ManySources/view.ui.in
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>View_@III@</class>
+ <widget class="QWidget" name="Base">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Form</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QTreeView" name="treeView"/>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/Tests/QtAutogen/Tests.cmake b/Tests/QtAutogen/Tests.cmake
index 096d5e3..6771828 100644
--- a/Tests/QtAutogen/Tests.cmake
+++ b/Tests/QtAutogen/Tests.cmake
@@ -5,6 +5,7 @@
ADD_AUTOGEN_TEST(Complex QtAutogen)
ADD_AUTOGEN_TEST(GlobalAutogenTarget)
ADD_AUTOGEN_TEST(LowMinimumVersion lowMinimumVersion)
+ADD_AUTOGEN_TEST(ManySources manySources)
ADD_AUTOGEN_TEST(MocOnly mocOnly)
ADD_AUTOGEN_TEST(MocOptions mocOptions)
ADD_AUTOGEN_TEST(ObjectLibrary someProgram)
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index 68411d6..a8dcb4b 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -195,6 +195,9 @@
if(CMAKE_CXX_COMPILER_ID MATCHES "^(Cray|PGI|XL|XLClang)$")
add_RunCMake_test(MetaCompileFeatures)
endif()
+if(MSVC)
+ add_RunCMake_test(MSVCRuntimeLibrary)
+endif()
add_RunCMake_test(ObjectLibrary)
add_RunCMake_test(ParseImplicitIncludeInfo)
if(UNIX AND CMAKE_SHARED_LIBRARY_RUNTIME_C_FLAG AND CMAKE_EXECUTABLE_FORMAT STREQUAL "ELF")
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-result.txt
similarity index 100%
rename from Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt
rename to Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt
new file mode 100644
index 0000000..3b2a814
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at ImportedTarget-TARGET_PDB_FILE_BASE_NAME.cmake:2 \(add_custom_target\):
+ Error evaluating generator expression:
+
+ \$<TARGET_PDB_FILE_BASE_NAME:empty>
+
+ TARGET_PDB_FILE_BASE_NAME not allowed for IMPORTED targets.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME.cmake b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME.cmake
new file mode 100644
index 0000000..489d8e6
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_FILE_BASE_NAME.cmake
@@ -0,0 +1,2 @@
+add_library(empty UNKNOWN IMPORTED)
+add_custom_target(custom COMMAND echo $<TARGET_PDB_FILE_BASE_NAME:empty>)
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt
deleted file mode 100644
index 783bfb3..0000000
--- a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CMake Error at ImportedTarget-TARGET_PDB_OUTPUT_NAME.cmake:2 \(add_custom_target\):
- Error evaluating generator expression:
-
- \$<TARGET_PDB_OUTPUT_NAME:empty>
-
- TARGET_PDB_OUTPUT_NAME not allowed for IMPORTED targets.
-Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME.cmake
deleted file mode 100644
index 010b38e..0000000
--- a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME.cmake
+++ /dev/null
@@ -1,2 +0,0 @@
-add_library(empty UNKNOWN IMPORTED)
-add_custom_target(custom COMMAND echo $<TARGET_PDB_OUTPUT_NAME:empty>)
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-result.txt
similarity index 100%
copy from Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt
copy to Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-stderr.txt
new file mode 100644
index 0000000..b061ce3
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME-stderr.txt
@@ -0,0 +1,8 @@
+CMake Error at NonValidCompiler-TARGET_PDB_FILE_BASE_NAME.cmake:6 \(file\):
+ Error evaluating generator expression:
+
+ \$<TARGET_PDB_FILE_BASE_NAME:empty>
+
+ TARGET_PDB_FILE_BASE_NAME is not supported by the target linker.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME.cmake
similarity index 70%
rename from Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake
rename to Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME.cmake
index 07951de..811c3f7 100644
--- a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake
+++ b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_FILE_BASE_NAME.cmake
@@ -5,5 +5,5 @@
file(GENERATE
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
- CONTENT "[$<TARGET_PDB_OUTPUT_NAME:empty>]"
+ CONTENT "[$<TARGET_PDB_FILE_BASE_NAME:empty>]"
)
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-result.txt
deleted file mode 100644
index d00491f..0000000
--- a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-result.txt
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-stderr.txt
deleted file mode 100644
index 00ec496..0000000
--- a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME-stderr.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-CMake Error at NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake:6 \(file\):
- Error evaluating generator expression:
-
- \$<TARGET_PDB_OUTPUT_NAME:empty>
-
- TARGET_PDB_OUTPUT_NAME is not supported by the target linker.
-Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-result.txt
similarity index 100%
copy from Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt
copy to Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt
new file mode 100644
index 0000000..c7d245c
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME-stderr.txt
@@ -0,0 +1,9 @@
+CMake Error at NonValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake:6 \(file\):
+ Error evaluating generator expression:
+
+ \$<TARGET_PDB_FILE_BASE_NAME:empty>
+
+ TARGET_PDB_FILE_BASE_NAME is allowed only for targets with linker created
+ artifacts.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake
similarity index 70%
copy from Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake
copy to Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake
index 07951de..811c3f7 100644
--- a/Tests/RunCMake/GeneratorExpression/NonValidCompiler-TARGET_PDB_OUTPUT_NAME.cmake
+++ b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake
@@ -5,5 +5,5 @@
file(GENERATE
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
- CONTENT "[$<TARGET_PDB_OUTPUT_NAME:empty>]"
+ CONTENT "[$<TARGET_PDB_FILE_BASE_NAME:empty>]"
)
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-result.txt
deleted file mode 100644
index d00491f..0000000
--- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-result.txt
+++ /dev/null
@@ -1 +0,0 @@
-1
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt
deleted file mode 100644
index 8ac349e..0000000
--- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME-stderr.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-CMake Error at NonValidTarget-TARGET_PDB_OUTPUT_NAME.cmake:6 \(file\):
- Error evaluating generator expression:
-
- \$<TARGET_PDB_OUTPUT_NAME:empty>
-
- TARGET_PDB_OUTPUT_NAME is allowed only for targets with linker created
- artifacts.
-Call Stack \(most recent call first\):
- CMakeLists.txt:3 \(include\)
diff --git a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME.cmake
deleted file mode 100644
index 07951de..0000000
--- a/Tests/RunCMake/GeneratorExpression/NonValidTarget-TARGET_PDB_OUTPUT_NAME.cmake
+++ /dev/null
@@ -1,9 +0,0 @@
-
-enable_language(C)
-
-add_library(empty STATIC empty.c)
-
-file(GENERATE
- OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
- CONTENT "[$<TARGET_PDB_OUTPUT_NAME:empty>]"
-)
diff --git a/Tests/RunCMake/GeneratorExpression/OUTPUT_NAME-recursion.cmake b/Tests/RunCMake/GeneratorExpression/OUTPUT_NAME-recursion.cmake
index 775f68a..006b0da 100644
--- a/Tests/RunCMake/GeneratorExpression/OUTPUT_NAME-recursion.cmake
+++ b/Tests/RunCMake/GeneratorExpression/OUTPUT_NAME-recursion.cmake
@@ -3,4 +3,4 @@
set_property(TARGET empty1 PROPERTY OUTPUT_NAME $<TARGET_FILE_NAME:empty1>)
add_executable(empty2 empty.c)
-set_property(TARGET empty2 PROPERTY OUTPUT_NAME $<TARGET_OUTPUT_NAME:empty2>)
+set_property(TARGET empty2 PROPERTY OUTPUT_NAME $<TARGET_FILE_BASE_NAME:empty2>)
diff --git a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
index 0b0fb78..477b593 100644
--- a/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
+++ b/Tests/RunCMake/GeneratorExpression/RunCMakeTest.cmake
@@ -41,10 +41,10 @@
run_cmake(TARGET_FILE_SUFFIX-imported-target)
run_cmake(TARGET_FILE_SUFFIX-non-valid-target)
run_cmake(TARGET_LINKER_FILE_SUFFIX-non-valid-target)
-run_cmake(TARGET_OUTPUT_NAME)
-run_cmake(TARGET_OUTPUT_NAME-imported-target)
-run_cmake(TARGET_OUTPUT_NAME-non-valid-target)
-run_cmake(TARGET_LINKER_OUTPUT_NAME-non-valid-target)
+run_cmake(TARGET_FILE_BASE_NAME)
+run_cmake(TARGET_FILE_BASE_NAME-imported-target)
+run_cmake(TARGET_FILE_BASE_NAME-non-valid-target)
+run_cmake(TARGET_LINKER_FILE_BASE_NAME-non-valid-target)
run_cmake(TARGET_PROPERTY-LOCATION)
run_cmake(TARGET_PROPERTY-SOURCES)
run_cmake(LINK_ONLY-not-linking)
@@ -78,15 +78,15 @@
run_cmake(ImportedTarget-TARGET_BUNDLE_DIR)
run_cmake(ImportedTarget-TARGET_BUNDLE_CONTENT_DIR)
run_cmake(ImportedTarget-TARGET_PDB_FILE)
-run_cmake(ImportedTarget-TARGET_PDB_OUTPUT_NAME)
+run_cmake(ImportedTarget-TARGET_PDB_FILE_BASE_NAME)
if(LINKER_SUPPORTS_PDB)
run_cmake(NonValidTarget-TARGET_PDB_FILE)
run_cmake(ValidTarget-TARGET_PDB_FILE)
- run_cmake(NonValidTarget-TARGET_PDB_OUTPUT_NAME)
- run_cmake(ValidTarget-TARGET_PDB_OUTPUT_NAME)
+ run_cmake(NonValidTarget-TARGET_PDB_FILE_BASE_NAME)
+ run_cmake(ValidTarget-TARGET_PDB_FILE_BASE_NAME)
else()
run_cmake(NonValidCompiler-TARGET_PDB_FILE)
- run_cmake(NonValidCompiler-TARGET_PDB_OUTPUT_NAME)
+ run_cmake(NonValidCompiler-TARGET_PDB_FILE_BASE_NAME)
endif()
set(RunCMake_TEST_OPTIONS -DCMAKE_POLICY_DEFAULT_CMP0085:STRING=OLD)
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-check.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-check.cmake
new file mode 100644
index 0000000..793edb1
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-check.cmake
@@ -0,0 +1,2 @@
+
+include ("${RunCMake_TEST_BINARY_DIR}/TARGET_FILE_BASE_NAME-generated.cmake")
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target-check.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target-check.cmake
new file mode 100644
index 0000000..793edb1
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target-check.cmake
@@ -0,0 +1,2 @@
+
+include ("${RunCMake_TEST_BINARY_DIR}/TARGET_FILE_BASE_NAME-generated.cmake")
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target.cmake
new file mode 100644
index 0000000..aa54b31
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-imported-target.cmake
@@ -0,0 +1,79 @@
+
+cmake_minimum_required(VERSION 3.14)
+
+enable_language (C)
+
+set (GENERATE_CONTENT [[
+macro (CHECK_VALUE test_msg value expected)
+ if (NOT "${value}" STREQUAL "${expected}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
+ endif()
+endmacro()
+]])
+
+add_executable(exec1 IMPORTED)
+add_library (shared1 SHARED IMPORTED)
+add_library (static1 STATIC IMPORTED)
+
+string (APPEND GENERATE_CONTENT [[
+
+check_value ("TARGET_FILE_BASE_NAME executable default" "$<TARGET_FILE_BASE_NAME:exec1>" "exec1")
+check_value ("TARGET_FILE_BASE_NAME shared default" "$<TARGET_FILE_BASE_NAME:shared1>" "shared1")
+check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker default" "$<TARGET_LINKER_FILE_BASE_NAME:shared1>" "shared1")
+check_value ("TARGET_FILE_BASE_NAME static default" "$<TARGET_FILE_BASE_NAME:static1>" "static1")
+check_value ("TARGET_LINKER_FILE_BASE_NAME static linker default" "$<TARGET_LINKER_FILE_BASE_NAME:static1>" "static1")
+]])
+
+
+add_executable (exec2 IMPORTED)
+set_property (TARGET exec2 PROPERTY OUTPUT_NAME exec2_custom)
+add_library (shared2 SHARED IMPORTED)
+set_property (TARGET shared2 PROPERTY OUTPUT_NAME shared2_custom)
+add_library (static2 STATIC IMPORTED)
+set_property (TARGET static2 PROPERTY OUTPUT_NAME static2_custom)
+
+string (APPEND GENERATE_CONTENT [[
+
+check_value ("TARGET_FILE_BASE_NAME executable custom" "$<TARGET_FILE_BASE_NAME:exec2>" "exec2_custom")
+check_value ("TARGET_FILE_BASE_NAME shared custom" "$<TARGET_FILE_BASE_NAME:shared2>" "shared2_custom")
+check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker custom" "$<TARGET_LINKER_FILE_BASE_NAME:shared2>" "shared2_custom")
+check_value ("TARGET_FILE_BASE_NAME static custom" "$<TARGET_FILE_BASE_NAME:static2>" "static2_custom")
+check_value ("TARGET_LINKER_FILE_BASE_NAME static linker custom" "$<TARGET_LINKER_FILE_BASE_NAME:static2>" "static2_custom")
+]])
+
+
+add_executable (exec3 IMPORTED)
+set_property (TARGET exec3 PROPERTY RUNTIME_OUTPUT_NAME exec3_runtime)
+set_property (TARGET exec3 PROPERTY LIBRARY_OUTPUT_NAME exec3_library)
+set_property (TARGET exec3 PROPERTY ARCHIVE_OUTPUT_NAME exec3_archive)
+set_property (TARGET exec3 PROPERTY PDB_NAME exec3_pdb)
+add_library (shared3 SHARED IMPORTED)
+set_property (TARGET shared3 PROPERTY RUNTIME_OUTPUT_NAME shared3_runtime)
+set_property (TARGET shared3 PROPERTY LIBRARY_OUTPUT_NAME shared3_library)
+set_property (TARGET shared3 PROPERTY ARCHIVE_OUTPUT_NAME shared3_archive)
+set_property (TARGET shared3 PROPERTY PDB_NAME shared3_pdb)
+add_library (static3 STATIC IMPORTED)
+set_property (TARGET static3 PROPERTY RUNTIME_OUTPUT_NAME static3_runtime)
+set_property (TARGET static3 PROPERTY LIBRARY_OUTPUT_NAME static3_library)
+set_property (TARGET static3 PROPERTY ARCHIVE_OUTPUT_NAME static3_archive)
+set_property (TARGET static3 PROPERTY PDB_NAME static3_pdb)
+
+string (APPEND GENERATE_CONTENT [[
+
+check_value ("TARGET_FILE_BASE_NAME executable all properties" "$<TARGET_FILE_BASE_NAME:exec3>" "exec3_runtime")
+check_value ("TARGET_FILE_BASE_NAME shared all properties" "$<TARGET_FILE_BASE_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_runtime,shared3_library>")
+check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker all properties" "$<TARGET_LINKER_FILE_BASE_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_archive,shared3_library>")
+check_value ("TARGET_FILE_BASE_NAME static all properties" "$<TARGET_FILE_BASE_NAME:static3>" "static3_archive")
+check_value ("TARGET_LINKER_FILE_BASE_NAME static linker all properties" "$<TARGET_LINKER_FILE_BASE_NAME:static3>" "static3_archive")
+]])
+
+
+unset(GENERATE_CONDITION)
+get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(_isMultiConfig)
+ list(GET CMAKE_CONFIGURATION_TYPES 0 FIRST_CONFIG)
+ set(GENERATE_CONDITION CONDITION $<CONFIG:${FIRST_CONFIG}>)
+endif()
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_FILE_BASE_NAME-generated.cmake"
+ CONTENT "${GENERATE_CONTENT}" ${GENERATE_CONDITION})
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-result.txt b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-result.txt
similarity index 100%
rename from Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-result.txt
rename to Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-stderr.txt b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-stderr.txt
new file mode 100644
index 0000000..ecb9e5d
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at TARGET_FILE_BASE_NAME-non-valid-target.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<TARGET_FILE_BASE_NAME:empty>
+
+ Target "empty" is not an executable or library\.
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target.cmake
similarity index 67%
rename from Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target.cmake
rename to Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target.cmake
index 5248dfa..8622b7d 100644
--- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target.cmake
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME-non-valid-target.cmake
@@ -3,5 +3,5 @@
file(GENERATE
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
- CONTENT "[$<TARGET_OUTPUT_NAME:empty>]"
+ CONTENT "[$<TARGET_FILE_BASE_NAME:empty>]"
)
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME.cmake
new file mode 100644
index 0000000..5ea53a0
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_FILE_BASE_NAME.cmake
@@ -0,0 +1,96 @@
+
+cmake_minimum_required(VERSION 3.14)
+
+enable_language (C)
+
+set (GENERATE_CONTENT [[
+macro (CHECK_VALUE test_msg value expected)
+ if (NOT "${value}" STREQUAL "${expected}")
+ string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
+ endif()
+endmacro()
+]])
+
+add_executable (exec1 empty.c)
+add_library (shared1 SHARED empty.c)
+add_library (static1 STATIC empty.c)
+
+string (APPEND GENERATE_CONTENT [[
+
+check_value ("TARGET_FILE_BASE_NAME executable default" "$<TARGET_FILE_BASE_NAME:exec1>" "exec1")
+check_value ("TARGET_FILE_BASE_NAME shared default" "$<TARGET_FILE_BASE_NAME:shared1>" "shared1")
+check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker default" "$<TARGET_LINKER_FILE_BASE_NAME:shared1>" "shared1")
+check_value ("TARGET_FILE_BASE_NAME static default" "$<TARGET_FILE_BASE_NAME:static1>" "static1")
+check_value ("TARGET_LINKER_FILE_BASE_NAME static linker default" "$<TARGET_LINKER_FILE_BASE_NAME:static1>" "static1")
+]])
+if (CMAKE_C_LINKER_SUPPORTS_PDB)
+ string(APPEND GENERATE_CONTENT [[
+check_value ("TARGET_PDB_FILE_BASE_NAME executable PDB default" "$<TARGET_PDB_FILE_BASE_NAME:exec1>" "exec1")
+check_value ("TARGET_PDB_FILE_BASE_NAME shared PDB default" "$<TARGET_PDB_FILE_BASE_NAME:shared1>" "shared1")
+]])
+endif()
+
+
+add_executable (exec2 empty.c)
+set_property (TARGET exec2 PROPERTY OUTPUT_NAME exec2_custom)
+add_library (shared2 SHARED empty.c)
+set_property (TARGET shared2 PROPERTY OUTPUT_NAME shared2_custom)
+add_library (static2 STATIC empty.c)
+set_property (TARGET static2 PROPERTY OUTPUT_NAME static2_custom)
+
+string (APPEND GENERATE_CONTENT [[
+
+check_value ("TARGET_FILE_BASE_NAME executable custom" "$<TARGET_FILE_BASE_NAME:exec2>" "exec2_custom")
+check_value ("TARGET_FILE_BASE_NAME shared custom" "$<TARGET_FILE_BASE_NAME:shared2>" "shared2_custom")
+check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker custom" "$<TARGET_LINKER_FILE_BASE_NAME:shared2>" "shared2_custom")
+check_value ("TARGET_FILE_BASE_NAME static custom" "$<TARGET_FILE_BASE_NAME:static2>" "static2_custom")
+check_value ("TARGET_LINKER_FILE_BASE_NAME static linker custom" "$<TARGET_LINKER_FILE_BASE_NAME:static2>" "static2_custom")
+]])
+if (CMAKE_C_LINKER_SUPPORTS_PDB)
+ string (APPEND GENERATE_CONTENT [[
+check_value ("TARGET_PDB_FILE_BASE_NAME executable PDB custom" "$<TARGET_PDB_FILE_BASE_NAME:exec2>" "exec2_custom")
+check_value ("TARGET_PDB_FILE_BASE_NAME shared PDB custom" "$<TARGET_PDB_FILE_BASE_NAME:shared2>" "shared2_custom")
+ ]])
+endif()
+
+add_executable (exec3 empty.c)
+set_property (TARGET exec3 PROPERTY RUNTIME_OUTPUT_NAME exec3_runtime)
+set_property (TARGET exec3 PROPERTY LIBRARY_OUTPUT_NAME exec3_library)
+set_property (TARGET exec3 PROPERTY ARCHIVE_OUTPUT_NAME exec3_archive)
+set_property (TARGET exec3 PROPERTY PDB_NAME exec3_pdb)
+add_library (shared3 SHARED empty.c)
+set_property (TARGET shared3 PROPERTY RUNTIME_OUTPUT_NAME shared3_runtime)
+set_property (TARGET shared3 PROPERTY LIBRARY_OUTPUT_NAME shared3_library)
+set_property (TARGET shared3 PROPERTY ARCHIVE_OUTPUT_NAME shared3_archive)
+set_property (TARGET shared3 PROPERTY PDB_NAME shared3_pdb)
+add_library (static3 STATIC empty.c)
+set_property (TARGET static3 PROPERTY RUNTIME_OUTPUT_NAME static3_runtime)
+set_property (TARGET static3 PROPERTY LIBRARY_OUTPUT_NAME static3_library)
+set_property (TARGET static3 PROPERTY ARCHIVE_OUTPUT_NAME static3_archive)
+set_property (TARGET static3 PROPERTY PDB_NAME static3_pdb)
+
+string (APPEND GENERATE_CONTENT [[
+
+check_value ("TARGET_FILE_BASE_NAME executable all properties" "$<TARGET_FILE_BASE_NAME:exec3>" "exec3_runtime")
+check_value ("TARGET_FILE_BASE_NAME shared all properties" "$<TARGET_FILE_BASE_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_runtime,shared3_library>")
+check_value ("TARGET_LINKER_FILE_BASE_NAME shared linker all properties" "$<TARGET_LINKER_FILE_BASE_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_archive,shared3_library>")
+check_value ("TARGET_FILE_BASE_NAME static all properties" "$<TARGET_FILE_BASE_NAME:static3>" "static3_archive")
+check_value ("TARGET_LINKER_FILE_BASE_NAME static linker all properties" "$<TARGET_LINKER_FILE_BASE_NAME:static3>" "static3_archive")
+]])
+if (CMAKE_C_LINKER_SUPPORTS_PDB)
+ string (APPEND GENERATE_CONTENT [[
+check_value ("TARGET_PDB_FILE_BASE_NAME executable PDB all properties" "$<TARGET_PDB_FILE_BASE_NAME:exec3>" "exec3_pdb")
+check_value ("TARGET_PDB_FILE_BASE_NAME shared PDB all properties" "$<TARGET_PDB_FILE_BASE_NAME:shared3>" "shared3_pdb")
+]])
+endif()
+
+
+unset(GENERATE_CONDITION)
+get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(_isMultiConfig)
+ list(GET CMAKE_CONFIGURATION_TYPES 0 FIRST_CONFIG)
+ set(GENERATE_CONDITION CONDITION $<CONFIG:${FIRST_CONFIG}>)
+endif()
+
+file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_FILE_BASE_NAME-generated.cmake"
+ CONTENT "${GENERATE_CONTENT}" ${GENERATE_CONDITION})
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-result.txt b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-result.txt
similarity index 100%
rename from Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-result.txt
rename to Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-result.txt
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-stderr.txt b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-stderr.txt
new file mode 100644
index 0000000..1ae2f2c
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target-stderr.txt
@@ -0,0 +1,6 @@
+CMake Error at TARGET_LINKER_FILE_BASE_NAME-non-valid-target.cmake:[0-9]+ \(file\):
+ Error evaluating generator expression:
+
+ \$<TARGET_LINKER_FILE_BASE_NAME:empty>
+
+ Target "empty" is not an executable or library\.
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target.cmake
similarity index 63%
rename from Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target.cmake
rename to Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target.cmake
index c439535..776fb4b 100644
--- a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target.cmake
+++ b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_FILE_BASE_NAME-non-valid-target.cmake
@@ -3,5 +3,5 @@
file(GENERATE
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
- CONTENT "[$<TARGET_LINKER_OUTPUT_NAME:empty>]"
+ CONTENT "[$<TARGET_LINKER_FILE_BASE_NAME:empty>]"
)
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-stderr.txt b/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-stderr.txt
deleted file mode 100644
index 0e09469..0000000
--- a/Tests/RunCMake/GeneratorExpression/TARGET_LINKER_OUTPUT_NAME-non-valid-target-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Error at TARGET_LINKER_OUTPUT_NAME-non-valid-target.cmake:[0-9]+ \(file\):
- Error evaluating generator expression:
-
- \$<TARGET_LINKER_OUTPUT_NAME:empty>
-
- Target "empty" is not an executable or library\.
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-check.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-check.cmake
deleted file mode 100644
index fa4f2b9..0000000
--- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-check.cmake
+++ /dev/null
@@ -1,2 +0,0 @@
-
-include ("${RunCMake_TEST_BINARY_DIR}/TARGET_OUTPUT_NAME-generated.cmake")
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target-check.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target-check.cmake
deleted file mode 100644
index fa4f2b9..0000000
--- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target-check.cmake
+++ /dev/null
@@ -1,2 +0,0 @@
-
-include ("${RunCMake_TEST_BINARY_DIR}/TARGET_OUTPUT_NAME-generated.cmake")
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target.cmake
deleted file mode 100644
index 548a2d7..0000000
--- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-imported-target.cmake
+++ /dev/null
@@ -1,79 +0,0 @@
-
-cmake_minimum_required(VERSION 3.14)
-
-enable_language (C)
-
-set (GENERATE_CONTENT [[
-macro (CHECK_VALUE test_msg value expected)
- if (NOT "${value}" STREQUAL "${expected}")
- string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
- endif()
-endmacro()
-]])
-
-add_executable(exec1 IMPORTED)
-add_library (shared1 SHARED IMPORTED)
-add_library (static1 STATIC IMPORTED)
-
-string (APPEND GENERATE_CONTENT [[
-
-check_value ("TARGET_OUTPUT_NAME executable default" "$<TARGET_OUTPUT_NAME:exec1>" "exec1")
-check_value ("TARGET_OUTPUT_NAME shared default" "$<TARGET_OUTPUT_NAME:shared1>" "shared1")
-check_value ("TARGET_LINKER_OUTPUT_NAME shared linker default" "$<TARGET_LINKER_OUTPUT_NAME:shared1>" "shared1")
-check_value ("TARGET_OUTPUT_NAME static default" "$<TARGET_OUTPUT_NAME:static1>" "static1")
-check_value ("TARGET_LINKER_OUTPUT_NAME static linker default" "$<TARGET_LINKER_OUTPUT_NAME:static1>" "static1")
-]])
-
-
-add_executable (exec2 IMPORTED)
-set_property (TARGET exec2 PROPERTY OUTPUT_NAME exec2_custom)
-add_library (shared2 SHARED IMPORTED)
-set_property (TARGET shared2 PROPERTY OUTPUT_NAME shared2_custom)
-add_library (static2 STATIC IMPORTED)
-set_property (TARGET static2 PROPERTY OUTPUT_NAME static2_custom)
-
-string (APPEND GENERATE_CONTENT [[
-
-check_value ("TARGET_OUTPUT_NAME executable custom" "$<TARGET_OUTPUT_NAME:exec2>" "exec2_custom")
-check_value ("TARGET_OUTPUT_NAME shared custom" "$<TARGET_OUTPUT_NAME:shared2>" "shared2_custom")
-check_value ("TARGET_LINKER_OUTPUT_NAME shared linker custom" "$<TARGET_LINKER_OUTPUT_NAME:shared2>" "shared2_custom")
-check_value ("TARGET_OUTPUT_NAME static custom" "$<TARGET_OUTPUT_NAME:static2>" "static2_custom")
-check_value ("TARGET_LINKER_OUTPUT_NAME static linker custom" "$<TARGET_LINKER_OUTPUT_NAME:static2>" "static2_custom")
-]])
-
-
-add_executable (exec3 IMPORTED)
-set_property (TARGET exec3 PROPERTY RUNTIME_OUTPUT_NAME exec3_runtime)
-set_property (TARGET exec3 PROPERTY LIBRARY_OUTPUT_NAME exec3_library)
-set_property (TARGET exec3 PROPERTY ARCHIVE_OUTPUT_NAME exec3_archive)
-set_property (TARGET exec3 PROPERTY PDB_NAME exec3_pdb)
-add_library (shared3 SHARED IMPORTED)
-set_property (TARGET shared3 PROPERTY RUNTIME_OUTPUT_NAME shared3_runtime)
-set_property (TARGET shared3 PROPERTY LIBRARY_OUTPUT_NAME shared3_library)
-set_property (TARGET shared3 PROPERTY ARCHIVE_OUTPUT_NAME shared3_archive)
-set_property (TARGET shared3 PROPERTY PDB_NAME shared3_pdb)
-add_library (static3 STATIC IMPORTED)
-set_property (TARGET static3 PROPERTY RUNTIME_OUTPUT_NAME static3_runtime)
-set_property (TARGET static3 PROPERTY LIBRARY_OUTPUT_NAME static3_library)
-set_property (TARGET static3 PROPERTY ARCHIVE_OUTPUT_NAME static3_archive)
-set_property (TARGET static3 PROPERTY PDB_NAME static3_pdb)
-
-string (APPEND GENERATE_CONTENT [[
-
-check_value ("TARGET_OUTPUT_NAME executable all properties" "$<TARGET_OUTPUT_NAME:exec3>" "exec3_runtime")
-check_value ("TARGET_OUTPUT_NAME shared all properties" "$<TARGET_OUTPUT_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_runtime,shared3_library>")
-check_value ("TARGET_LINKER_OUTPUT_NAME shared linker all properties" "$<TARGET_LINKER_OUTPUT_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_archive,shared3_library>")
-check_value ("TARGET_OUTPUT_NAME static all properties" "$<TARGET_OUTPUT_NAME:static3>" "static3_archive")
-check_value ("TARGET_LINKER_OUTPUT_NAME static linker all properties" "$<TARGET_LINKER_OUTPUT_NAME:static3>" "static3_archive")
-]])
-
-
-unset(GENERATE_CONDITION)
-get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
-if(_isMultiConfig)
- list(GET CMAKE_CONFIGURATION_TYPES 0 FIRST_CONFIG)
- set(GENERATE_CONDITION CONDITION $<CONFIG:${FIRST_CONFIG}>)
-endif()
-
-file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_OUTPUT_NAME-generated.cmake"
- CONTENT "${GENERATE_CONTENT}" ${GENERATE_CONDITION})
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-stderr.txt b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-stderr.txt
deleted file mode 100644
index 9672a99..0000000
--- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-stderr.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-CMake Error at TARGET_OUTPUT_NAME-non-valid-target.cmake:[0-9]+ \(file\):
- Error evaluating generator expression:
-
- \$<TARGET_OUTPUT_NAME:empty>
-
- Target "empty" is not an executable or library\.
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME.cmake
deleted file mode 100644
index b7bae15..0000000
--- a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME.cmake
+++ /dev/null
@@ -1,96 +0,0 @@
-
-cmake_minimum_required(VERSION 3.14)
-
-enable_language (C)
-
-set (GENERATE_CONTENT [[
-macro (CHECK_VALUE test_msg value expected)
- if (NOT "${value}" STREQUAL "${expected}")
- string (APPEND RunCMake_TEST_FAILED "${test_msg}: actual result:\n [${value}]\nbut expected:\n [${expected}]\n")
- endif()
-endmacro()
-]])
-
-add_executable (exec1 empty.c)
-add_library (shared1 SHARED empty.c)
-add_library (static1 STATIC empty.c)
-
-string (APPEND GENERATE_CONTENT [[
-
-check_value ("TARGET_OUTPUT_NAME executable default" "$<TARGET_OUTPUT_NAME:exec1>" "exec1")
-check_value ("TARGET_OUTPUT_NAME shared default" "$<TARGET_OUTPUT_NAME:shared1>" "shared1")
-check_value ("TARGET_LINKER_OUTPUT_NAME shared linker default" "$<TARGET_LINKER_OUTPUT_NAME:shared1>" "shared1")
-check_value ("TARGET_OUTPUT_NAME static default" "$<TARGET_OUTPUT_NAME:static1>" "static1")
-check_value ("TARGET_LINKER_OUTPUT_NAME static linker default" "$<TARGET_LINKER_OUTPUT_NAME:static1>" "static1")
-]])
-if (CMAKE_C_LINKER_SUPPORTS_PDB)
- string(APPEND GENERATE_CONTENT [[
-check_value ("TARGET_PDB_OUTPUT_NAME executable PDB default" "$<TARGET_PDB_OUTPUT_NAME:exec1>" "exec1")
-check_value ("TARGET_PDB_OUTPUT_NAME shared PDB default" "$<TARGET_PDB_OUTPUT_NAME:shared1>" "shared1")
-]])
-endif()
-
-
-add_executable (exec2 empty.c)
-set_property (TARGET exec2 PROPERTY OUTPUT_NAME exec2_custom)
-add_library (shared2 SHARED empty.c)
-set_property (TARGET shared2 PROPERTY OUTPUT_NAME shared2_custom)
-add_library (static2 STATIC empty.c)
-set_property (TARGET static2 PROPERTY OUTPUT_NAME static2_custom)
-
-string (APPEND GENERATE_CONTENT [[
-
-check_value ("TARGET_OUTPUT_NAME executable custom" "$<TARGET_OUTPUT_NAME:exec2>" "exec2_custom")
-check_value ("TARGET_OUTPUT_NAME shared custom" "$<TARGET_OUTPUT_NAME:shared2>" "shared2_custom")
-check_value ("TARGET_LINKER_OUTPUT_NAME shared linker custom" "$<TARGET_LINKER_OUTPUT_NAME:shared2>" "shared2_custom")
-check_value ("TARGET_OUTPUT_NAME static custom" "$<TARGET_OUTPUT_NAME:static2>" "static2_custom")
-check_value ("TARGET_LINKER_OUTPUT_NAME static linker custom" "$<TARGET_LINKER_OUTPUT_NAME:static2>" "static2_custom")
-]])
-if (CMAKE_C_LINKER_SUPPORTS_PDB)
- string (APPEND GENERATE_CONTENT [[
-check_value ("TARGET_PDB_OUTPUT_NAME executable PDB custom" "$<TARGET_PDB_OUTPUT_NAME:exec2>" "exec2_custom")
-check_value ("TARGET_PDB_OUTPUT_NAME shared PDB custom" "$<TARGET_PDB_OUTPUT_NAME:shared2>" "shared2_custom")
- ]])
-endif()
-
-add_executable (exec3 empty.c)
-set_property (TARGET exec3 PROPERTY RUNTIME_OUTPUT_NAME exec3_runtime)
-set_property (TARGET exec3 PROPERTY LIBRARY_OUTPUT_NAME exec3_library)
-set_property (TARGET exec3 PROPERTY ARCHIVE_OUTPUT_NAME exec3_archive)
-set_property (TARGET exec3 PROPERTY PDB_NAME exec3_pdb)
-add_library (shared3 SHARED empty.c)
-set_property (TARGET shared3 PROPERTY RUNTIME_OUTPUT_NAME shared3_runtime)
-set_property (TARGET shared3 PROPERTY LIBRARY_OUTPUT_NAME shared3_library)
-set_property (TARGET shared3 PROPERTY ARCHIVE_OUTPUT_NAME shared3_archive)
-set_property (TARGET shared3 PROPERTY PDB_NAME shared3_pdb)
-add_library (static3 STATIC empty.c)
-set_property (TARGET static3 PROPERTY RUNTIME_OUTPUT_NAME static3_runtime)
-set_property (TARGET static3 PROPERTY LIBRARY_OUTPUT_NAME static3_library)
-set_property (TARGET static3 PROPERTY ARCHIVE_OUTPUT_NAME static3_archive)
-set_property (TARGET static3 PROPERTY PDB_NAME static3_pdb)
-
-string (APPEND GENERATE_CONTENT [[
-
-check_value ("TARGET_OUTPUT_NAME executable all properties" "$<TARGET_OUTPUT_NAME:exec3>" "exec3_runtime")
-check_value ("TARGET_OUTPUT_NAME shared all properties" "$<TARGET_OUTPUT_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_runtime,shared3_library>")
-check_value ("TARGET_LINKER_OUTPUT_NAME shared linker all properties" "$<TARGET_LINKER_OUTPUT_NAME:shared3>" "$<IF:$<IN_LIST:$<PLATFORM_ID>,Windows$<SEMICOLON>CYGWIN>,shared3_archive,shared3_library>")
-check_value ("TARGET_OUTPUT_NAME static all properties" "$<TARGET_OUTPUT_NAME:static3>" "static3_archive")
-check_value ("TARGET_LINKER_OUTPUT_NAME static linker all properties" "$<TARGET_LINKER_OUTPUT_NAME:static3>" "static3_archive")
-]])
-if (CMAKE_C_LINKER_SUPPORTS_PDB)
- string (APPEND GENERATE_CONTENT [[
-check_value ("TARGET_PDB_OUTPUT_NAME executable PDB all properties" "$<TARGET_PDB_OUTPUT_NAME:exec3>" "exec3_pdb")
-check_value ("TARGET_PDB_OUTPUT_NAME shared PDB all properties" "$<TARGET_PDB_OUTPUT_NAME:shared3>" "shared3_pdb")
-]])
-endif()
-
-
-unset(GENERATE_CONDITION)
-get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
-if(_isMultiConfig)
- list(GET CMAKE_CONFIGURATION_TYPES 0 FIRST_CONFIG)
- set(GENERATE_CONDITION CONDITION $<CONFIG:${FIRST_CONFIG}>)
-endif()
-
-file (GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/TARGET_OUTPUT_NAME-generated.cmake"
- CONTENT "${GENERATE_CONTENT}" ${GENERATE_CONDITION})
diff --git a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME-check.cmake b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME-check.cmake
new file mode 100644
index 0000000..996d2d4
--- /dev/null
+++ b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME-check.cmake
@@ -0,0 +1,7 @@
+file(STRINGS ${RunCMake_TEST_BINARY_DIR}/test.txt TEST_TXT ENCODING UTF-8)
+
+list(GET TEST_TXT 0 PDB_FILE_BASE_NAME)
+
+if(NOT PDB_FILE_BASE_NAME MATCHES "empty")
+ set(RunCMake_TEST_FAILED "unexpected PDB_FILE_BASE_NAME [${PDB_FILE_BASE_NAME}]")
+endif()
diff --git a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME.cmake b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake
similarity index 88%
rename from Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME.cmake
rename to Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake
index ba70b43..cc53bdf 100644
--- a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME.cmake
+++ b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_FILE_BASE_NAME.cmake
@@ -11,6 +11,6 @@
file(GENERATE
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test.txt"
- CONTENT "$<TARGET_PDB_OUTPUT_NAME:empty>"
+ CONTENT "$<TARGET_PDB_FILE_BASE_NAME:empty>"
${GENERATE_CONDITION}
)
diff --git a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME-check.cmake b/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME-check.cmake
deleted file mode 100644
index 8d1103e..0000000
--- a/Tests/RunCMake/GeneratorExpression/ValidTarget-TARGET_PDB_OUTPUT_NAME-check.cmake
+++ /dev/null
@@ -1,7 +0,0 @@
-file(STRINGS ${RunCMake_TEST_BINARY_DIR}/test.txt TEST_TXT ENCODING UTF-8)
-
-list(GET TEST_TXT 0 PDB_OUTPUT_NAME)
-
-if(NOT PDB_OUTPUT_NAME MATCHES "empty")
- set(RunCMake_TEST_FAILED "unexpected PDB_OUTPUT_NAME [${PDB_OUTPUT_NAME}]")
-endif()
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW-result.txt
similarity index 100%
copy from Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt
copy to Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW-result.txt
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW-stderr.txt b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW-stderr.txt
new file mode 100644
index 0000000..803058d
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error in CMakeLists.txt:
+ MSVC_RUNTIME_LIBRARY value 'BogusValue' not known for this C compiler.$
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW.cmake b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW.cmake
new file mode 100644
index 0000000..c3ea2fd
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-NEW.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0091 NEW)
+include(CMP0091-common.cmake)
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-OLD.cmake b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-OLD.cmake
new file mode 100644
index 0000000..734cc9f
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-OLD.cmake
@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0091 OLD)
+include(CMP0091-common.cmake)
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-WARN.cmake b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-WARN.cmake
new file mode 100644
index 0000000..26f86a0
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-WARN.cmake
@@ -0,0 +1,2 @@
+
+include(CMP0091-common.cmake)
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-common.cmake b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-common.cmake
new file mode 100644
index 0000000..7827d2a
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMP0091-common.cmake
@@ -0,0 +1,37 @@
+enable_language(C)
+
+cmake_policy(GET CMP0091 cmp0091)
+if(cmp0091 STREQUAL "NEW")
+ if(NOT CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
+ message(SEND_ERROR "CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT not set under NEW behavior")
+ endif()
+else()
+ if(CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT)
+ message(SEND_ERROR "CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT is set under OLD behavior")
+ endif()
+endif()
+
+if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
+ if(CMAKE_C_FLAGS_DEBUG MATCHES "[/-]MDd( |$)")
+ set(have_MDd 1)
+ else()
+ set(have_MDd 0)
+ endif()
+ if(CMAKE_C_FLAGS_RELEASE MATCHES "[/-]MD( |$)")
+ set(have_MD 1)
+ else()
+ set(have_MD 0)
+ endif()
+ if(cmp0091 STREQUAL "NEW")
+ if(have_MDd OR have_MD)
+ message(SEND_ERROR "Have a -MD* flag under NEW behavior.")
+ endif()
+ else()
+ if(NOT (have_MDd AND have_MD))
+ message(SEND_ERROR "Do not have -MD* flags under OLD behavior.")
+ endif()
+ endif()
+endif()
+
+set(CMAKE_MSVC_RUNTIME_LIBRARY BogusValue)
+add_library(foo empty.c)
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/CMakeLists.txt b/Tests/RunCMake/MSVCRuntimeLibrary/CMakeLists.txt
new file mode 100644
index 0000000..3e470a2
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/CMakeLists.txt
@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.14)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/RunCMakeTest.cmake b/Tests/RunCMake/MSVCRuntimeLibrary/RunCMakeTest.cmake
new file mode 100644
index 0000000..fad18da
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/RunCMakeTest.cmake
@@ -0,0 +1,5 @@
+include(RunCMake)
+
+run_cmake(CMP0091-WARN)
+run_cmake(CMP0091-OLD)
+run_cmake(CMP0091-NEW)
diff --git a/Tests/RunCMake/MSVCRuntimeLibrary/empty.c b/Tests/RunCMake/MSVCRuntimeLibrary/empty.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/MSVCRuntimeLibrary/empty.c
diff --git a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
index df253a9..0ac589d 100644
--- a/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
+++ b/Tests/RunCMake/VS10Project/RunCMakeTest.cmake
@@ -2,6 +2,7 @@
run_cmake(VsCSharpCompilerOpts)
run_cmake(ExplicitCMakeLists)
+run_cmake(RuntimeLibrary)
run_cmake(SourceGroupCMakeLists)
run_cmake(VsConfigurationType)
@@ -18,3 +19,4 @@
run_cmake(VSCSharpDefines)
run_cmake(VsSdkDirectories)
run_cmake(VsGlobals)
+run_cmake(VsProjectImport)
diff --git a/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake b/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake
new file mode 100644
index 0000000..6b43d47
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/RuntimeLibrary-check.cmake
@@ -0,0 +1,34 @@
+macro(RuntimeLibrary_check tgt rtl_expect)
+ set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/${tgt}.vcxproj")
+ if(NOT EXISTS "${vcProjectFile}")
+ set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not exist.")
+ return()
+ endif()
+
+ set(HAVE_Runtimelibrary 0)
+
+ file(STRINGS "${vcProjectFile}" lines)
+ foreach(line IN LISTS lines)
+ if(line MATCHES "^ *<RuntimeLibrary>([^<>]+)</RuntimeLibrary>")
+ set(rtl_actual "${CMAKE_MATCH_1}")
+ if(NOT "${rtl_actual}" STREQUAL "${rtl_expect}")
+ set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj has RuntimeLibrary '${rtl_actual}', not '${rtl_expect}'.")
+ return()
+ endif()
+ set(HAVE_Runtimelibrary 1)
+ break()
+ endif()
+ endforeach()
+
+ if(NOT HAVE_Runtimelibrary)
+ set(RunCMake_TEST_FAILED "Project file ${tgt}.vcxproj does not have a RuntimeLibrary field.")
+ return()
+ endif()
+endmacro()
+
+RuntimeLibrary_check(default-C MultiThreadedDebugDLL)
+RuntimeLibrary_check(default-CXX MultiThreadedDebugDLL)
+RuntimeLibrary_check(MTd-C MultiThreadedDebug)
+RuntimeLibrary_check(MTd-CXX MultiThreadedDebug)
+RuntimeLibrary_check(MT-C MultiThreaded)
+RuntimeLibrary_check(MT-CXX MultiThreaded)
diff --git a/Tests/RunCMake/VS10Project/RuntimeLibrary.cmake b/Tests/RunCMake/VS10Project/RuntimeLibrary.cmake
new file mode 100644
index 0000000..6c77a25
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/RuntimeLibrary.cmake
@@ -0,0 +1,16 @@
+set(CMAKE_CONFIGURATION_TYPES Debug)
+cmake_policy(SET CMP0091 NEW)
+enable_language(C)
+enable_language(CXX)
+
+add_library(default-C empty.c)
+add_library(default-CXX empty.cxx)
+
+set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreadedDebug")
+add_library(MTd-C empty.c)
+add_library(MTd-CXX empty.cxx)
+
+add_library(MT-C empty.c)
+set_property(TARGET MT-C PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
+add_library(MT-CXX empty.cxx)
+set_property(TARGET MT-CXX PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
diff --git a/Tests/RunCMake/VS10Project/VsProjectImport-check.cmake b/Tests/RunCMake/VS10Project/VsProjectImport-check.cmake
new file mode 100644
index 0000000..e438bf4
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsProjectImport-check.cmake
@@ -0,0 +1,28 @@
+set(vcProjectFile "${RunCMake_TEST_BINARY_DIR}/foo.vcxproj")
+if(NOT EXISTS "${vcProjectFile}")
+ set(RunCMake_TEST_FAILED "Project file ${vcProjectFile} does not exist.")
+ return()
+endif()
+
+set(test1Import "path\\\\to\\\\nuget_packages\\\\Foo.1.0.0\\\\build\\\\Foo.props")
+set(test2Import "path\\\\to\\\\nuget_packages\\\\Bar.1.0.0\\\\build\\\\Bar.props")
+
+set(import1Found FALSE)
+set(import2Found FALSE)
+
+file(STRINGS "${vcProjectFile}" lines)
+
+foreach(i 1 2)
+ set(testImport "${test${i}Import}")
+ foreach(line IN LISTS lines)
+ if(line MATCHES "^ *<Import Project=\".*${test1Import}\" />$")
+ message(STATUS "foo.vcxproj is using project import ${testImport}")
+ set(import${i}Found TRUE)
+ endif()
+ endforeach()
+endforeach()
+
+if(NOT import1Found OR NOT import2Found)
+ set(RunCMake_TEST_FAILED "Imported project not found.")
+ return()
+endif()
diff --git a/Tests/RunCMake/VS10Project/VsProjectImport.cmake b/Tests/RunCMake/VS10Project/VsProjectImport.cmake
new file mode 100644
index 0000000..70bdded
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/VsProjectImport.cmake
@@ -0,0 +1,11 @@
+enable_language(CXX)
+add_library(foo foo.cpp)
+
+set(test1Import "path/to/nuget_packages/Foo.1.0.0/build/Foo.props")
+set(test2Import "path/to/nuget_packages/Bar.1.0.0/build/Bar.props")
+
+set_property(TARGET foo PROPERTY
+ VS_PROJECT_IMPORT
+ ${test1Import}
+ ${test2Import}
+ )
diff --git a/Tests/RunCMake/VS10Project/empty.c b/Tests/RunCMake/VS10Project/empty.c
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/empty.c
diff --git a/Tests/RunCMake/VS10Project/empty.cxx b/Tests/RunCMake/VS10Project/empty.cxx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tests/RunCMake/VS10Project/empty.cxx
diff --git a/Tests/RunCMake/string/Repeat.cmake b/Tests/RunCMake/string/Repeat.cmake
new file mode 100644
index 0000000..fc390aa
--- /dev/null
+++ b/Tests/RunCMake/string/Repeat.cmake
@@ -0,0 +1,45 @@
+string(REPEAT "q" 4 q_out)
+
+if(NOT DEFINED q_out)
+ message(FATAL_ERROR "q_out is not defined")
+endif()
+
+if(NOT q_out STREQUAL "qqqq")
+ message(FATAL_ERROR "unexpected result")
+endif()
+
+string(REPEAT "1234" 0 zero_out)
+
+if(NOT DEFINED zero_out)
+ message(FATAL_ERROR "zero_out is not defined")
+endif()
+
+if(NOT zero_out STREQUAL "")
+ message(FATAL_ERROR "unexpected result")
+endif()
+
+unset(zero_out)
+
+string(REPEAT "" 100 zero_out)
+
+if(NOT DEFINED zero_out)
+ message(FATAL_ERROR "zero_out is not defined")
+endif()
+
+if(NOT zero_out STREQUAL "")
+ message(FATAL_ERROR "unexpected result")
+endif()
+
+string(REPEAT "1" 1 one_out)
+
+if(NOT one_out STREQUAL "1")
+ message(FATAL_ERROR "unexpected result")
+endif()
+
+unset(one_out)
+
+string(REPEAT "one" 1 one_out)
+
+if(NOT one_out STREQUAL "one")
+ message(FATAL_ERROR "unexpected result")
+endif()
diff --git a/Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-result.txt b/Tests/RunCMake/string/RepeatNegativeCount-result.txt
similarity index 100%
copy from Tests/RunCMake/GeneratorExpression/TARGET_OUTPUT_NAME-non-valid-target-result.txt
copy to Tests/RunCMake/string/RepeatNegativeCount-result.txt
diff --git a/Tests/RunCMake/string/RepeatNegativeCount-stderr.txt b/Tests/RunCMake/string/RepeatNegativeCount-stderr.txt
new file mode 100644
index 0000000..bbd498e
--- /dev/null
+++ b/Tests/RunCMake/string/RepeatNegativeCount-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at RepeatNegativeCount.cmake:[0-9]+ \(string\):
+ repeat count is not a positive number.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/string/RepeatNegativeCount.cmake b/Tests/RunCMake/string/RepeatNegativeCount.cmake
new file mode 100644
index 0000000..769e7c0
--- /dev/null
+++ b/Tests/RunCMake/string/RepeatNegativeCount.cmake
@@ -0,0 +1 @@
+string(REPEAT "blah" -1 out)
diff --git a/Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt b/Tests/RunCMake/string/RepeatNoArgs-result.txt
similarity index 100%
copy from Tests/RunCMake/GeneratorExpression/ImportedTarget-TARGET_PDB_OUTPUT_NAME-result.txt
copy to Tests/RunCMake/string/RepeatNoArgs-result.txt
diff --git a/Tests/RunCMake/string/RepeatNoArgs-stderr.txt b/Tests/RunCMake/string/RepeatNoArgs-stderr.txt
new file mode 100644
index 0000000..5abcb3b
--- /dev/null
+++ b/Tests/RunCMake/string/RepeatNoArgs-stderr.txt
@@ -0,0 +1,4 @@
+CMake Error at RepeatNoArgs.cmake:[0-9]+ \(string\):
+ sub-command REPEAT requires three arguments.
+Call Stack \(most recent call first\):
+ CMakeLists.txt:[0-9]+ \(include\)
diff --git a/Tests/RunCMake/string/RepeatNoArgs.cmake b/Tests/RunCMake/string/RepeatNoArgs.cmake
new file mode 100644
index 0000000..e327e99
--- /dev/null
+++ b/Tests/RunCMake/string/RepeatNoArgs.cmake
@@ -0,0 +1 @@
+string(REPEAT)
diff --git a/Tests/RunCMake/string/RunCMakeTest.cmake b/Tests/RunCMake/string/RunCMakeTest.cmake
index 211337a..c432b4e 100644
--- a/Tests/RunCMake/string/RunCMakeTest.cmake
+++ b/Tests/RunCMake/string/RunCMakeTest.cmake
@@ -33,3 +33,7 @@
run_cmake(UTF-16LE)
run_cmake(UTF-32BE)
run_cmake(UTF-32LE)
+
+run_cmake(Repeat)
+run_cmake(RepeatNoArgs)
+run_cmake(RepeatNegativeCount)
diff --git a/bootstrap b/bootstrap
index d46b8a8..8b9c404 100755
--- a/bootstrap
+++ b/bootstrap
@@ -685,6 +685,7 @@
s/@KWSYS_NAME_IS_KWSYS@/${KWSYS_NAME_IS_KWSYS}/g;
s/@KWSYS_STL_HAS_WSTRING@/${KWSYS_STL_HAS_WSTRING}/g;
s/@KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H@/${KWSYS_CXX_HAS_EXT_STDIO_FILEBUF_H}/g;
+ s/@KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP@/${KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP}/g;
}" "${INFILE}" >> "${OUTFILE}${_tmp}"
if [ -f "${OUTFILE}${_tmp}" ]; then
if "${_diff}" "${OUTFILE}" "${OUTFILE}${_tmp}" > /dev/null 2> /dev/null ; then
@@ -1217,6 +1218,7 @@
KWSYS_CXX_HAS_ENVIRON_IN_STDLIB_H=0
KWSYS_CXX_HAS_UTIMENSAT=0
KWSYS_CXX_HAS_UTIMES=0
+KWSYS_SYSTEMTOOLS_USE_TRANSLATION_MAP=1
if cmake_try_run "${cmake_cxx_compiler}" \
"${cmake_cxx_flags} -DTEST_KWSYS_CXX_HAS_SETENV" \