Bläddra i källkod

Unity build: Add support for Ninja and Makefile generators

Cristian Adam 6 år sedan
förälder
incheckning
7114c141e2

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

@@ -323,6 +323,10 @@ Properties on Targets
    /prop_tgt/Swift_MODULE_DIRECTORY
    /prop_tgt/Swift_MODULE_NAME
    /prop_tgt/TYPE
+   /prop_tgt/UNITY_BUILD
+   /prop_tgt/UNITY_BUILD_BATCH_SIZE
+   /prop_tgt/UNITY_BUILD_CODE_AFTER_INCLUDE
+   /prop_tgt/UNITY_BUILD_CODE_BEFORE_INCLUDE
    /prop_tgt/VERSION
    /prop_tgt/VISIBILITY_INLINES_HIDDEN
    /prop_tgt/VS_CONFIGURATION_TYPE
@@ -450,6 +454,7 @@ Properties on Source Files
    /prop_sf/SKIP_AUTORCC
    /prop_sf/SKIP_AUTOUIC
    /prop_sf/SKIP_PRECOMPILE_HEADERS
+   /prop_sf/SKIP_UNITY_BUILD_INCLUSION
    /prop_sf/Swift_DEPENDENCIES_FILE
    /prop_sf/Swift_DIAGNOSTICS_FILE
    /prop_sf/SYMBOLIC

+ 2 - 0
Help/manual/cmake-variables.7.rst

@@ -431,6 +431,8 @@ Variables that Control the Build
    /variable/CMAKE_TRY_COMPILE_CONFIGURATION
    /variable/CMAKE_TRY_COMPILE_PLATFORM_VARIABLES
    /variable/CMAKE_TRY_COMPILE_TARGET_TYPE
+   /variable/CMAKE_UNITY_BUILD
+   /variable/CMAKE_UNITY_BUILD_BATCH_SIZE
    /variable/CMAKE_USE_RELATIVE_PATHS
    /variable/CMAKE_VISIBILITY_INLINES_HIDDEN
    /variable/CMAKE_VS_GLOBALS

+ 7 - 0
Help/prop_sf/SKIP_UNITY_BUILD_INCLUSION.rst

@@ -0,0 +1,7 @@
+SKIP_UNITY_BUILD_INCLUSION
+--------------------------
+
+Is this source file skipped by :prop_tgt:`UNITY_BUILD` feature.
+
+This property helps with "ODR (One definition rule)" problems
+that one would run into when using an :prop_tgt:`UNITY_BUILD`.

+ 55 - 0
Help/prop_tgt/UNITY_BUILD.rst

@@ -0,0 +1,55 @@
+UNITY_BUILD
+-----------
+
+Should the target source files be processed into batches for
+faster compilation. This feature is known as "Unity build",
+or "Jumbo build".
+
+The `C` and `CXX` source files are grouped separately.
+
+This property is initialized by the value of the
+:variable:`CMAKE_UNITY_BUILD` variable if it is set when
+a target is created.
+
+.. note ::
+
+   It's not recommended to directly set :prop_tgt:`UNITY_BUILD`
+   to `ON`, but to instead set :variable:`CMAKE_UNITY_BUILD` from
+   the command line. However, it IS recommended to set
+   :prop_tgt:`UNITY_BUILD` to `OFF` if you need to ensure that a
+   target doesn't get a unity build.
+
+The batch size can be specified by setting
+:prop_tgt:`UNITY_BUILD_BATCH_SIZE`.
+
+The batching of source files is done by adding new sources files
+wich will `#include` the source files, and exclude them from
+building by setting :prop_sf:`HEADER_FILE_ONLY` to `ON`.
+
+
+ODR (One definition rule) errors
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Since multiple source files are included into one source file,
+it can lead to ODR errors. This section contains properites
+which help fixing these errors.
+
+The source files marked by :prop_sf:`GENERATED` will be skipped
+from unity build. This applies also for the source files marked
+with :prop_sf:`SKIP_UNITY_BUILD_INCLUSION`.
+
+The source files that have :prop_sf:`COMPILE_OPTIONS`,
+:prop_sf:`COMPILE_DEFINITIONS`, :prop_sf:`COMPILE_FLAGS`, or
+:prop_sf:`INCLUDE_DIRECTORIES` will also be skipped.
+
+With the :prop_tgt:`UNITY_BUILD_CODE_BEFORE_INCLUDE` and
+:prop_tgt:`UNITY_BUILD_CODE_AFTER_INCLUDE` one can specify code
+to be injected in the unity source file before and after every
+`#include` statement.
+
+.. note ::
+
+   The order of source files defined in the `CMakeLists.txt` will
+   be preserved into the generated unity source files. This can
+   be used to manually enforce a specific grouping based on the
+   :prop_tgt:`UNITY_BUILD_BATCH_SIZE`.

+ 13 - 0
Help/prop_tgt/UNITY_BUILD_BATCH_SIZE.rst

@@ -0,0 +1,13 @@
+UNITY_BUILD_BATCH_SIZE
+----------------------
+
+Specifies how many source code files will be included into a
+:prop_tgt:`UNITY_BUILD` source file.
+
+If the property is not set, CMake will use the value provided
+by :variable:`CMAKE_UNITY_BUILD_BATCH_SIZE`.
+
+By setting it to value `0` the generated unity source file will
+contain all the source files that would be otherwise be split
+into multiple batches. It is not recommended to do so, since it
+would affect performance.

+ 8 - 0
Help/prop_tgt/UNITY_BUILD_CODE_AFTER_INCLUDE.rst

@@ -0,0 +1,8 @@
+UNITY_BUILD_CODE_AFTER_INCLUDE
+------------------------------
+
+Code snippet which is included verbatim by the :prop_tgt:`UNITY_BUILD`
+feature just after the `#include` statement of the targeted source
+files.
+
+This could be something like `#undef NOMINMAX`.

+ 8 - 0
Help/prop_tgt/UNITY_BUILD_CODE_BEFORE_INCLUDE.rst

@@ -0,0 +1,8 @@
+UNITY_BUILD_CODE_BEFORE_INCLUDE
+-------------------------------
+
+Code snippet which is included verbatim by the :prop_tgt:`UNITY_BUILD`
+feature just before the `#include` statement of the targeted source
+files.
+
+This could be something like `#define NOMINMAX`.

+ 6 - 0
Help/release/dev/unity-build.rst

@@ -0,0 +1,6 @@
+Unity build
+-----------
+
+* The :prop_tgt:`UNITY_BUILD` target property was added to tell
+  generators to batch include source files for faster compilation
+  times.

+ 6 - 0
Help/variable/CMAKE_UNITY_BUILD.rst

@@ -0,0 +1,6 @@
+CMAKE_UNITY_BUILD
+-----------------
+
+Default value for :prop_tgt:`UNITY_BUILD` of targets.
+
+By default ``CMAKE_UNITY_BUILD`` is ``OFF``.

+ 6 - 0
Help/variable/CMAKE_UNITY_BUILD_BATCH_SIZE.rst

@@ -0,0 +1,6 @@
+CMAKE_UNITY_BUILD_BATCH_SIZE
+----------------------------
+
+Default value for :prop_tgt:`UNITY_BUILD_BATCH_SIZE` of targets.
+
+By default ``CMAKE_UNITY_BUILD_BATCH_SIZE`` is set to ``8``.

+ 85 - 0
Source/cmLocalGenerator.cxx

@@ -38,6 +38,7 @@
 
 #include <algorithm>
 #include <assert.h>
+#include <cstdlib>
 #include <functional>
 #include <initializer_list>
 #include <iterator>
@@ -2202,6 +2203,90 @@ void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target,
   }
 }
 
+void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target,
+                                     const std::string& config)
+{
+  if (!target->GetPropertyAsBool("UNITY_BUILD")) {
+    return;
+  }
+
+  const std::string buildType = cmSystemTools::UpperCase(config);
+
+  std::string filename_base =
+    cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/",
+             target->GetName(), ".dir/Unity/");
+
+  std::vector<cmSourceFile*> sources;
+  target->GetSourceFiles(sources, buildType);
+
+  auto batchSizeString = target->GetProperty("UNITY_BUILD_BATCH_SIZE");
+  const size_t unityBatchSize =
+    static_cast<size_t>(std::atoi(batchSizeString));
+
+  auto beforeInclude = target->GetProperty("UNITY_BUILD_CODE_BEFORE_INCLUDE");
+  auto afterInclude = target->GetProperty("UNITY_BUILD_CODE_AFTER_INCLUDE");
+
+  for (std::string lang : { "C", "CXX" }) {
+    std::vector<cmSourceFile*> filtered_sources;
+    std::copy_if(sources.begin(), sources.end(),
+                 std::back_inserter(filtered_sources), [&](cmSourceFile* sf) {
+                   return sf->GetLanguage() == lang &&
+                     !sf->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION") &&
+                     !sf->GetPropertyAsBool("GENERATED") &&
+                     !sf->GetProperty("COMPILE_OPTIONS") &&
+                     !sf->GetProperty("COMPILE_DEFINITIONS") &&
+                     !sf->GetProperty("COMPILE_FLAGS") &&
+                     !sf->GetProperty("INCLUDE_DIRECTORIES");
+                 });
+
+    size_t batchSize = unityBatchSize;
+    if (unityBatchSize == 0) {
+      batchSize = filtered_sources.size();
+    }
+
+    for (size_t itemsLeft = filtered_sources.size(), chunk = batchSize,
+                batch = 0;
+         itemsLeft > 0; itemsLeft -= chunk, ++batch) {
+
+      chunk = std::min(itemsLeft, batchSize);
+
+      std::string filename = cmStrCat(filename_base, "unity_", batch,
+                                      (lang == "C") ? ".c" : ".cxx");
+
+      const std::string filename_tmp = cmStrCat(filename, ".tmp");
+      {
+        size_t begin = batch * batchSize;
+        size_t end = begin + chunk;
+
+        cmGeneratedFileStream file(
+          filename_tmp, false,
+          this->GetGlobalGenerator()->GetMakefileEncoding());
+        file << "/* generated by CMake */\n\n";
+
+        for (; begin != end; ++begin) {
+          cmSourceFile* sf = filtered_sources[begin];
+
+          sf->SetProperty("HEADER_FILE_ONLY", "ON");
+
+          if (beforeInclude) {
+            file << beforeInclude << "\n";
+          }
+
+          file << "#include \"" << sf->GetFullPath() << "\"\n";
+
+          if (afterInclude) {
+            file << afterInclude << "\n";
+          }
+        }
+      }
+      cmSystemTools::CopyFileIfDifferent(filename_tmp, filename);
+      cmSystemTools::RemoveFile(filename_tmp);
+
+      target->AddSource(filename, true);
+    }
+  }
+}
+
 void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags,
                                             cmGeneratorTarget* target,
                                             const std::string& config,

+ 1 - 0
Source/cmLocalGenerator.h

@@ -126,6 +126,7 @@ public:
                                 const std::string& rawFlag) const;
   void AddPchDependencies(cmGeneratorTarget* target,
                           const std::string& config);
+  void AddUnityBuild(cmGeneratorTarget* target, const std::string& config);
   void AppendIPOLinkerFlags(std::string& flags, cmGeneratorTarget* target,
                             const std::string& config,
                             const std::string& lang);

+ 1 - 0
Source/cmMakefileExecutableTargetGenerator.cxx

@@ -41,6 +41,7 @@ cmMakefileExecutableTargetGenerator::cmMakefileExecutableTargetGenerator(
     cm::make_unique<cmOSXBundleGenerator>(target, this->ConfigName);
   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
 
+  this->LocalGenerator->AddUnityBuild(target, this->ConfigName);
   this->LocalGenerator->AddPchDependencies(target, this->ConfigName);
 }
 

+ 1 - 0
Source/cmMakefileLibraryTargetGenerator.cxx

@@ -43,6 +43,7 @@ cmMakefileLibraryTargetGenerator::cmMakefileLibraryTargetGenerator(
     cm::make_unique<cmOSXBundleGenerator>(target, this->ConfigName);
   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
 
+  this->LocalGenerator->AddUnityBuild(target, this->ConfigName);
   this->LocalGenerator->AddPchDependencies(target, this->ConfigName);
 }
 

+ 1 - 0
Source/cmMakefileUtilityTargetGenerator.cxx

@@ -26,6 +26,7 @@ cmMakefileUtilityTargetGenerator::cmMakefileUtilityTargetGenerator(
     cm::make_unique<cmOSXBundleGenerator>(target, this->ConfigName);
   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
 
+  this->LocalGenerator->AddUnityBuild(target, this->ConfigName);
   this->LocalGenerator->AddPchDependencies(target, this->ConfigName);
 }
 

+ 1 - 0
Source/cmNinjaNormalTargetGenerator.cxx

@@ -60,6 +60,7 @@ cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator(
     cm::make_unique<cmOSXBundleGenerator>(target, this->GetConfigName());
   this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
 
+  GetLocalGenerator()->AddUnityBuild(target, this->GetConfigName());
   GetLocalGenerator()->AddPchDependencies(target, this->GetConfigName());
 }
 

+ 2 - 0
Source/cmTarget.cxx

@@ -352,6 +352,8 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
     initProp("Swift_MODULE_DIRECTORY");
     initProp("VS_JUST_MY_CODE_DEBUGGING");
     initProp("DISABLE_PRECOMPILE_HEADERS");
+    initProp("UNITY_BUILD");
+    initPropValue("UNITY_BUILD_BATCH_SIZE", "8");
 #ifdef __APPLE__
     if (this->GetGlobalGenerator()->IsXcode()) {
       initProp("XCODE_GENERATE_SCHEME");