Browse Source

Merge topic 'automoc-macro-names'

c5c3aff1f5 Autogen: Add INTERFACE_AUTOMOC_MACRO_NAMES target property
69cf9700e6 Autogen: Defer setup until Generate step
7cecb6353e cmGeneratorTarget: Factor out EvaluatedTargetProperty infrastructure
2daba01ddf cmGeneratorTarget: Avoid incidental include-what-you-use warning
850b4d990c IWYU: Add mapping for 'std::remove_reference<Defer &>::type'

Acked-by: Kitware Robot <[email protected]>
Merge-request: !8391
Craig Scott 2 years ago
parent
commit
e245b4df75

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

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

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

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

+ 6 - 2
Help/prop_tgt/AUTOMOC_MACRO_NAMES.rst

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

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

+ 2 - 0
Source/CMakeLists.txt

@@ -193,6 +193,8 @@ add_library(
   cmDyndepCollation.h
   cmELF.h
   cmELF.cxx
+  cmEvaluatedTargetProperty.cxx
+  cmEvaluatedTargetProperty.h
   cmExprParserHelper.cxx
   cmExportBuildAndroidMKGenerator.h
   cmExportBuildAndroidMKGenerator.cxx

+ 111 - 0
Source/cmEvaluatedTargetProperty.cxx

@@ -0,0 +1,111 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmEvaluatedTargetProperty.h"
+
+#include <unordered_map>
+#include <utility>
+
+#include "cmGeneratorExpressionContext.h"
+#include "cmGeneratorTarget.h"
+#include "cmLinkItem.h"
+#include "cmStringAlgorithms.h"
+
+struct cmGeneratorExpressionDAGChecker;
+
+EvaluatedTargetPropertyEntry::EvaluatedTargetPropertyEntry(
+  cmLinkImplItem const& item, cmListFileBacktrace bt)
+  : LinkImplItem(item)
+  , Backtrace(std::move(bt))
+{
+}
+
+EvaluatedTargetPropertyEntry EvaluateTargetPropertyEntry(
+  cmGeneratorTarget const* thisTarget, std::string const& config,
+  std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
+  cmGeneratorTarget::TargetPropertyEntry& entry)
+{
+  EvaluatedTargetPropertyEntry ee(entry.LinkImplItem, entry.GetBacktrace());
+  cmExpandList(entry.Evaluate(thisTarget->GetLocalGenerator(), config,
+                              thisTarget, dagChecker, lang),
+               ee.Values);
+  if (entry.GetHadContextSensitiveCondition()) {
+    ee.ContextDependent = true;
+  }
+  return ee;
+}
+
+EvaluatedTargetPropertyEntries EvaluateTargetPropertyEntries(
+  cmGeneratorTarget const* thisTarget, std::string const& config,
+  std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
+  std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>> const&
+    in)
+{
+  EvaluatedTargetPropertyEntries out;
+  out.Entries.reserve(in.size());
+  for (auto const& entry : in) {
+    out.Entries.emplace_back(EvaluateTargetPropertyEntry(
+      thisTarget, config, lang, dagChecker, *entry));
+  }
+  return out;
+}
+
+namespace {
+void addInterfaceEntry(cmGeneratorTarget const* headTarget,
+                       std::string const& config, std::string const& prop,
+                       std::string const& lang,
+                       cmGeneratorExpressionDAGChecker* dagChecker,
+                       EvaluatedTargetPropertyEntries& entries,
+                       cmGeneratorTarget::LinkInterfaceFor interfaceFor,
+                       std::vector<cmLinkImplItem> const& libraries)
+{
+  for (cmLinkImplItem const& lib : libraries) {
+    if (lib.Target) {
+      EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
+      // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
+      // caller's property and hand-evaluate it as if it were compiled.
+      // Create a context as cmCompiledGeneratorExpression::Evaluate does.
+      cmGeneratorExpressionContext context(
+        headTarget->GetLocalGenerator(), config, false, headTarget, headTarget,
+        true, lib.Backtrace, lang);
+      cmExpandList(lib.Target->EvaluateInterfaceProperty(
+                     prop, &context, dagChecker, interfaceFor),
+                   ee.Values);
+      ee.ContextDependent = context.HadContextSensitiveCondition;
+      entries.Entries.emplace_back(std::move(ee));
+    }
+  }
+}
+}
+
+void AddInterfaceEntries(cmGeneratorTarget const* headTarget,
+                         std::string const& config, std::string const& prop,
+                         std::string const& lang,
+                         cmGeneratorExpressionDAGChecker* dagChecker,
+                         EvaluatedTargetPropertyEntries& entries,
+                         IncludeRuntimeInterface searchRuntime,
+                         cmGeneratorTarget::LinkInterfaceFor interfaceFor)
+{
+  if (searchRuntime == IncludeRuntimeInterface::Yes) {
+    if (cmLinkImplementation const* impl =
+          headTarget->GetLinkImplementation(config, interfaceFor)) {
+      entries.HadContextSensitiveCondition =
+        impl->HadContextSensitiveCondition;
+
+      auto runtimeLibIt = impl->LanguageRuntimeLibraries.find(lang);
+      if (runtimeLibIt != impl->LanguageRuntimeLibraries.end()) {
+        addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
+                          interfaceFor, runtimeLibIt->second);
+      }
+      addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
+                        interfaceFor, impl->Libraries);
+    }
+  } else {
+    if (cmLinkImplementationLibraries const* impl =
+          headTarget->GetLinkImplementationLibraries(config, interfaceFor)) {
+      entries.HadContextSensitiveCondition =
+        impl->HadContextSensitiveCondition;
+      addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
+                        interfaceFor, impl->Libraries);
+    }
+  }
+}

+ 80 - 0
Source/cmEvaluatedTargetProperty.h

@@ -0,0 +1,80 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "cmGeneratorTarget.h"
+#include "cmListFileCache.h"
+
+class cmLinkImplItem;
+struct cmGeneratorExpressionDAGChecker;
+
+// Represent a target property entry after evaluating generator expressions
+// and splitting up lists.
+struct EvaluatedTargetPropertyEntry
+{
+  EvaluatedTargetPropertyEntry(cmLinkImplItem const& item,
+                               cmListFileBacktrace bt);
+
+  // Move-only.
+  EvaluatedTargetPropertyEntry(EvaluatedTargetPropertyEntry&&) = default;
+  EvaluatedTargetPropertyEntry(EvaluatedTargetPropertyEntry const&) = delete;
+  EvaluatedTargetPropertyEntry& operator=(EvaluatedTargetPropertyEntry&&) =
+    delete;
+  EvaluatedTargetPropertyEntry& operator=(
+    EvaluatedTargetPropertyEntry const&) = delete;
+
+  cmLinkImplItem const& LinkImplItem;
+  cmListFileBacktrace Backtrace;
+  std::vector<std::string> Values;
+  bool ContextDependent = false;
+};
+
+EvaluatedTargetPropertyEntry EvaluateTargetPropertyEntry(
+  cmGeneratorTarget const* thisTarget, std::string const& config,
+  std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
+  cmGeneratorTarget::TargetPropertyEntry& entry);
+
+struct EvaluatedTargetPropertyEntries
+{
+  std::vector<EvaluatedTargetPropertyEntry> Entries;
+  bool HadContextSensitiveCondition = false;
+};
+
+EvaluatedTargetPropertyEntries EvaluateTargetPropertyEntries(
+  cmGeneratorTarget const* thisTarget, std::string const& config,
+  std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
+  std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>> const&
+    in);
+
+// IncludeRuntimeInterface is used to break the cycle in computing
+// the necessary transitive dependencies of targets that can occur
+// now that we have implicit language runtime targets.
+//
+// To determine the set of languages that a target has we need to iterate
+// all the sources which includes transitive INTERFACE sources.
+// Therefore we can't determine what language runtimes are needed
+// for a target until after all sources are computed.
+//
+// Therefore while computing the applicable INTERFACE_SOURCES we
+// must ignore anything in LanguageRuntimeLibraries or we would
+// create a cycle ( INTERFACE_SOURCES requires LanguageRuntimeLibraries,
+// LanguageRuntimeLibraries requires INTERFACE_SOURCES).
+//
+enum class IncludeRuntimeInterface
+{
+  Yes,
+  No
+};
+
+void AddInterfaceEntries(cmGeneratorTarget const* headTarget,
+                         std::string const& config, std::string const& prop,
+                         std::string const& lang,
+                         cmGeneratorExpressionDAGChecker* dagChecker,
+                         EvaluatedTargetPropertyEntries& entries,
+                         IncludeRuntimeInterface searchRuntime,
+                         cmGeneratorTarget::LinkInterfaceFor interfaceFor =
+                           cmGeneratorTarget::LinkInterfaceFor::Usage);

+ 3 - 0
Source/cmExportBuildFileGenerator.cxx

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

+ 3 - 0
Source/cmExportInstallFileGenerator.cxx

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

+ 39 - 191
Source/cmGeneratorTarget.cxx

@@ -27,6 +27,7 @@
 #include "cmAlgorithms.h"
 #include "cmComputeLinkInformation.h"
 #include "cmCustomCommandGenerator.h"
+#include "cmEvaluatedTargetProperty.h"
 #include "cmFileSet.h"
 #include "cmFileTimes.h"
 #include "cmGeneratedFileStream.h"
@@ -89,31 +90,38 @@ cmTargetPropertyComputer::ComputeLocation<cmGeneratorTarget>(
   return tgt->GetLocation(config);
 }
 
-class cmGeneratorTarget::TargetPropertyEntry
-{
-protected:
-  static cmLinkImplItem NoLinkImplItem;
+cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem;
 
+class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry
+{
 public:
-  TargetPropertyEntry(cmLinkImplItem const& item)
-    : LinkImplItem(item)
+  TargetPropertyEntryString(BT<std::string> propertyValue,
+                            cmLinkImplItem const& item = NoLinkImplItem)
+    : cmGeneratorTarget::TargetPropertyEntry(item)
+    , PropertyValue(std::move(propertyValue))
   {
   }
-  virtual ~TargetPropertyEntry() = default;
 
-  virtual const std::string& Evaluate(
-    cmLocalGenerator* lg, const std::string& config,
-    cmGeneratorTarget const* headTarget,
-    cmGeneratorExpressionDAGChecker* dagChecker,
-    std::string const& language) const = 0;
+  const std::string& Evaluate(cmLocalGenerator*, const std::string&,
+                              cmGeneratorTarget const*,
+                              cmGeneratorExpressionDAGChecker*,
+                              std::string const&) const override
+  {
+    return this->PropertyValue.Value;
+  }
 
-  virtual cmListFileBacktrace GetBacktrace() const = 0;
-  virtual std::string const& GetInput() const = 0;
-  virtual bool GetHadContextSensitiveCondition() const { return false; }
+  cmListFileBacktrace GetBacktrace() const override
+  {
+    return this->PropertyValue.Backtrace;
+  }
+  std::string const& GetInput() const override
+  {
+    return this->PropertyValue.Value;
+  }
 
-  cmLinkImplItem const& LinkImplItem;
+private:
+  BT<std::string> PropertyValue;
 };
-cmLinkImplItem cmGeneratorTarget::TargetPropertyEntry::NoLinkImplItem;
 
 class TargetPropertyEntryGenex : public cmGeneratorTarget::TargetPropertyEntry
 {
@@ -150,37 +158,6 @@ private:
   const std::unique_ptr<cmCompiledGeneratorExpression> ge;
 };
 
-class TargetPropertyEntryString : public cmGeneratorTarget::TargetPropertyEntry
-{
-public:
-  TargetPropertyEntryString(BT<std::string> propertyValue,
-                            cmLinkImplItem const& item = NoLinkImplItem)
-    : cmGeneratorTarget::TargetPropertyEntry(item)
-    , PropertyValue(std::move(propertyValue))
-  {
-  }
-
-  const std::string& Evaluate(cmLocalGenerator*, const std::string&,
-                              cmGeneratorTarget const*,
-                              cmGeneratorExpressionDAGChecker*,
-                              std::string const&) const override
-  {
-    return this->PropertyValue.Value;
-  }
-
-  cmListFileBacktrace GetBacktrace() const override
-  {
-    return this->PropertyValue.Backtrace;
-  }
-  std::string const& GetInput() const override
-  {
-    return this->PropertyValue.Value;
-  }
-
-private:
-  BT<std::string> PropertyValue;
-};
-
 class TargetPropertyEntryFileSet
   : public cmGeneratorTarget::TargetPropertyEntry
 {
@@ -263,6 +240,18 @@ std::unique_ptr<
     cm::make_unique<TargetPropertyEntryString>(propertyValue));
 }
 
+cmGeneratorTarget::TargetPropertyEntry::TargetPropertyEntry(
+  cmLinkImplItem const& item)
+  : LinkImplItem(item)
+{
+}
+
+bool cmGeneratorTarget::TargetPropertyEntry::GetHadContextSensitiveCondition()
+  const
+{
+  return false;
+}
+
 static void CreatePropertyGeneratorExpressions(
   cmake& cmakeInstance, cmBTStringRange entries,
   std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>>& items,
@@ -274,69 +263,6 @@ static void CreatePropertyGeneratorExpressions(
   }
 }
 
-namespace {
-// Represent a target property entry after evaluating generator expressions
-// and splitting up lists.
-struct EvaluatedTargetPropertyEntry
-{
-  EvaluatedTargetPropertyEntry(cmLinkImplItem const& item,
-                               cmListFileBacktrace bt)
-    : LinkImplItem(item)
-    , Backtrace(std::move(bt))
-  {
-  }
-
-  // Move-only.
-  EvaluatedTargetPropertyEntry(EvaluatedTargetPropertyEntry&&) = default;
-  EvaluatedTargetPropertyEntry(EvaluatedTargetPropertyEntry const&) = delete;
-  EvaluatedTargetPropertyEntry& operator=(EvaluatedTargetPropertyEntry&&) =
-    delete;
-  EvaluatedTargetPropertyEntry& operator=(
-    EvaluatedTargetPropertyEntry const&) = delete;
-
-  cmLinkImplItem const& LinkImplItem;
-  cmListFileBacktrace Backtrace;
-  std::vector<std::string> Values;
-  bool ContextDependent = false;
-};
-
-EvaluatedTargetPropertyEntry EvaluateTargetPropertyEntry(
-  cmGeneratorTarget const* thisTarget, std::string const& config,
-  std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
-  cmGeneratorTarget::TargetPropertyEntry& entry)
-{
-  EvaluatedTargetPropertyEntry ee(entry.LinkImplItem, entry.GetBacktrace());
-  cmExpandList(entry.Evaluate(thisTarget->GetLocalGenerator(), config,
-                              thisTarget, dagChecker, lang),
-               ee.Values);
-  if (entry.GetHadContextSensitiveCondition()) {
-    ee.ContextDependent = true;
-  }
-  return ee;
-}
-
-struct EvaluatedTargetPropertyEntries
-{
-  std::vector<EvaluatedTargetPropertyEntry> Entries;
-  bool HadContextSensitiveCondition = false;
-};
-
-EvaluatedTargetPropertyEntries EvaluateTargetPropertyEntries(
-  cmGeneratorTarget const* thisTarget, std::string const& config,
-  std::string const& lang, cmGeneratorExpressionDAGChecker* dagChecker,
-  std::vector<std::unique_ptr<cmGeneratorTarget::TargetPropertyEntry>> const&
-    in)
-{
-  EvaluatedTargetPropertyEntries out;
-  out.Entries.reserve(in.size());
-  for (auto const& entry : in) {
-    out.Entries.emplace_back(EvaluateTargetPropertyEntry(
-      thisTarget, config, lang, dagChecker, *entry));
-  }
-  return out;
-}
-}
-
 cmGeneratorTarget::cmGeneratorTarget(cmTarget* t, cmLocalGenerator* lg)
   : Target(t)
 {
@@ -1607,84 +1533,6 @@ void AddLangSpecificImplicitIncludeDirectories(
   }
 }
 
-void addInterfaceEntry(cmGeneratorTarget const* headTarget,
-                       std::string const& config, std::string const& prop,
-                       std::string const& lang,
-                       cmGeneratorExpressionDAGChecker* dagChecker,
-                       EvaluatedTargetPropertyEntries& entries,
-                       LinkInterfaceFor interfaceFor,
-                       std::vector<cmLinkImplItem> const& libraries)
-{
-  for (cmLinkImplItem const& lib : libraries) {
-    if (lib.Target) {
-      EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
-      // Pretend $<TARGET_PROPERTY:lib.Target,prop> appeared in our
-      // caller's property and hand-evaluate it as if it were compiled.
-      // Create a context as cmCompiledGeneratorExpression::Evaluate does.
-      cmGeneratorExpressionContext context(
-        headTarget->GetLocalGenerator(), config, false, headTarget, headTarget,
-        true, lib.Backtrace, lang);
-      cmExpandList(lib.Target->EvaluateInterfaceProperty(
-                     prop, &context, dagChecker, interfaceFor),
-                   ee.Values);
-      ee.ContextDependent = context.HadContextSensitiveCondition;
-      entries.Entries.emplace_back(std::move(ee));
-    }
-  }
-}
-
-// IncludeRuntimeInterface is used to break the cycle in computing
-// the necessary transitive dependencies of targets that can occur
-// now that we have implicit language runtime targets.
-//
-// To determine the set of languages that a target has we need to iterate
-// all the sources which includes transitive INTERFACE sources.
-// Therefore we can't determine what language runtimes are needed
-// for a target until after all sources are computed.
-//
-// Therefore while computing the applicable INTERFACE_SOURCES we
-// must ignore anything in LanguageRuntimeLibraries or we would
-// create a cycle ( INTERFACE_SOURCES requires LanguageRuntimeLibraries,
-// LanguageRuntimeLibraries requires INTERFACE_SOURCES).
-//
-enum class IncludeRuntimeInterface
-{
-  Yes,
-  No
-};
-void AddInterfaceEntries(
-  cmGeneratorTarget const* headTarget, std::string const& config,
-  std::string const& prop, std::string const& lang,
-  cmGeneratorExpressionDAGChecker* dagChecker,
-  EvaluatedTargetPropertyEntries& entries,
-  IncludeRuntimeInterface searchRuntime,
-  LinkInterfaceFor interfaceFor = LinkInterfaceFor::Usage)
-{
-  if (searchRuntime == IncludeRuntimeInterface::Yes) {
-    if (cmLinkImplementation const* impl =
-          headTarget->GetLinkImplementation(config, interfaceFor)) {
-      entries.HadContextSensitiveCondition =
-        impl->HadContextSensitiveCondition;
-
-      auto runtimeLibIt = impl->LanguageRuntimeLibraries.find(lang);
-      if (runtimeLibIt != impl->LanguageRuntimeLibraries.end()) {
-        addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
-                          interfaceFor, runtimeLibIt->second);
-      }
-      addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
-                        interfaceFor, impl->Libraries);
-    }
-  } else {
-    if (cmLinkImplementationLibraries const* impl =
-          headTarget->GetLinkImplementationLibraries(config, interfaceFor)) {
-      entries.HadContextSensitiveCondition =
-        impl->HadContextSensitiveCondition;
-      addInterfaceEntry(headTarget, config, prop, lang, dagChecker, entries,
-                        interfaceFor, impl->Libraries);
-    }
-  }
-}
-
 void AddObjectEntries(cmGeneratorTarget const* headTarget,
                       std::string const& config,
                       cmGeneratorExpressionDAGChecker* dagChecker,
@@ -8977,10 +8825,10 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
 
   cmGeneratedFileStream fout(filename);
   fout.SetCopyIfDifferent(true);
-  // IWYU pragma: associated allows include what you use to
+  // The IWYU "associated" pragma tells include-what-you-use to
   // consider the headerFile as part of the entire language
   // unit within include-what-you-use and as a result allows
-  // one to get IWYU advice for headers :)
+  // one to get IWYU advice for headers.
   fout << "#include <" << headerFilename << "> // IWYU pragma: associated\n";
   fout.close();
 

+ 22 - 0
Source/cmGeneratorTarget.h

@@ -1291,3 +1291,25 @@ private:
   };
   mutable std::map<std::string, InfoByConfig> Configs;
 };
+
+class cmGeneratorTarget::TargetPropertyEntry
+{
+protected:
+  static cmLinkImplItem NoLinkImplItem;
+
+public:
+  TargetPropertyEntry(cmLinkImplItem const& item);
+  virtual ~TargetPropertyEntry() = default;
+
+  virtual const std::string& Evaluate(
+    cmLocalGenerator* lg, const std::string& config,
+    cmGeneratorTarget const* headTarget,
+    cmGeneratorExpressionDAGChecker* dagChecker,
+    std::string const& language) const = 0;
+
+  virtual cmListFileBacktrace GetBacktrace() const = 0;
+  virtual std::string const& GetInput() const = 0;
+  virtual bool GetHadContextSensitiveCondition() const;
+
+  cmLinkImplItem const& LinkImplItem;
+};

+ 14 - 8
Source/cmGlobalGenerator.cxx

@@ -1544,8 +1544,9 @@ bool cmGlobalGenerator::Compute()
   }
 
 #ifndef CMAKE_BOOTSTRAP
-  cmQtAutoGenGlobalInitializer qtAutoGen(this->LocalGenerators);
-  if (!qtAutoGen.InitializeCustomTargets()) {
+  this->QtAutoGen =
+    cm::make_unique<cmQtAutoGenGlobalInitializer>(this->LocalGenerators);
+  if (!this->QtAutoGen->InitializeCustomTargets()) {
     return false;
   }
 #endif
@@ -1565,12 +1566,6 @@ bool cmGlobalGenerator::Compute()
     }
   }
 
-#ifndef CMAKE_BOOTSTRAP
-  if (!qtAutoGen.SetupCustomTargets()) {
-    return false;
-  }
-#endif
-
   for (const auto& localGen : this->LocalGenerators) {
     cmMakefile* mf = localGen->GetMakefile();
     for (const auto& g : mf->GetInstallGenerators()) {
@@ -1635,6 +1630,17 @@ void cmGlobalGenerator::Generate()
 
   this->CMakeInstance->UpdateProgress("Generating", 0.1f);
 
+#ifndef CMAKE_BOOTSTRAP
+  if (!this->QtAutoGen->SetupCustomTargets()) {
+    if (!cmSystemTools::GetErrorOccurredFlag()) {
+      this->GetCMakeInstance()->IssueMessage(
+        MessageType::FATAL_ERROR,
+        "Problem setting up custom targets for QtAutoGen");
+    }
+    return;
+  }
+#endif
+
   // Generate project files
   for (unsigned int i = 0; i < this->LocalGenerators.size(); ++i) {
     this->SetCurrentMakefile(this->LocalGenerators[i]->GetMakefile());

+ 6 - 0
Source/cmGlobalGenerator.h

@@ -50,6 +50,7 @@ class cmLinkLineComputer;
 class cmLocalGenerator;
 class cmMakefile;
 class cmOutputConverter;
+class cmQtAutoGenGlobalInitializer;
 class cmSourceFile;
 class cmState;
 class cmStateDirectory;
@@ -683,6 +684,11 @@ protected:
   cmake* CMakeInstance;
   std::vector<std::unique_ptr<cmMakefile>> Makefiles;
   LocalGeneratorVector LocalGenerators;
+
+#ifndef CMAKE_BOOTSTRAP
+  std::unique_ptr<cmQtAutoGenGlobalInitializer> QtAutoGen;
+#endif
+
   cmMakefile* CurrentConfigureMakefile;
   // map from project name to vector of local generators in that project
   std::map<std::string, std::vector<cmLocalGenerator*>> ProjectMap;

+ 19 - 0
Source/cmQtAutoGenInitializer.cxx

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

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

+ 1 - 0
Utilities/IWYU/mapping.imp

@@ -99,6 +99,7 @@
   { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<60, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<1, 1000> > >::type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "__gnu_cxx::__enable_if<true, bool>::__type", private, "\"cmConfigure.h\"", public ] },
+  { symbol: [ "std::remove_reference<Defer &>::type", private, "\"cmConfigure.h\"", public ] },
 
   # Wrappers for 3rd-party libraries
   { include: [ "@<.*curl/curlver.h>", private, "<cm3p/curl/curl.h>", public ] },

+ 1 - 0
bootstrap

@@ -338,6 +338,7 @@ CMAKE_CXX_SOURCES="\
   cmELF \
   cmEnableLanguageCommand \
   cmEnableTestingCommand \
+  cmEvaluatedTargetProperty \
   cmExecProgramCommand \
   cmExecuteProcessCommand \
   cmExpandedCommandArgument \