Unity build: Add support for Ninja and Makefile generators

This commit is contained in:
Cristian Adam 2019-07-26 16:08:32 +02:00
parent 3ec986ce8e
commit 7114c141e2
17 changed files with 208 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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