Ver Fonte

Autogen: Add INTERFACE_AUTOMOC_MACRO_NAMES target property

Add this target property to specify macro names that propagate to
dependents as `AUTOMOC_MACRO_NAMES`.  The dependents will automatically
generate MOC files for source files that contain the inherited macro
names.

Co-Authored-By: Craig Scott <[email protected]>
Fixes: #19679
Orkun Tokdemir há 2 anos atrás
pai
commit
c5c3aff1f5

+ 1 - 0
Auxiliary/vim/syntax/cmake.vim

@@ -220,6 +220,7 @@ syn keyword cmakeProperty contained
             \ INSTALL_RPATH
             \ INSTALL_RPATH
             \ INSTALL_RPATH_USE_LINK_PATH
             \ INSTALL_RPATH_USE_LINK_PATH
             \ INTERFACE_AUTOUIC_OPTIONS
             \ INTERFACE_AUTOUIC_OPTIONS
+            \ INTERFACE_AUTOMOC_MACRO_NAMES
             \ INTERFACE_COMPILE_DEFINITIONS
             \ INTERFACE_COMPILE_DEFINITIONS
             \ INTERFACE_COMPILE_FEATURES
             \ INTERFACE_COMPILE_FEATURES
             \ INTERFACE_COMPILE_OPTIONS
             \ INTERFACE_COMPILE_OPTIONS

+ 1 - 0
Help/manual/cmake-properties.7.rst

@@ -273,6 +273,7 @@ Properties on Targets
    /prop_tgt/INSTALL_REMOVE_ENVIRONMENT_RPATH
    /prop_tgt/INSTALL_REMOVE_ENVIRONMENT_RPATH
    /prop_tgt/INSTALL_RPATH
    /prop_tgt/INSTALL_RPATH
    /prop_tgt/INSTALL_RPATH_USE_LINK_PATH
    /prop_tgt/INSTALL_RPATH_USE_LINK_PATH
+   /prop_tgt/INTERFACE_AUTOMOC_MACRO_NAMES
    /prop_tgt/INTERFACE_AUTOUIC_OPTIONS
    /prop_tgt/INTERFACE_AUTOUIC_OPTIONS
    /prop_tgt/INTERFACE_COMPILE_DEFINITIONS
    /prop_tgt/INTERFACE_COMPILE_DEFINITIONS
    /prop_tgt/INTERFACE_COMPILE_FEATURES
    /prop_tgt/INTERFACE_COMPILE_FEATURES

+ 6 - 2
Help/prop_tgt/AUTOMOC_MACRO_NAMES.rst

@@ -3,7 +3,7 @@ AUTOMOC_MACRO_NAMES
 
 
 .. versionadded:: 3.10
 .. versionadded:: 3.10
 
 
-A :ref:`semicolon-separated list <CMake Language Lists>` list of macro names used by
+A :ref:`semicolon-separated list <CMake Language Lists>` of macro names used by
 :prop_tgt:`AUTOMOC` to determine if a C++ file needs to be processed by ``moc``.
 :prop_tgt:`AUTOMOC` to determine if a C++ file needs to be processed by ``moc``.
 
 
 This property is only used if the :prop_tgt:`AUTOMOC` property is ``ON``
 This property is only used if the :prop_tgt:`AUTOMOC` property is ``ON``
@@ -21,6 +21,8 @@ then the file will be processed by ``moc``.
 By default ``AUTOMOC_MACRO_NAMES`` is initialized from
 By default ``AUTOMOC_MACRO_NAMES`` is initialized from
 :variable:`CMAKE_AUTOMOC_MACRO_NAMES`.
 :variable:`CMAKE_AUTOMOC_MACRO_NAMES`.
 
 
+See also the :prop_tgt:`INTERFACE_AUTOMOC_MACRO_NAMES` target property.
+
 See the :manual:`cmake-qt(7)` manual for more information on using CMake
 See the :manual:`cmake-qt(7)` manual for more information on using CMake
 with Qt.
 with Qt.
 
 
@@ -29,6 +31,8 @@ Example
 
 
 In this case the ``Q_OBJECT`` macro is hidden inside another macro
 In this case the ``Q_OBJECT`` macro is hidden inside another macro
 called ``CUSTOM_MACRO``.  To let CMake know that source files that contain
 called ``CUSTOM_MACRO``.  To let CMake know that source files that contain
-``CUSTOM_MACRO`` need to be ``moc`` processed, we call::
+``CUSTOM_MACRO`` need to be ``moc`` processed, we call:
+
+.. code-block:: cmake
 
 
   set_property(TARGET tgt APPEND PROPERTY AUTOMOC_MACRO_NAMES "CUSTOM_MACRO")
   set_property(TARGET tgt APPEND PROPERTY AUTOMOC_MACRO_NAMES "CUSTOM_MACRO")

+ 89 - 0
Help/prop_tgt/INTERFACE_AUTOMOC_MACRO_NAMES.rst

@@ -0,0 +1,89 @@
+INTERFACE_AUTOMOC_MACRO_NAMES
+-----------------------------
+
+.. versionadded:: 3.27
+
+A :ref:`semicolon-separated list <CMake Language Lists>` of macro names for
+:prop_tgt:`AUTOMOC` to be propagated to consumers.
+
+When a target with :prop_tgt:`AUTOMOC` enabled links to a library that sets
+``INTERFACE_AUTOMOC_MACRO_NAMES``, the target inherits the listed macro names
+and merges them with those specified in its own :prop_tgt:`AUTOMOC_MACRO_NAMES`
+property.  The target will then automatically generate MOC files for source
+files that contain the inherited macro names too, not just the macro names
+specified in its own :prop_tgt:`AUTOMOC_MACRO_NAMES` property.
+
+By default ``INTERFACE_AUTOMOC_MACRO_NAMES`` is empty.
+
+See the :manual:`cmake-qt(7)` manual for more information on using CMake
+with Qt.
+
+Example 1
+^^^^^^^^^
+
+In this example, ``myapp`` inherits the macro names ``STATIC_LIB_1`` and
+``STATIC_LIB_2`` from ``static_lib``.  The ``moc`` tool will then automatically
+be run on any of the ``myapp`` sources which contain ``STATIC_LIB_1`` or
+``STATIC_LIB_2``.
+
+.. code-block:: cmake
+
+  set(AUTOMOC ON)
+  add_executable(myapp main.cpp)
+  target_link_libraries(myapp PRIVATE static_lib)
+
+  add_library(static_lib STATIC static.cpp)
+  set_property(TARGET static_lib PROPERTY
+    INTERFACE_AUTOMOC_MACRO_NAMES "STATIC_LIB_1;STATIC_LIB_2"
+  )
+
+Example 2
+^^^^^^^^^
+
+In this example, the ``INTERFACE_AUTOMOC_MACRO_NAMES`` target property of the
+various ``*_deep_lib`` libraries will propagate to ``shared_lib``,
+``static_lib`` and ``interface_lib``.  Because the linking relationships are
+specified as ``PUBLIC`` and ``INTERFACE``, those macro names will also further
+propagate transitively up to ``app``.
+
+.. code-block:: cmake
+
+  set(AUTOMOC ON)
+
+  add_library(shared_deep_lib SHARED deep_lib.cpp)
+  add_library(static_deep_lib STATIC deep_lib.cpp)
+  add_library(interface_deep_lib INTERFACE)
+
+  set_property(TARGET shared_deep_lib PROPERTY
+    INTERFACE_AUTOMOC_MACRO_NAMES "SHARED_LINK_LIB"
+  )
+  set_property(TARGET static_deep_lib PROPERTY
+    INTERFACE_AUTOMOC_MACRO_NAMES "STATIC_LINK_LIB"
+  )
+  set_property(TARGET interface_deep_lib PROPERTY
+    INTERFACE_AUTOMOC_MACRO_NAMES "INTERFACE_LINK_LIB"
+  )
+
+  add_library(shared_lib SHARED lib.cpp)
+  add_library(static_lib STATIC lib.cpp)
+  add_library(interface_lib INTERFACE)
+
+  # PUBLIC and INTERFACE here ensure the macro names propagate to any
+  # consumers of shared_lib, static_lib or interface_lib too
+  target_link_libraries(shared_lib PUBLIC shared_deep_lib)
+  target_link_libraries(static_lib PUBLIC static_deep_lib)
+  target_link_libraries(interface_lib INTERFACE interface_deep_lib)
+
+  # This consumer will receive all three of the above custom macro names as
+  # transitive usage requirements
+  add_executable(app main.cpp)
+  target_link_libraries(app PRIVATE shared_lib static_lib interface_lib)
+
+In the above:
+
+* ``shared_lib`` sources will be processed by ``moc`` if they contain
+  ``SHARED_LINK_LIB``.
+* ``static_lib`` sources will be processed by ``moc`` if they contain
+  ``STATIC_LINK_LIB``.
+* ``app`` sources will be processed by ``moc`` if they contain
+  ``SHARED_LINK_LIB``, ``STATIC_LINK_LIB`` or ``INTERFACE_LINK_LIB``.

+ 5 - 0
Help/release/dev/automoc-macro-names.rst

@@ -0,0 +1,5 @@
+automoc-macro-names
+-------------------
+
+* The :prop_tgt:`INTERFACE_AUTOMOC_MACRO_NAMES` target property was added to
+  specify macro names for ``moc`` as a transitive usage requirement.

+ 3 - 0
Source/cmExportBuildFileGenerator.cxx

@@ -106,6 +106,9 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
     this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
     this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
                                     cmGeneratorExpression::BuildInterface,
                                     cmGeneratorExpression::BuildInterface,
                                     properties);
                                     properties);
+    this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", gte,
+                                    cmGeneratorExpression::BuildInterface,
+                                    properties);
     this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte,
     this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte,
                                     cmGeneratorExpression::BuildInterface,
                                     cmGeneratorExpression::BuildInterface,
                                     properties);
                                     properties);

+ 3 - 0
Source/cmExportInstallFileGenerator.cxx

@@ -110,6 +110,9 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
     this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gt,
     this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gt,
                                     cmGeneratorExpression::InstallInterface,
                                     cmGeneratorExpression::InstallInterface,
                                     properties);
                                     properties);
+    this->PopulateInterfaceProperty("INTERFACE_AUTOMOC_MACRO_NAMES", gt,
+                                    cmGeneratorExpression::InstallInterface,
+                                    properties);
     this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gt,
     this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gt,
                                     cmGeneratorExpression::InstallInterface,
                                     cmGeneratorExpression::InstallInterface,
                                     properties);
                                     properties);

+ 19 - 0
Source/cmQtAutoGenInitializer.cxx

@@ -28,8 +28,10 @@
 #include "cmAlgorithms.h"
 #include "cmAlgorithms.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommand.h"
 #include "cmCustomCommandLines.h"
 #include "cmCustomCommandLines.h"
+#include "cmEvaluatedTargetProperty.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratedFileStream.h"
 #include "cmGeneratorExpression.h"
 #include "cmGeneratorExpression.h"
+#include "cmGeneratorExpressionDAGChecker.h"
 #include "cmGeneratorTarget.h"
 #include "cmGeneratorTarget.h"
 #include "cmGlobalGenerator.h"
 #include "cmGlobalGenerator.h"
 #include "cmLinkItem.h"
 #include "cmLinkItem.h"
@@ -1691,6 +1693,23 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
     info.SetArray("MOC_OPTIONS", this->Moc.Options);
     info.SetArray("MOC_OPTIONS", this->Moc.Options);
     info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode);
     info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode);
     info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix);
     info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix);
+
+    cmGeneratorExpressionDAGChecker dagChecker(
+      this->GenTarget, "AUTOMOC_MACRO_NAMES", nullptr, nullptr);
+    EvaluatedTargetPropertyEntries InterfaceAutoMocMacroNamesEntries;
+
+    AddInterfaceEntries(this->GenTarget, this->ConfigDefault,
+                        "INTERFACE_AUTOMOC_MACRO_NAMES", "CXX", &dagChecker,
+                        InterfaceAutoMocMacroNamesEntries,
+                        IncludeRuntimeInterface::Yes);
+
+    for (auto const& entry : InterfaceAutoMocMacroNamesEntries.Entries) {
+      this->Moc.MacroNames.insert(this->Moc.MacroNames.end(),
+                                  entry.Values.begin(), entry.Values.end());
+    }
+    this->Moc.MacroNames.erase(cmRemoveDuplicates(this->Moc.MacroNames),
+                               this->Moc.MacroNames.end());
+
     info.SetArray("MOC_MACRO_NAMES", this->Moc.MacroNames);
     info.SetArray("MOC_MACRO_NAMES", this->Moc.MacroNames);
     info.SetArrayArray(
     info.SetArrayArray(
       "MOC_DEPEND_FILTERS", this->Moc.DependFilters,
       "MOC_DEPEND_FILTERS", this->Moc.DependFilters,

+ 61 - 0
Tests/QtAutogen/MocInterfaceMacroNames/CMakeLists.txt

@@ -0,0 +1,61 @@
+cmake_minimum_required(VERSION 3.16)
+project(MocInterfaceMacroNames)
+
+include("../AutogenCoreTest.cmake")
+
+set(CMAKE_AUTOMOC ON)
+
+add_executable(dummy dummy.cpp)
+target_link_libraries(dummy PRIVATE static_lib interface_lib shared_lib)
+
+add_library(shared_lib SHARED shared_lib.cpp)
+set_target_properties(shared_lib PROPERTIES INTERFACE_AUTOMOC_MACRO_NAMES "SHARED_LIB_MACRO")
+
+add_library(interface_lib INTERFACE)
+set_target_properties(interface_lib PROPERTIES INTERFACE_AUTOMOC_MACRO_NAMES "INTERFACE_LIB_MACRO")
+
+add_library(static_lib STATIC static_lib.cpp)
+set_target_properties(static_lib PROPERTIES INTERFACE_AUTOMOC_MACRO_NAMES "STATIC_LIB_MACRO")
+
+set(AUTOGEN_INFO_FILE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/dummy_autogen.dir/AutogenInfo.json")
+set(CHECK_AUTOGEN_JSON_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CheckAutogenJson.cmake")
+message(STATUS "AutogenInfo.json: ${AUTOGEN_INFO_FILE}")
+
+add_custom_command(TARGET dummy POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -DFILE_PATH=${AUTOGEN_INFO_FILE} -P ${CHECK_AUTOGEN_JSON_PATH}
+)
+
+install(TARGETS shared_lib EXPORT shared_lib)
+install(TARGETS interface_lib EXPORT interface_lib)
+install(TARGETS static_lib EXPORT static_lib)
+
+install(EXPORT shared_lib FILE shared_libTargets.cmake DESTINATION lib/cmake/shared_lib)
+install(EXPORT interface_lib FILE interface_libTargets.cmake DESTINATION lib/cmake/interface_lib)
+install(EXPORT static_lib FILE static_libTargets.cmake DESTINATION lib/cmake/static_lib)
+
+set(CHECK_EXPORT_TARGETS_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CheckExportTargets.cmake")
+set(EXPORT_FOLDER_PATH "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Export")
+
+add_custom_command(TARGET dummy POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -DFOLDER_PATH=${EXPORT_FOLDER_PATH} -P ${CHECK_EXPORT_TARGETS_PATH}
+)
+
+# check if INTERFACE_AUTOMOC_MACRO_NAMES were transferred to the *_link libraries correctly
+add_executable(dummy_link dummy.cpp)
+target_link_libraries(dummy_link PRIVATE static_link_lib interface_link_lib shared_link_lib)
+
+add_library(shared_link_lib SHARED shared_lib.cpp)
+target_link_libraries(shared_link_lib PUBLIC shared_lib)
+
+add_library(interface_link_lib INTERFACE)
+target_link_libraries(interface_link_lib INTERFACE interface_lib)
+
+add_library(static_link_lib STATIC static_lib.cpp)
+target_link_libraries(static_link_lib PUBLIC static_lib)
+
+set(AUTOGEN_INFO_FILE "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/dummy_link_autogen.dir/AutogenInfo.json")
+message(STATUS "AutogenInfo.json: ${AUTOGEN_INFO_FILE}")
+
+add_custom_command(TARGET dummy_link POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -DFILE_PATH=${AUTOGEN_INFO_FILE} -P ${CHECK_AUTOGEN_JSON_PATH}
+)

+ 27 - 0
Tests/QtAutogen/MocInterfaceMacroNames/CheckAutogenJson.cmake

@@ -0,0 +1,27 @@
+
+
+set(expected_values "SHARED_LIB_MACRO" "INTERFACE_LIB_MACRO" "STATIC_LIB_MACRO")
+function(checkAutoMocMacroNames FILE_PATH)
+  message(STATUS "Checking for auto moc macro names in ${FILE_PATH}")
+  file(READ ${FILE_PATH} FILE_CONTENT)
+  string(JSON MOC_MACRO_NAMES_ARR GET ${FILE_CONTENT} MOC_MACRO_NAMES)
+  # get the length of MOC_MACRO_NAMES in JSON
+  string(JSON MOC_MACRO_NAMES_LENGTH LENGTH ${MOC_MACRO_NAMES_ARR})
+  if(${MOC_MACRO_NAMES_LENGTH} EQUAL 0)
+      message(FATAL_ERROR "MOC_MACRO_NAMES is empty")
+  endif()
+  message(STATUS "MOC_MACRO_NAMES: ${MOC_MACRO_NAMES_ARR}")
+
+  math(EXPR last_index "${MOC_MACRO_NAMES_LENGTH} - 1")
+  set(reverse_index ${last_index})
+  foreach(expected_value IN LISTS expected_values)
+    string(JSON element GET ${MOC_MACRO_NAMES_ARR} ${reverse_index})
+    # check if element equals to expected value
+    if(NOT ${element} STREQUAL ${expected_value})
+      message(FATAL_ERROR "MOC_MACRO_NAMES is expected to contain ${expected_value} but contains ${element}")
+    endif()
+    math(EXPR reverse_index "${reverse_index} - 1")
+  endforeach()
+endfunction()
+
+checkAutoMocMacroNames(${FILE_PATH})

+ 45 - 0
Tests/QtAutogen/MocInterfaceMacroNames/CheckExportTargets.cmake

@@ -0,0 +1,45 @@
+
+set(TARGET_NAMES "static_lib" "shared_lib" "interface_lib")
+set(static_lib_FOUND "0")
+set(shared_lib_FOUND "0")
+set(interface_lib_FOUND "0")
+
+macro(checkExportTargets FOLDER_PATH)
+  message("Checking folder: ${FOLDER_PATH}")
+  file(GLOB sources_list LIST_DIRECTORIES true RELATIVE ${FOLDER_PATH} ${FOLDER_PATH}/*)
+  message("Found files and folders: ${sources_list}")
+  foreach(source ${sources_list})
+    set(SOURCE_ABS "${FOLDER_PATH}/${source}")
+    if(IS_DIRECTORY ${SOURCE_ABS})
+      message("Found subfolder: ${source}")
+      checkExportTargets(${SOURCE_ABS})
+    else()
+      message("Found file: ${source}")
+      foreach(TARGET_NAME ${TARGET_NAMES})
+        set(TARGETS_FILE "${TARGET_NAME}Targets.cmake")
+        if(${source} STREQUAL ${TARGETS_FILE})
+          message("Found ${TARGETS_FILE} in ${FOLDER_PATH}")
+          string(TOUPPER ${TARGET_NAME} TARGET_NAME_UPPER)
+          set(expected_macro "${TARGET_NAME_UPPER}_MACRO")
+          set(expected_string "INTERFACE_AUTOMOC_MACRO_NAMES \"${expected_macro}\"")
+          file(READ ${FOLDER_PATH}/${source} contents)
+          if (NOT contents MATCHES ${expected_string})
+            message(FATAL_ERROR "Expected ${expected_string} in ${FOLDER_PATH}/${source}")
+          else()
+            message("Found ${expected_string} in ${FOLDER_PATH}/${source}")
+            set(${TARGET_NAME}_FOUND "1")
+          endif()
+        endif()
+      endforeach()
+    endif()
+  endforeach()
+endmacro()
+
+checkExportTargets(${FOLDER_PATH})
+
+foreach(TARGET_NAME ${TARGET_NAMES})
+  # check if the target found equals the expected value
+  if(NOT ${TARGET_NAME}_FOUND STREQUAL "1")
+    message(FATAL_ERROR "Did not find ${TARGET_NAME}Targets.cmake")
+  endif()
+endforeach()

+ 4 - 0
Tests/QtAutogen/MocInterfaceMacroNames/dummy.cpp

@@ -0,0 +1,4 @@
+int main()
+{
+  return 0;
+}

+ 6 - 0
Tests/QtAutogen/MocInterfaceMacroNames/shared_lib.cpp

@@ -0,0 +1,6 @@
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+  void foo()
+{
+}

+ 3 - 0
Tests/QtAutogen/MocInterfaceMacroNames/static_lib.cpp

@@ -0,0 +1,3 @@
+void foo()
+{
+}

+ 1 - 0
Tests/QtAutogen/Tests.cmake

@@ -7,6 +7,7 @@ ADD_AUTOGEN_TEST(GlobalAutogenTarget)
 ADD_AUTOGEN_TEST(GlobalAutogenExecutable)
 ADD_AUTOGEN_TEST(GlobalAutogenExecutable)
 ADD_AUTOGEN_TEST(LowMinimumVersion lowMinimumVersion)
 ADD_AUTOGEN_TEST(LowMinimumVersion lowMinimumVersion)
 ADD_AUTOGEN_TEST(ManySources manySources)
 ADD_AUTOGEN_TEST(ManySources manySources)
+ADD_AUTOGEN_TEST(MocInterfaceMacroNames)
 ADD_AUTOGEN_TEST(MocOnly mocOnly)
 ADD_AUTOGEN_TEST(MocOnly mocOnly)
 ADD_AUTOGEN_TEST(MocOptions mocOptions)
 ADD_AUTOGEN_TEST(MocOptions mocOptions)
 ADD_AUTOGEN_TEST(ObjectLibrary someProgram)
 ADD_AUTOGEN_TEST(ObjectLibrary someProgram)