Explorar o código

Unity Build: Fix per-config sources in multi-config generators

Single-config generators already support unity builds with per-config
sources because they compute sources using `CMAKE_BUILD_TYPE` as the
configuration.  Each original source is either included in the unity
build source, or not.

Teach multi-config generators to compute the list of sources for
inclusion in unity builds using all configurations.  Previously they
only used the empty string as the configuration.  Each original source
may be included in some configurations, but not others.  Use
preprocessor conditions to guard their inclusion when necessary.

Fixes: #22892
Brad King %!s(int64=4) %!d(string=hai) anos
pai
achega
129e3c6540

+ 58 - 29
Source/cmLocalGenerator.cxx

@@ -2808,7 +2808,7 @@ inline void RegisterUnitySources(cmGeneratorTarget* target, cmSourceFile* sf,
 }
 }
 
 
 cmLocalGenerator::UnitySource cmLocalGenerator::WriteUnitySource(
 cmLocalGenerator::UnitySource cmLocalGenerator::WriteUnitySource(
-  cmGeneratorTarget* target,
+  cmGeneratorTarget* target, std::vector<std::string> const& configs,
   cmRange<std::vector<UnityBatchedSource>::const_iterator> sources,
   cmRange<std::vector<UnityBatchedSource>::const_iterator> sources,
   cmValue beforeInclude, cmValue afterInclude, std::string filename) const
   cmValue beforeInclude, cmValue afterInclude, std::string filename) const
 {
 {
@@ -2818,21 +2818,36 @@ cmLocalGenerator::UnitySource cmLocalGenerator::WriteUnitySource(
   file.SetCopyIfDifferent(true);
   file.SetCopyIfDifferent(true);
   file << "/* generated by CMake */\n\n";
   file << "/* generated by CMake */\n\n";
 
 
+  bool perConfig = false;
   for (UnityBatchedSource const& ubs : sources) {
   for (UnityBatchedSource const& ubs : sources) {
+    cm::optional<std::string> cond;
+    if (ubs.Configs.size() != configs.size()) {
+      perConfig = true;
+      cond = std::string();
+      cm::string_view sep;
+      for (size_t ci : ubs.Configs) {
+        cond = cmStrCat(*cond, sep, "defined(CMAKE_UNITY_CONFIG_",
+                        cmSystemTools::UpperCase(configs[ci]), ")");
+        sep = " || "_s;
+      }
+    }
     RegisterUnitySources(target, ubs.Source, filename);
     RegisterUnitySources(target, ubs.Source, filename);
-    WriteUnitySourceInclude(file, ubs.Source->ResolveFullPath(), beforeInclude,
-                            afterInclude, uniqueIdName);
+    WriteUnitySourceInclude(file, cond, ubs.Source->ResolveFullPath(),
+                            beforeInclude, afterInclude, uniqueIdName);
   }
   }
 
 
-  return UnitySource(std::move(filename));
+  return UnitySource(std::move(filename), perConfig);
 }
 }
 
 
-void cmLocalGenerator::WriteUnitySourceInclude(std::ostream& unity_file,
-                                               std::string const& sf_full_path,
-                                               cmValue beforeInclude,
-                                               cmValue afterInclude,
-                                               cmValue uniqueIdName) const
+void cmLocalGenerator::WriteUnitySourceInclude(
+  std::ostream& unity_file, cm::optional<std::string> const& cond,
+  std::string const& sf_full_path, cmValue beforeInclude, cmValue afterInclude,
+  cmValue uniqueIdName) const
 {
 {
+  if (cond) {
+    unity_file << "#if " << *cond << "\n";
+  }
+
   if (cmNonempty(uniqueIdName)) {
   if (cmNonempty(uniqueIdName)) {
     std::string pathToHash;
     std::string pathToHash;
     auto PathEqOrSubDir = [](std::string const& a, std::string const& b) {
     auto PathEqOrSubDir = [](std::string const& a, std::string const& b) {
@@ -2867,12 +2882,16 @@ void cmLocalGenerator::WriteUnitySourceInclude(std::ostream& unity_file,
   if (afterInclude) {
   if (afterInclude) {
     unity_file << *afterInclude << "\n";
     unity_file << *afterInclude << "\n";
   }
   }
+  if (cond) {
+    unity_file << "#endif\n";
+  }
   unity_file << "\n";
   unity_file << "\n";
 }
 }
 
 
 std::vector<cmLocalGenerator::UnitySource>
 std::vector<cmLocalGenerator::UnitySource>
 cmLocalGenerator::AddUnityFilesModeAuto(
 cmLocalGenerator::AddUnityFilesModeAuto(
   cmGeneratorTarget* target, std::string const& lang,
   cmGeneratorTarget* target, std::string const& lang,
+  std::vector<std::string> const& configs,
   std::vector<UnityBatchedSource> const& filtered_sources,
   std::vector<UnityBatchedSource> const& filtered_sources,
   cmValue beforeInclude, cmValue afterInclude,
   cmValue beforeInclude, cmValue afterInclude,
   std::string const& filename_base, size_t batchSize)
   std::string const& filename_base, size_t batchSize)
@@ -2891,9 +2910,9 @@ cmLocalGenerator::AddUnityFilesModeAuto(
                                     (lang == "C") ? "_c.c" : "_cxx.cxx");
                                     (lang == "C") ? "_c.c" : "_cxx.cxx");
     auto const begin = filtered_sources.begin() + batch * batchSize;
     auto const begin = filtered_sources.begin() + batch * batchSize;
     auto const end = begin + chunk;
     auto const end = begin + chunk;
-    unity_files.emplace_back(
-      this->WriteUnitySource(target, cmMakeRange(begin, end), beforeInclude,
-                             afterInclude, std::move(filename)));
+    unity_files.emplace_back(this->WriteUnitySource(
+      target, configs, cmMakeRange(begin, end), beforeInclude, afterInclude,
+      std::move(filename)));
   }
   }
   return unity_files;
   return unity_files;
 }
 }
@@ -2901,6 +2920,7 @@ cmLocalGenerator::AddUnityFilesModeAuto(
 std::vector<cmLocalGenerator::UnitySource>
 std::vector<cmLocalGenerator::UnitySource>
 cmLocalGenerator::AddUnityFilesModeGroup(
 cmLocalGenerator::AddUnityFilesModeGroup(
   cmGeneratorTarget* target, std::string const& lang,
   cmGeneratorTarget* target, std::string const& lang,
+  std::vector<std::string> const& configs,
   std::vector<UnityBatchedSource> const& filtered_sources,
   std::vector<UnityBatchedSource> const& filtered_sources,
   cmValue beforeInclude, cmValue afterInclude,
   cmValue beforeInclude, cmValue afterInclude,
   std::string const& filename_base)
   std::string const& filename_base)
@@ -2927,9 +2947,9 @@ cmLocalGenerator::AddUnityFilesModeGroup(
     auto const& name = item.first;
     auto const& name = item.first;
     std::string filename = cmStrCat(filename_base, "unity_", name,
     std::string filename = cmStrCat(filename_base, "unity_", name,
                                     (lang == "C") ? "_c.c" : "_cxx.cxx");
                                     (lang == "C") ? "_c.c" : "_cxx.cxx");
-    unity_files.emplace_back(
-      this->WriteUnitySource(target, cmMakeRange(item.second), beforeInclude,
-                             afterInclude, std::move(filename)));
+    unity_files.emplace_back(this->WriteUnitySource(
+      target, configs, cmMakeRange(item.second), beforeInclude, afterInclude,
+      std::move(filename)));
   }
   }
 
 
   return unity_files;
   return unity_files;
@@ -2943,19 +2963,24 @@ void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target)
 
 
   std::vector<UnityBatchedSource> unitySources;
   std::vector<UnityBatchedSource> unitySources;
 
 
-  {
-    // FIXME: Handle all configurations in multi-config generators.
-    std::string config;
-    if (!this->GetGlobalGenerator()->IsMultiConfig()) {
-      config = this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
-    }
+  std::vector<std::string> configs =
+    this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
+
+  std::map<cmSourceFile const*, size_t> index;
 
 
+  for (size_t ci = 0; ci < configs.size(); ++ci) {
     // FIXME: Refactor collection of sources to not evaluate object libraries.
     // FIXME: Refactor collection of sources to not evaluate object libraries.
     std::vector<cmSourceFile*> sources;
     std::vector<cmSourceFile*> sources;
-    target->GetSourceFiles(sources, config);
-
+    target->GetSourceFiles(sources, configs[ci]);
     for (cmSourceFile* sf : sources) {
     for (cmSourceFile* sf : sources) {
-      unitySources.emplace_back(sf);
+      auto mi = index.find(sf);
+      if (mi == index.end()) {
+        unitySources.emplace_back(sf);
+        std::map<cmSourceFile const*, size_t>::value_type entry(
+          sf, unitySources.size() - 1);
+        mi = index.insert(entry).first;
+      }
+      unitySources[mi->second].Configs.emplace_back(ci);
     }
     }
   }
   }
 
 
@@ -2990,13 +3015,13 @@ void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target)
 
 
     std::vector<UnitySource> unity_files;
     std::vector<UnitySource> unity_files;
     if (!unityMode || *unityMode == "BATCH") {
     if (!unityMode || *unityMode == "BATCH") {
-      unity_files =
-        AddUnityFilesModeAuto(target, lang, filtered_sources, beforeInclude,
-                              afterInclude, filename_base, unityBatchSize);
+      unity_files = AddUnityFilesModeAuto(
+        target, lang, configs, filtered_sources, beforeInclude, afterInclude,
+        filename_base, unityBatchSize);
     } else if (unityMode && *unityMode == "GROUP") {
     } else if (unityMode && *unityMode == "GROUP") {
       unity_files =
       unity_files =
-        AddUnityFilesModeGroup(target, lang, filtered_sources, beforeInclude,
-                               afterInclude, filename_base);
+        AddUnityFilesModeGroup(target, lang, configs, filtered_sources,
+                               beforeInclude, afterInclude, filename_base);
     } else {
     } else {
       // unity mode is set to an unsupported value
       // unity mode is set to an unsupported value
       std::string e("Invalid UNITY_BUILD_MODE value of " + *unityMode +
       std::string e("Invalid UNITY_BUILD_MODE value of " + *unityMode +
@@ -3010,6 +3035,10 @@ void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target)
       target->AddSource(file.Path, true);
       target->AddSource(file.Path, true);
       unity->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "ON");
       unity->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "ON");
       unity->SetProperty("UNITY_SOURCE_FILE", file.Path);
       unity->SetProperty("UNITY_SOURCE_FILE", file.Path);
+      if (file.PerConfig) {
+        unity->SetProperty("COMPILE_DEFINITIONS",
+                           "CMAKE_UNITY_CONFIG_$<UPPER_CASE:$<CONFIG>>");
+      }
     }
     }
   }
   }
 }
 }

+ 10 - 2
Source/cmLocalGenerator.h

@@ -14,6 +14,8 @@
 #include <utility>
 #include <utility>
 #include <vector>
 #include <vector>
 
 
+#include <cm/optional>
+
 #include <cm3p/kwiml/int.h>
 #include <cm3p/kwiml/int.h>
 
 
 #include "cmCustomCommandTypes.h"
 #include "cmCustomCommandTypes.h"
@@ -664,6 +666,7 @@ private:
   struct UnityBatchedSource
   struct UnityBatchedSource
   {
   {
     cmSourceFile* Source = nullptr;
     cmSourceFile* Source = nullptr;
+    std::vector<size_t> Configs;
     UnityBatchedSource(cmSourceFile* sf)
     UnityBatchedSource(cmSourceFile* sf)
       : Source(sf)
       : Source(sf)
     {
     {
@@ -672,27 +675,32 @@ private:
   struct UnitySource
   struct UnitySource
   {
   {
     std::string Path;
     std::string Path;
-    UnitySource(std::string path)
+    bool PerConfig = false;
+    UnitySource(std::string path, bool perConfig)
       : Path(std::move(path))
       : Path(std::move(path))
+      , PerConfig(perConfig)
     {
     {
     }
     }
   };
   };
 
 
   UnitySource WriteUnitySource(
   UnitySource WriteUnitySource(
-    cmGeneratorTarget* target,
+    cmGeneratorTarget* target, std::vector<std::string> const& configs,
     cmRange<std::vector<UnityBatchedSource>::const_iterator> sources,
     cmRange<std::vector<UnityBatchedSource>::const_iterator> sources,
     cmValue beforeInclude, cmValue afterInclude, std::string filename) const;
     cmValue beforeInclude, cmValue afterInclude, std::string filename) const;
   void WriteUnitySourceInclude(std::ostream& unity_file,
   void WriteUnitySourceInclude(std::ostream& unity_file,
+                               cm::optional<std::string> const& cond,
                                std::string const& sf_full_path,
                                std::string const& sf_full_path,
                                cmValue beforeInclude, cmValue afterInclude,
                                cmValue beforeInclude, cmValue afterInclude,
                                cmValue uniqueIdName) const;
                                cmValue uniqueIdName) const;
   std::vector<UnitySource> AddUnityFilesModeAuto(
   std::vector<UnitySource> AddUnityFilesModeAuto(
     cmGeneratorTarget* target, std::string const& lang,
     cmGeneratorTarget* target, std::string const& lang,
+    std::vector<std::string> const& configs,
     std::vector<UnityBatchedSource> const& filtered_sources,
     std::vector<UnityBatchedSource> const& filtered_sources,
     cmValue beforeInclude, cmValue afterInclude,
     cmValue beforeInclude, cmValue afterInclude,
     std::string const& filename_base, size_t batchSize);
     std::string const& filename_base, size_t batchSize);
   std::vector<UnitySource> AddUnityFilesModeGroup(
   std::vector<UnitySource> AddUnityFilesModeGroup(
     cmGeneratorTarget* target, std::string const& lang,
     cmGeneratorTarget* target, std::string const& lang,
+    std::vector<std::string> const& configs,
     std::vector<UnityBatchedSource> const& filtered_sources,
     std::vector<UnityBatchedSource> const& filtered_sources,
     cmValue beforeInclude, cmValue afterInclude,
     cmValue beforeInclude, cmValue afterInclude,
     std::string const& filename_base);
     std::string const& filename_base);

+ 16 - 0
Tests/RunCMake/UnityBuild/RunCMakeTest.cmake

@@ -26,6 +26,22 @@ run_build(unitybuild_anon_ns)
 run_build(unitybuild_anon_ns_no_unity_build)
 run_build(unitybuild_anon_ns_no_unity_build)
 run_build(unitybuild_anon_ns_group_mode)
 run_build(unitybuild_anon_ns_group_mode)
 
 
+function(run_per_config name)
+  set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
+  run_cmake(${name})
+  set(RunCMake_TEST_NO_CLEAN 1)
+  if(RunCMake_GENERATOR_IS_MULTI_CONFIG)
+    run_cmake_command(${name}-build-debug ${CMAKE_COMMAND} --build . --config Debug)
+    run_cmake_command(${name}-build-release ${CMAKE_COMMAND} --build . --config Release)
+  else()
+    run_cmake_command(${name}-build ${CMAKE_COMMAND} --build .)
+  endif()
+endfunction()
+
+if(NOT RunCMake_GENERATOR STREQUAL "Xcode")
+  run_per_config(per_config_c)
+endif()
+
 function(run_test name)
 function(run_test name)
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
   set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${name}-build)
   run_cmake(${name})
   run_cmake(${name})

+ 16 - 0
Tests/RunCMake/UnityBuild/per_config_c.c

@@ -0,0 +1,16 @@
+#ifdef CFG_DEBUG
+extern void per_config_c_debug(void);
+#endif
+#ifdef CFG_OTHER
+extern void per_config_c_other(void);
+#endif
+int main(void)
+{
+#ifdef CFG_DEBUG
+  per_config_c_debug();
+#endif
+#ifdef CFG_OTHER
+  per_config_c_other();
+#endif
+  return 0;
+}

+ 12 - 0
Tests/RunCMake/UnityBuild/per_config_c.cmake

@@ -0,0 +1,12 @@
+enable_language(C)
+
+add_executable(per_config_c per_config_c.c
+  "$<$<CONFIG:Debug>:per_config_c_debug.c>"
+  "$<$<NOT:$<CONFIG:Debug>>:per_config_c_other.c>"
+  )
+
+set_target_properties(per_config_c PROPERTIES UNITY_BUILD ON)
+target_compile_definitions(per_config_c PRIVATE
+  "$<$<CONFIG:Debug>:CFG_DEBUG>"
+  "$<$<NOT:$<CONFIG:Debug>>:CFG_OTHER>"
+  )

+ 9 - 0
Tests/RunCMake/UnityBuild/per_config_c_debug.c

@@ -0,0 +1,9 @@
+#ifndef CFG_DEBUG
+#  error "per_config_c_debug built without CFG_DEBUG"
+#endif
+#ifdef CFG_OTHER
+#  error "per_config_c_debug built with CFG_OTHER"
+#endif
+void per_config_c_debug(void)
+{
+}

+ 9 - 0
Tests/RunCMake/UnityBuild/per_config_c_other.c

@@ -0,0 +1,9 @@
+#ifdef CFG_DEBUG
+#  error "per_config_c_other built with CFG_DEBUG"
+#endif
+#ifndef CFG_OTHER
+#  error "per_config_c_other built without CFG_OTHER"
+#endif
+void per_config_c_other(void)
+{
+}