Commit 7114c141 authored by Cristian Adam's avatar Cristian Adam

Unity build: Add support for Ninja and Makefile generators

parent 3ec986ce
......@@ -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
......
......@@ -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
......
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`.
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`.
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.
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`.
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`.
Unity build
-----------
* The :prop_tgt:`UNITY_BUILD` target property was added to tell
generators to batch include source files for faster compilation
times.
CMAKE_UNITY_BUILD
-----------------
Default value for :prop_tgt:`UNITY_BUILD` of targets.
By default ``CMAKE_UNITY_BUILD`` is ``OFF``.
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``.
......@@ -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,
......
......@@ -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);
......
......@@ -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);
}
......
......@@ -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);
}
......
......@@ -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);
}
......
......@@ -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());
}
......
......@@ -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");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment