Browse Source

Merge topic 'qt-autogen-per-config'

20e4db4a66 cmGeneratorTarget: Make GetConfigCommonSourceFiles Xcode-specific
92d7b456e5 Autogen: Add support for per-config sources
3ffebbaefb Tests/QtAutogen: Forward build configuration in multi-config generators

Acked-by: Kitware Robot <[email protected]>
Merge-request: !5624
Brad King 5 years ago
parent
commit
6d7621baea

+ 5 - 2
Help/manual/cmake-qt.7.rst

@@ -48,6 +48,8 @@ and ``rcc`` for virtual file system content generation.  These tools may be
 automatically invoked by :manual:`cmake(1)` if the appropriate conditions
 are met.  The automatic tool invocation may be used with both Qt 4 and Qt 5.
 
+.. _`Qt AUTOMOC`:
+
 AUTOMOC
 ^^^^^^^
 
@@ -77,8 +79,9 @@ automatically added to the target's :prop_tgt:`INCLUDE_DIRECTORIES`.
 
 Not included ``moc_<basename>.cpp`` files will be generated in custom
 folders to avoid name collisions and included in a separate
-``<AUTOGEN_BUILD_DIR>/mocs_compilation.cpp`` file which is compiled
-into the target.
+file which is compiled into the target, named either
+``<AUTOGEN_BUILD_DIR>/mocs_compilation.cpp`` or
+``<AUTOGEN_BUILD_DIR>/mocs_compilation_$<CONFIG>.cpp``.
 
 * See :prop_tgt:`AUTOGEN_BUILD_DIR`.
 

+ 2 - 1
Help/prop_tgt/AUTOMOC.rst

@@ -137,7 +137,8 @@ parent directory path of the ``moc`` input file.  This scheme allows to have
 All not included ``moc`` output files will be included automatically by the
 CMake generated file
 
-- ``<AUTOGEN_BUILD_DIR>/mocs_compilation.cpp``,
+- ``<AUTOGEN_BUILD_DIR>/mocs_compilation.cpp``, or
+- ``<AUTOGEN_BUILD_DIR>/mocs_compilation_$<CONFIG>.cpp``,
 
 which is added to the target's sources.
 

+ 4 - 0
Help/release/dev/qt-autogen-per-config.rst

@@ -0,0 +1,4 @@
+qt-autogen-per-config
+---------------------
+
+* The :ref:`Qt AUTOMOC` feature now works with per-config sources.

+ 1 - 1
Source/cmGeneratorTarget.cxx

@@ -7039,7 +7039,7 @@ const cmLinkImplementation* cmGeneratorTarget::GetLinkImplementation(
   return &impl;
 }
 
-bool cmGeneratorTarget::GetConfigCommonSourceFiles(
+bool cmGeneratorTarget::GetConfigCommonSourceFilesForXcode(
   std::vector<cmSourceFile*>& files) const
 {
   std::vector<std::string> const& configs =

+ 3 - 2
Source/cmGeneratorTarget.h

@@ -430,8 +430,9 @@ public:
 
   /** Get source files common to all configurations and diagnose cases
       with per-config sources.  Excludes sources added by a TARGET_OBJECTS
-      generator expression.  */
-  bool GetConfigCommonSourceFiles(std::vector<cmSourceFile*>& files) const;
+      generator expression.  Do not use outside the Xcode generator.  */
+  bool GetConfigCommonSourceFilesForXcode(
+    std::vector<cmSourceFile*>& files) const;
 
   bool HaveBuildTreeRPATH(const std::string& config) const;
 

+ 4 - 4
Source/cmGlobalXCodeGenerator.cxx

@@ -1384,7 +1384,7 @@ bool cmGlobalXCodeGenerator::CreateXCodeTarget(
 
   // organize the sources
   std::vector<cmSourceFile*> commonSourceFiles;
-  if (!gtgt->GetConfigCommonSourceFiles(commonSourceFiles)) {
+  if (!gtgt->GetConfigCommonSourceFilesForXcode(commonSourceFiles)) {
     return false;
   }
 
@@ -1748,7 +1748,7 @@ void cmGlobalXCodeGenerator::CreateCustomCommands(
       this->CreateRunScriptBuildPhase("CMake PostBuild Rules", postbuild);
   } else {
     std::vector<cmSourceFile*> classes;
-    if (!gtgt->GetConfigCommonSourceFiles(classes)) {
+    if (!gtgt->GetConfigCommonSourceFilesForXcode(classes)) {
       return;
     }
     // add all the sources
@@ -1821,7 +1821,7 @@ void cmGlobalXCodeGenerator::CreateRunScriptBuildPhases(
   cmXCodeObject* buildPhases, cmGeneratorTarget const* gt)
 {
   std::vector<cmSourceFile*> sources;
-  if (!gt->GetConfigCommonSourceFiles(sources)) {
+  if (!gt->GetConfigCommonSourceFilesForXcode(sources)) {
     return;
   }
   auto& visited = this->CommandsVisited[gt];
@@ -2964,7 +2964,7 @@ cmXCodeObject* cmGlobalXCodeGenerator::CreateUtilityTarget(
   if (gtgt->GetType() != cmStateEnums::GLOBAL_TARGET &&
       gtgt->GetName() != CMAKE_CHECK_BUILD_SYSTEM_TARGET) {
     std::vector<cmSourceFile*> sources;
-    if (!gtgt->GetConfigCommonSourceFiles(sources)) {
+    if (!gtgt->GetConfigCommonSourceFilesForXcode(sources)) {
       return nullptr;
     }
 

+ 88 - 39
Source/cmQtAutoGenInitializer.cxx

@@ -17,6 +17,7 @@
 #include <cm/iterator>
 #include <cm/memory>
 #include <cmext/algorithm>
+#include <cmext/string_view>
 
 #include <cm3p/json/value.h>
 #include <cm3p/json/writer.h>
@@ -564,8 +565,22 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
 bool cmQtAutoGenInitializer::InitMoc()
 {
   // Mocs compilation file
-  this->Moc.CompilationFile =
-    cmStrCat(this->Dir.Build, "/mocs_compilation.cpp");
+  if (this->GlobalGen->IsXcode()) {
+    // XXX(xcode-per-cfg-src): Drop this Xcode-specific code path
+    // when the Xcode generator supports per-config sources.
+    this->Moc.CompilationFile.Default =
+      cmStrCat(this->Dir.Build, "/mocs_compilation.cpp");
+    this->Moc.CompilationFileGenex = this->Moc.CompilationFile.Default;
+  } else {
+    ConfigFileNames(this->Moc.CompilationFile,
+                    cmStrCat(this->Dir.Build, "/mocs_compilation"), ".cpp");
+    if (this->MultiConfig) {
+      this->Moc.CompilationFileGenex =
+        cmStrCat(this->Dir.Build, "/mocs_compilation_$<CONFIG>.cpp"_s);
+    } else {
+      this->Moc.CompilationFileGenex = this->Moc.CompilationFile.Default;
+    }
+  }
 
   // Moc predefs
   if (this->GenTarget->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
@@ -731,10 +746,14 @@ bool cmQtAutoGenInitializer::InitScanFiles()
   auto const& kw = this->GlobalInitializer->kw();
 
   auto makeMUFile = [this, &kw](cmSourceFile* sf, std::string const& fullPath,
+                                std::vector<size_t> const& configs,
                                 bool muIt) -> MUFileHandle {
     MUFileHandle muf = cm::make_unique<MUFile>();
     muf->FullPath = fullPath;
     muf->SF = sf;
+    if (!configs.empty() && configs.size() != this->ConfigsList.size()) {
+      muf->Configs = configs;
+    }
     muf->Generated = sf->GetIsGenerated();
     bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN);
     muf->SkipMoc = this->Moc.Enabled &&
@@ -773,42 +792,37 @@ bool cmQtAutoGenInitializer::InitScanFiles()
   // Scan through target files
   {
     // Scan through target files
-    std::vector<cmSourceFile*> srcFiles;
-    this->GenTarget->GetConfigCommonSourceFiles(srcFiles);
-    for (cmSourceFile* sf : srcFiles) {
-      // sf->GetExtension() is only valid after sf->ResolveFullPath() ...
-      // Since we're iterating over source files that might be not in the
-      // target we need to check for path errors (not existing files).
-      std::string pathError;
-      std::string const& fullPath = sf->ResolveFullPath(&pathError);
-      if (!pathError.empty() || fullPath.empty()) {
-        continue;
-      }
+    for (cmGeneratorTarget::AllConfigSource const& acs :
+         this->GenTarget->GetAllConfigSources()) {
+      std::string const& fullPath = acs.Source->GetFullPath();
       std::string const& extLower =
-        cmSystemTools::LowerCase(sf->GetExtension());
+        cmSystemTools::LowerCase(acs.Source->GetExtension());
 
       // Register files that will be scanned by moc or uic
       if (this->MocOrUicEnabled()) {
         if (cm->IsAHeaderExtension(extLower)) {
-          addMUHeader(makeMUFile(sf, fullPath, true), extLower);
+          addMUHeader(makeMUFile(acs.Source, fullPath, acs.Configs, true),
+                      extLower);
         } else if (cm->IsACLikeSourceExtension(extLower)) {
-          addMUSource(makeMUFile(sf, fullPath, true));
+          addMUSource(makeMUFile(acs.Source, fullPath, acs.Configs, true));
         }
       }
 
       // Register rcc enabled files
       if (this->Rcc.Enabled) {
-        if ((extLower == kw.qrc) && !sf->GetPropertyAsBool(kw.SKIP_AUTOGEN) &&
-            !sf->GetPropertyAsBool(kw.SKIP_AUTORCC)) {
+        if ((extLower == kw.qrc) &&
+            !acs.Source->GetPropertyAsBool(kw.SKIP_AUTOGEN) &&
+            !acs.Source->GetPropertyAsBool(kw.SKIP_AUTORCC)) {
           // Register qrc file
           Qrc qrc;
           qrc.QrcFile = fullPath;
           qrc.QrcName =
             cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
-          qrc.Generated = sf->GetIsGenerated();
+          qrc.Generated = acs.Source->GetIsGenerated();
           // RCC options
           {
-            std::string const& opts = sf->GetSafeProperty(kw.AUTORCC_OPTIONS);
+            std::string const& opts =
+              acs.Source->GetSafeProperty(kw.AUTORCC_OPTIONS);
             if (!opts.empty()) {
               cmExpandList(opts, qrc.Options);
             }
@@ -818,7 +832,7 @@ bool cmQtAutoGenInitializer::InitScanFiles()
       }
     }
   }
-  // cmGeneratorTarget::GetConfigCommonSourceFiles computes the target's
+  // cmGeneratorTarget::GetAllConfigSources computes the target's
   // sources meta data cache. Clear it so that OBJECT library targets that
   // are AUTOGEN initialized after this target get their added
   // mocs_compilation.cpp source acknowledged by this target.
@@ -862,7 +876,7 @@ bool cmQtAutoGenInitializer::InitScanFiles()
             }
 
             if (sf != nullptr) {
-              auto eMuf = makeMUFile(sf, fullPath, true);
+              auto eMuf = makeMUFile(sf, fullPath, muf.Configs, true);
               // Only process moc/uic when the parent is processed as well
               if (!muf.MocIt) {
                 eMuf->MocIt = false;
@@ -897,14 +911,14 @@ bool cmQtAutoGenInitializer::InitScanFiles()
 
       if (cm->IsAHeaderExtension(extLower)) {
         if (!cm::contains(this->AutogenTarget.Headers, sf.get())) {
-          auto muf = makeMUFile(sf.get(), fullPath, false);
+          auto muf = makeMUFile(sf.get(), fullPath, {}, false);
           if (muf->SkipMoc || muf->SkipUic) {
             addMUHeader(std::move(muf), extLower);
           }
         }
       } else if (cm->IsACLikeSourceExtension(extLower)) {
         if (!cm::contains(this->AutogenTarget.Sources, sf.get())) {
-          auto muf = makeMUFile(sf.get(), fullPath, false);
+          auto muf = makeMUFile(sf.get(), fullPath, {}, false);
           if (muf->SkipMoc || muf->SkipUic) {
             addMUSource(std::move(muf));
           }
@@ -1067,10 +1081,10 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
   this->Makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
 
   // Files provided by the autogen target
-  std::vector<std::string> autogenProvides;
+  std::vector<std::string> autogenByproducts;
   if (this->Moc.Enabled) {
     this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true);
-    autogenProvides.push_back(this->Moc.CompilationFile);
+    autogenByproducts.push_back(this->Moc.CompilationFileGenex);
   }
 
   // Compose target comment
@@ -1091,8 +1105,8 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
   }
 
   // Compose command lines
-  // TODO: Refactor autogen to output a per-config mocs_compilation.cpp instead
-  // of fiddling with the include directories
+  // FIXME: Take advantage of our per-config mocs_compilation_$<CONFIG>.cpp
+  // instead of fiddling with the include directories
   std::vector<std::string> configs;
   this->GlobalGen->GetQtAutoGenConfigs(configs);
   bool stdPipesUTF8 = true;
@@ -1138,7 +1152,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
     // PRE_BUILD does not support file dependencies!
     const std::vector<std::string> no_output;
     const std::vector<std::string> no_deps;
-    cmCustomCommand cc(no_output, autogenProvides, no_deps, commandLines,
+    cmCustomCommand cc(no_output, autogenByproducts, no_deps, commandLines,
                        this->Makefile->GetBacktrace(), autogenComment.c_str(),
                        this->Dir.Work.c_str(), stdPipesUTF8);
     cc.SetEscapeOldStyle(false);
@@ -1283,7 +1297,7 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
     // Create autogen target
     cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand(
       this->AutogenTarget.Name, true, this->Dir.Work.c_str(),
-      /*byproducts=*/autogenProvides,
+      /*byproducts=*/autogenByproducts,
       /*depends=*/dependencies, commandLines, false, autogenComment.c_str());
     // Create autogen generator target
     this->LocalGen->AddGeneratorTarget(
@@ -1533,18 +1547,31 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
   info.SetArray("CMAKE_LIST_FILES", this->Makefile->GetListFiles());
   info.SetArray("HEADER_EXTENSIONS",
                 this->Makefile->GetCMakeInstance()->GetHeaderExtensions());
+  auto cfgArray = [this](std::vector<size_t> const& configs) -> Json::Value {
+    Json::Value value;
+    if (!configs.empty()) {
+      value = Json::arrayValue;
+      for (size_t ci : configs) {
+        value.append(this->ConfigsList[ci]);
+      }
+    }
+    return value;
+  };
+  info.SetArrayArray("HEADERS", headers,
+                     [this, &cfgArray](Json::Value& jval, MUFile const* muf) {
+                       jval.resize(4u);
+                       jval[0u] = muf->FullPath;
+                       jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm',
+                                           muf->UicIt ? 'U' : 'u');
+                       jval[2u] = cfgArray(muf->Configs);
+                       jval[3u] = this->GetMocBuildPath(*muf);
+                     });
   info.SetArrayArray(
-    "HEADERS", headers, [this](Json::Value& jval, MUFile const* muf) {
+    "SOURCES", sources, [&cfgArray](Json::Value& jval, MUFile const* muf) {
       jval.resize(3u);
       jval[0u] = muf->FullPath;
       jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u');
-      jval[2u] = this->GetMocBuildPath(*muf);
-    });
-  info.SetArrayArray(
-    "SOURCES", sources, [](Json::Value& jval, MUFile const* muf) {
-      jval.resize(2u);
-      jval[0u] = muf->FullPath;
-      jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u');
+      jval[2u] = cfgArray(muf->Configs);
     });
 
   // Write moc settings
@@ -1563,7 +1590,7 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
         jval[0u] = pair.first;
         jval[1u] = pair.second;
       });
-    info.Set("MOC_COMPILATION_FILE", this->Moc.CompilationFile);
+    info.SetConfig("MOC_COMPILATION_FILE", this->Moc.CompilationFile);
     info.SetArray("MOC_PREDEFS_CMD", this->Moc.PredefsCmd);
     info.SetConfig("MOC_PREDEFS_FILE", this->Moc.PredefsFile);
   }
@@ -1656,6 +1683,28 @@ cmSourceFile* cmQtAutoGenInitializer::AddGeneratedSource(
   return gFile;
 }
 
+void cmQtAutoGenInitializer::AddGeneratedSource(ConfigString const& filename,
+                                                GenVarsT const& genVars,
+                                                bool prepend)
+{
+  // XXX(xcode-per-cfg-src): Drop the Xcode-specific part of the condition
+  // when the Xcode generator supports per-config sources.
+  if (!this->MultiConfig || this->GlobalGen->IsXcode()) {
+    this->AddGeneratedSource(filename.Default, genVars, prepend);
+    return;
+  }
+  for (auto const& cfg : this->ConfigsList) {
+    std::string const& filenameCfg = filename.Config.at(cfg);
+    // Register source at makefile
+    this->RegisterGeneratedSource(filenameCfg);
+    // Add source file to target for this configuration.
+    this->GenTarget->AddSource(
+      cmStrCat("$<$<CONFIG:"_s, cfg, ">:"_s, filenameCfg, ">"_s), prepend);
+    // Add source file to source group
+    this->AddToSourceGroup(filenameCfg, genVars.GenNameUpper);
+  }
+}
+
 void cmQtAutoGenInitializer::AddToSourceGroup(std::string const& fileName,
                                               cm::string_view genNameUpper)
 {

+ 6 - 1
Source/cmQtAutoGenInitializer.h

@@ -4,6 +4,7 @@
 
 #include "cmConfigure.h" // IWYU pragma: keep
 
+#include <cstddef>
 #include <memory>
 #include <set>
 #include <string>
@@ -70,6 +71,7 @@ public:
   {
     std::string FullPath;
     cmSourceFile* SF = nullptr;
+    std::vector<size_t> Configs;
     bool Generated = false;
     bool SkipMoc = false;
     bool SkipUic = false;
@@ -132,6 +134,8 @@ private:
   cmSourceFile* AddGeneratedSource(std::string const& filename,
                                    GenVarsT const& genVars,
                                    bool prepend = false);
+  void AddGeneratedSource(ConfigString const& filename,
+                          GenVarsT const& genVars, bool prepend = false);
   void AddToSourceGroup(std::string const& fileName,
                         cm::string_view genNameUpper);
   void AddCleanFile(std::string const& fileName);
@@ -207,7 +211,8 @@ private:
 
     bool RelaxedMode = false;
     bool PathPrefix = false;
-    std::string CompilationFile;
+    ConfigString CompilationFile;
+    std::string CompilationFileGenex;
     // Compiler implicit pre defines
     std::vector<std::string> PredefsCmd;
     ConfigString PredefsFile;

+ 42 - 4
Source/cmQtAutoMocUic.cxx

@@ -2452,17 +2452,20 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
 
       Json::Value const& entry = val[ii];
       if (testEntry(entry.isArray(), "JSON value is not an array.") ||
-          testEntry(entry.size() == 3, "JSON array size invalid.")) {
+          testEntry(entry.size() == 4, "JSON array size invalid.")) {
         return false;
       }
 
       Json::Value const& entryName = entry[0u];
       Json::Value const& entryFlags = entry[1u];
-      Json::Value const& entryBuild = entry[2u];
+      Json::Value const& entryConfigs = entry[2u];
+      Json::Value const& entryBuild = entry[3u];
       if (testEntry(entryName.isString(),
                     "JSON value for name is not a string.") ||
           testEntry(entryFlags.isString(),
                     "JSON value for flags is not a string.") ||
+          testEntry(entryConfigs.isNull() || entryConfigs.isArray(),
+                    "JSON value for configs is not null or array.") ||
           testEntry(entryBuild.isString(),
                     "JSON value for build path is not a string.")) {
         return false;
@@ -2475,6 +2478,22 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
         return false;
       }
 
+      if (entryConfigs.isArray()) {
+        bool configFound = false;
+        Json::ArrayIndex const configArraySize = entryConfigs.size();
+        for (Json::ArrayIndex ci = 0; ci != configArraySize; ++ci) {
+          Json::Value const& config = entryConfigs[ci];
+          if (testEntry(config.isString(),
+                        "JSON value in config array is not a string.")) {
+            return false;
+          }
+          configFound = configFound || config.asString() == this->InfoConfig();
+        }
+        if (!configFound) {
+          continue;
+        }
+      }
+
       cmFileTime fileTime;
       if (!fileTime.Load(name)) {
         return info.LogError(cmStrCat(
@@ -2515,16 +2534,19 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
 
       Json::Value const& entry = val[ii];
       if (testEntry(entry.isArray(), "JSON value is not an array.") ||
-          testEntry(entry.size() == 2, "JSON array size invalid.")) {
+          testEntry(entry.size() == 3, "JSON array size invalid.")) {
         return false;
       }
 
       Json::Value const& entryName = entry[0u];
       Json::Value const& entryFlags = entry[1u];
+      Json::Value const& entryConfigs = entry[2u];
       if (testEntry(entryName.isString(),
                     "JSON value for name is not a string.") ||
           testEntry(entryFlags.isString(),
-                    "JSON value for flags is not a string.")) {
+                    "JSON value for flags is not a string.") ||
+          testEntry(entryConfigs.isNull() || entryConfigs.isArray(),
+                    "JSON value for configs is not null or array.")) {
         return false;
       }
 
@@ -2534,6 +2556,22 @@ bool cmQtAutoMocUicT::InitFromInfo(InfoT const& info)
         return false;
       }
 
+      if (entryConfigs.isArray()) {
+        bool configFound = false;
+        Json::ArrayIndex const configArraySize = entryConfigs.size();
+        for (Json::ArrayIndex ci = 0; ci != configArraySize; ++ci) {
+          Json::Value const& config = entryConfigs[ci];
+          if (testEntry(config.isString(),
+                        "JSON value in config array is not a string.")) {
+            return false;
+          }
+          configFound = configFound || config.asString() == this->InfoConfig();
+        }
+        if (!configFound) {
+          continue;
+        }
+      }
+
       cmFileTime fileTime;
       if (!fileTime.Load(name)) {
         return info.LogError(cmStrCat(

+ 11 - 2
Tests/QtAutogen/AutogenOriginDependsOff/CMakeLists.txt

@@ -2,6 +2,15 @@ cmake_minimum_required(VERSION 3.11)
 project(AutogenOriginDependsOff)
 include("../AutogenCoreTest.cmake")
 
+get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+# XXX(xcode-per-cfg-src): Enable multi-config code path for Xcode
+# when the Xcode generator supports per-config sources.
+if(_isMultiConfig AND NOT CMAKE_GENERATOR STREQUAL "Xcode")
+  set(mocs_compilation_cpp "mocs_compilation_$<CONFIG>.cpp")
+else()
+  set(mocs_compilation_cpp "mocs_compilation.cpp")
+endif()
+
 set(CSD ${CMAKE_CURRENT_SOURCE_DIR})
 set(CBD ${CMAKE_CURRENT_BINARY_DIR})
 include_directories(${CSD})
@@ -19,7 +28,7 @@ add_custom_command (
 add_custom_target ( a_mc
     COMMAND ${CMAKE_COMMAND} -E sleep 2
     COMMAND ${CMAKE_COMMAND}
-        "-DMCF=${CBD}/a_qt_autogen/mocs_compilation.cpp"
+        "-DMCF=${CBD}/a_qt_autogen/${mocs_compilation_cpp}"
         "-DCF_IN=${CSD}/a_mc.hpp.in"
         "-DCF_OUT=${CBD}/a_mc.hpp"
         -P ${CSD}/configure_content.cmake
@@ -51,7 +60,7 @@ add_custom_command (
     DEPENDS b_qt_autogen
     COMMAND ${CMAKE_COMMAND} -E sleep 2
     COMMAND ${CMAKE_COMMAND}
-        "-DMCF=${CBD}/b_qt_autogen/mocs_compilation.cpp"
+        "-DMCF=${CBD}/b_qt_autogen/${mocs_compilation_cpp}"
         "-DCF_IN=${CSD}/b_mc.cpp.in"
         "-DCF_OUT=${CBD}/b_mc.cpp"
         -P ${CSD}/configure_content.cmake

+ 4 - 4
Tests/QtAutogen/GlobalAutogenTarget/CMakeLists.txt

@@ -13,10 +13,10 @@ include("../AutogenCoreTest.cmake")
 set(GAT_SDIR "${CMAKE_CURRENT_SOURCE_DIR}/GAT")
 set(GAT_BDIR "${CMAKE_CURRENT_BINARY_DIR}/GAT")
 # Files
-set(MCA "sda/sda_autogen/mocs_compilation.cpp")
-set(MCB "sdb/sdb_autogen/mocs_compilation.cpp")
-set(MCC "sdc/sdc_autogen/mocs_compilation.cpp")
-set(MCG "gat_autogen/mocs_compilation.cpp")
+set(MCA "sda/sda_autogen/mocs_compilation*.cpp")
+set(MCB "sdb/sdb_autogen/mocs_compilation*.cpp")
+set(MCC "sdc/sdc_autogen/mocs_compilation*.cpp")
+set(MCG "gat_autogen/mocs_compilation*.cpp")
 
 set(DRA "sda/sda_autogen/*qrc_data.cpp")
 set(DRB "sdb/sdb_autogen/*qrc_data.cpp")

+ 12 - 0
Tests/QtAutogen/MocOnly/CMakeLists.txt

@@ -11,6 +11,18 @@ add_executable(mocOnly
   IncA.cpp
   IncB.cpp
 )
+# XXX(xcode-per-cfg-src): Drop the NO_PER_CONFIG_SOURCES exclusion
+# when the Xcode generator supports per-config sources.
+if(NOT NO_PER_CONFIG_SOURCES)
+  target_sources(mocOnly PRIVATE
+    "$<$<CONFIG:Debug>:${CMAKE_CURRENT_SOURCE_DIR}/CfgDebug.cpp>"
+    "$<$<NOT:$<CONFIG:Debug>>:${CMAKE_CURRENT_SOURCE_DIR}/CfgOther.cpp>"
+    )
+  target_compile_definitions(mocOnly PRIVATE
+    "$<$<CONFIG:Debug>:HAVE_CFG_DEBUG>"
+    "$<$<NOT:$<CONFIG:Debug>>:HAVE_CFG_OTHER>"
+    )
+endif()
 set_property(TARGET mocOnly PROPERTY AUTOMOC ON)
 target_link_libraries(mocOnly ${QT_LIBRARIES})
 # Add compile definitions with unusual characters

+ 5 - 0
Tests/QtAutogen/MocOnly/CfgDebug.cpp

@@ -0,0 +1,5 @@
+#include "CfgDebug.hpp"
+
+CfgDebug::CfgDebug()
+{
+}

+ 15 - 0
Tests/QtAutogen/MocOnly/CfgDebug.hpp

@@ -0,0 +1,15 @@
+#ifndef CFGDEBUG_HPP
+#define CFGDEBUG_HPP
+
+#include <QObject>
+
+/* clang-format off */
+class CfgDebug : public QObject
+{
+  Q_OBJECT
+public:
+  CfgDebug();
+};
+/* clang-format on */
+
+#endif

+ 5 - 0
Tests/QtAutogen/MocOnly/CfgOther.cpp

@@ -0,0 +1,5 @@
+#include "CfgOther.hpp"
+
+CfgOther::CfgOther()
+{
+}

+ 15 - 0
Tests/QtAutogen/MocOnly/CfgOther.hpp

@@ -0,0 +1,15 @@
+#ifndef CFGOTHER_HPP
+#define CFGOTHER_HPP
+
+#include <QObject>
+
+/* clang-format off */
+class CfgOther : public QObject
+{
+  Q_OBJECT
+public:
+  CfgOther();
+};
+/* clang-format on */
+
+#endif

+ 8 - 0
Tests/QtAutogen/MocOnly/main.cpp

@@ -5,6 +5,14 @@
 #include "StyleA.hpp"
 #include "StyleB.hpp"
 
+#ifdef HAVE_CFG_DEBUG
+#  include "CfgDebug.hpp"
+#endif
+
+#ifdef HAVE_CFG_OTHER
+#  include "CfgOther.hpp"
+#endif
+
 int main(int argv, char** args)
 {
   StyleA styleA;

+ 9 - 1
Tests/QtAutogen/TestMacros.cmake

@@ -1,12 +1,19 @@
 # Autogen build options
 set(Autogen_BUILD_OPTIONS "-DQT_TEST_VERSION=${QT_TEST_VERSION}")
-if(NOT _isMultiConfig)   # Set in Tests/CMakeLists.txt
+if(_isMultiConfig)   # Set in Tests/CMakeLists.txt
+  list(APPEND Autogen_CTEST_OPTIONS --build-config $<CONFIGURATION>)
+else()
   list(APPEND Autogen_BUILD_OPTIONS "-DCMAKE_BUILD_TYPE=$<CONFIGURATION>")
 endif()
 list(APPEND Autogen_BUILD_OPTIONS
     "-DCMAKE_AUTOGEN_VERBOSE=1"
     "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}"
 )
+# XXX(xcode-per-cfg-src): Drop the NO_PER_CONFIG_SOURCES exclusion
+# when the Xcode generator supports per-config sources.
+if(CMAKE_GENERATOR STREQUAL "Xcode")
+  list(APPEND Autogen_BUILD_OPTIONS -DNO_PER_CONFIG_SOURCES=1)
+endif()
 
 # A macro to add a QtAutogen test
 macro(ADD_AUTOGEN_TEST NAME)
@@ -30,6 +37,7 @@ macro(ADD_AUTOGEN_TEST NAME)
     "${_BuildDir}"
     ${build_generator_args}
     --build-project ${NAME}
+    ${Autogen_CTEST_OPTIONS}
     --build-exe-dir "${_BuildDir}"
     --force-new-ctest-process
     --build-options ${build_options} ${Autogen_BUILD_OPTIONS}

+ 2 - 1
Tests/RunCMake/NinjaMultiConfig/Qt5.cmake

@@ -16,12 +16,13 @@ if(Qt5Core_VERSION VERSION_GREATER_EQUAL "5.15.0")
   set(moc_writes_depfiles 1)
 endif()
 
-set(autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation.cpp")
+set(autogen_files)
 if(moc_writes_depfiles)
   list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/deps")
   list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/timestamp")
 endif()
 foreach(c IN LISTS CMAKE_CONFIGURATION_TYPES)
+  list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/mocs_compilation_${c}.cpp")
   list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp")
   if(moc_writes_depfiles)
     list(APPEND autogen_files "${CMAKE_BINARY_DIR}/exe_autogen/include_${c}/moc_qt5.cpp.d")