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 <craig.scott@crascit.com>
Fixes: #19679
This commit is contained in:
Orkun Tokdemir
2023-04-03 16:55:56 +02:00
committed by Craig Scott
parent 69cf9700e6
commit c5c3aff1f5
15 changed files with 274 additions and 2 deletions

View File

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

View File

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

View File

@@ -3,7 +3,7 @@ AUTOMOC_MACRO_NAMES
.. 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``.
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
: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
with Qt.
@@ -29,6 +31,8 @@ Example
In this case the ``Q_OBJECT`` macro is hidden inside another macro
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")

View File

@@ -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``.

View File

@@ -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.

View File

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

View File

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

View File

@@ -28,8 +28,10 @@
#include "cmAlgorithms.h"
#include "cmCustomCommand.h"
#include "cmCustomCommandLines.h"
#include "cmEvaluatedTargetProperty.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLinkItem.h"
@@ -1691,6 +1693,23 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
info.SetArray("MOC_OPTIONS", this->Moc.Options);
info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode);
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.SetArrayArray(
"MOC_DEPEND_FILTERS", this->Moc.DependFilters,

View File

@@ -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}
)

View File

@@ -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})

View File

@@ -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()

View File

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

View File

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

View File

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

View File

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