You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cmake/Source/cmQtAutoGenInitializer.cxx

2515 lines
86 KiB

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmQtAutoGenInitializer.h"
#include <array>
#include <cstddef>
#include <deque>
#include <initializer_list>
#include <limits>
#include <map>
#include <set>
#include <sstream> // for basic_ios, istringstream
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#include <cm/algorithm>
#include <cm/iterator>
#include <cm/memory>
#include <cm/string_view>
#include <cmext/algorithm>
#include <cmext/string_view>
#include <cm3p/json/value.h>
#include <cm3p/json/writer.h>
#include "cmsys/SystemInformation.hxx"
#include "cmAlgorithms.h"
#include "cmCustomCommand.h"
#include "cmCustomCommandLines.h"
#include "cmEvaluatedTargetProperty.h"
#include "cmGeneratedFileStream.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h"
#include "cmLinkItem.h"
#include "cmList.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmQtAutoGen.h"
#include "cmQtAutoGenGlobalInitializer.h"
#include "cmSourceFile.h"
#include "cmSourceFileLocationKind.h"
#include "cmSourceGroup.h"
#include "cmStandardLevelResolver.h"
#include "cmState.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmValue.h"
#include "cmake.h"
namespace {
unsigned int GetParallelCPUCount()
{
static unsigned int count = 0;
// Detect only on the first call
if (count == 0) {
cmsys::SystemInformation info;
info.RunCPUCheck();
count =
cm::clamp(info.GetNumberOfPhysicalCPU(), 1u, cmQtAutoGen::ParallelMax);
}
return count;
}
std::string FileProjectRelativePath(cmMakefile const* makefile,
std::string const& fileName)
{
std::string res;
{
std::string pSource = cmSystemTools::RelativePath(
makefile->GetCurrentSourceDirectory(), fileName);
std::string pBinary = cmSystemTools::RelativePath(
makefile->GetCurrentBinaryDirectory(), fileName);
if (pSource.size() < pBinary.size()) {
res = std::move(pSource);
} else if (pBinary.size() < fileName.size()) {
res = std::move(pBinary);
} else {
res = fileName;
}
}
return res;
}
/**
* Tests if targetDepend is a STATIC_LIBRARY and if any of its
* recursive STATIC_LIBRARY dependencies depends on targetOrigin
* (STATIC_LIBRARY cycle).
*/
bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin,
cmGeneratorTarget const* targetDepend,
std::string const& config)
{
bool cycle = false;
if ((targetOrigin->GetType() == cmStateEnums::STATIC_LIBRARY) &&
(targetDepend->GetType() == cmStateEnums::STATIC_LIBRARY)) {
std::set<cmGeneratorTarget const*> knownLibs;
std::deque<cmGeneratorTarget const*> testLibs;
// Insert initial static_library dependency
knownLibs.insert(targetDepend);
testLibs.push_back(targetDepend);
while (!testLibs.empty()) {
cmGeneratorTarget const* testTarget = testLibs.front();
testLibs.pop_front();
// Check if the test target is the origin target (cycle)
if (testTarget == targetOrigin) {
cycle = true;
break;
}
// Collect all static_library dependencies from the test target
cmLinkImplementationLibraries const* libs =
testTarget->GetLinkImplementationLibraries(
config, cmGeneratorTarget::UseTo::Link);
if (libs) {
for (cmLinkItem const& item : libs->Libraries) {
cmGeneratorTarget const* depTarget = item.Target;
if (depTarget &&
(depTarget->GetType() == cmStateEnums::STATIC_LIBRARY) &&
knownLibs.insert(depTarget).second) {
testLibs.push_back(depTarget);
}
}
}
}
}
return cycle;
}
/** Sanitizes file search paths. */
class SearchPathSanitizer
{
public:
SearchPathSanitizer(cmMakefile* makefile)
: SourcePath_(makefile->GetCurrentSourceDirectory())
{
}
std::vector<std::string> operator()(
std::vector<std::string> const& paths) const;
private:
std::string SourcePath_;
};
std::vector<std::string> SearchPathSanitizer::operator()(
std::vector<std::string> const& paths) const
{
std::vector<std::string> res;
res.reserve(paths.size());
for (std::string const& srcPath : paths) {
// Collapse relative paths
std::string path =
cmSystemTools::CollapseFullPath(srcPath, this->SourcePath_);
// Remove suffix slashes
while (cmHasSuffix(path, '/')) {
path.pop_back();
}
// Accept only non empty paths
if (!path.empty()) {
res.emplace_back(std::move(path));
}
}
return res;
}
/** @brief Writes a CMake info file. */
class InfoWriter
{
public:
// -- Single value
void Set(std::string const& key, std::string const& value)
{
this->Value_[key] = value;
}
void SetConfig(std::string const& key,
cmQtAutoGenInitializer::ConfigString const& cfgStr);
void SetBool(std::string const& key, bool value)
{
this->Value_[key] = value;
}
void SetUInt(std::string const& key, unsigned int value)
{
this->Value_[key] = value;
}
// -- Array utility
template <typename CONT>
static bool MakeArray(Json::Value& jval, CONT const& container);
template <typename CONT>
static void MakeStringArray(Json::Value& jval, CONT const& container);
// -- Array value
template <typename CONT>
void SetArray(std::string const& key, CONT const& container);
template <typename CONT>
void SetConfigArray(
std::string const& key,
cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr);
// -- Array of arrays
template <typename CONT, typename FUNC>
void SetArrayArray(std::string const& key, CONT const& container, FUNC func);
// -- Save to json file
bool Save(std::string const& filename);
private:
Json::Value Value_;
};
void InfoWriter::SetConfig(std::string const& key,
cmQtAutoGenInitializer::ConfigString const& cfgStr)
{
this->Set(key, cfgStr.Default);
for (auto const& item : cfgStr.Config) {
this->Set(cmStrCat(key, '_', item.first), item.second);
}
}
template <typename CONT>
bool InfoWriter::MakeArray(Json::Value& jval, CONT const& container)
{
jval = Json::arrayValue;
std::size_t const listSize = cm::size(container);
if (listSize == 0) {
return false;
}
jval.resize(static_cast<unsigned int>(listSize));
return true;
}
template <typename CONT>
void InfoWriter::MakeStringArray(Json::Value& jval, CONT const& container)
{
if (MakeArray(jval, container)) {
Json::ArrayIndex ii = 0;
for (std::string const& item : container) {
jval[ii++] = item;
}
}
}
template <typename CONT>
void InfoWriter::SetArray(std::string const& key, CONT const& container)
{
MakeStringArray(this->Value_[key], container);
}
template <typename CONT, typename FUNC>
void InfoWriter::SetArrayArray(std::string const& key, CONT const& container,
FUNC func)
{
Json::Value& jval = this->Value_[key];
if (MakeArray(jval, container)) {
Json::ArrayIndex ii = 0;
for (auto const& citem : container) {
Json::Value& aval = jval[ii++];
aval = Json::arrayValue;
func(aval, citem);
}
}
}
template <typename CONT>
void InfoWriter::SetConfigArray(
std::string const& key,
cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr)
{
this->SetArray(key, cfgStr.Default);
for (auto const& item : cfgStr.Config) {
this->SetArray(cmStrCat(key, '_', item.first), item.second);
}
}
bool InfoWriter::Save(std::string const& filename)
{
cmGeneratedFileStream fileStream;
fileStream.SetCopyIfDifferent(true);
fileStream.Open(filename, false, true);
if (!fileStream) {
return false;
}
Json::StyledStreamWriter jsonWriter;
try {
jsonWriter.write(fileStream, this->Value_);
} catch (...) {
return false;
}
return fileStream.Close();
}
cmQtAutoGen::ConfigStrings<std::vector<std::string>> generateListOptions(
cmQtAutoGen::ConfigStrings<cmQtAutoGen::CompilerFeaturesHandle> const&
executableFeatures,
bool IsMultiConfig)
{
cmQtAutoGen::ConfigStrings<std::vector<std::string>> tempListOptions;
if (IsMultiConfig) {
for (auto const& executableFeature : executableFeatures.Config) {
tempListOptions.Config[executableFeature.first] =
executableFeature.second->ListOptions;
}
} else {
tempListOptions.Default = executableFeatures.Default->ListOptions;
}
return tempListOptions;
}
} // End of unnamed namespace
cmQtAutoGenInitializer::cmQtAutoGenInitializer(
cmQtAutoGenGlobalInitializer* globalInitializer,
cmGeneratorTarget* genTarget, IntegerVersion const& qtVersion,
bool mocEnabled, bool uicEnabled, bool rccEnabled, bool globalAutogenTarget,
bool globalAutoRccTarget)
: GlobalInitializer(globalInitializer)
, GenTarget(genTarget)
, GlobalGen(genTarget->GetGlobalGenerator())
, LocalGen(genTarget->GetLocalGenerator())
, Makefile(genTarget->Makefile)
, PathCheckSum(genTarget->Makefile)
, QtVersion(qtVersion)
{
this->AutogenTarget.GlobalTarget = globalAutogenTarget;
this->Moc.Enabled = mocEnabled;
this->Uic.Enabled = uicEnabled;
this->Rcc.Enabled = rccEnabled;
this->Rcc.GlobalTarget = globalAutoRccTarget;
this->CrossConfig =
!this->Makefile->GetSafeDefinition("CMAKE_CROSS_CONFIGS").empty();
this->UseBetterGraph =
this->GenTarget->GetProperty("AUTOGEN_BETTER_GRAPH_MULTI_CONFIG").IsSet()
? this->GenTarget->GetProperty("AUTOGEN_BETTER_GRAPH_MULTI_CONFIG").IsOn()
: (this->QtVersion >= IntegerVersion(6, 8));
// AUTOGEN_BETTER_GRAPH_MULTI_CONFIG is set explicitly because it is read by
// the qt library
this->GenTarget->Target->SetProperty("AUTOGEN_BETTER_GRAPH_MULTI_CONFIG",
this->UseBetterGraph ? "ON" : "OFF");
}
void cmQtAutoGenInitializer::AddAutogenExecutableToDependencies(
cmQtAutoGenInitializer::GenVarsT const& genVars,
std::vector<std::string>& dependencies) const
{
if (genVars.ExecutableTarget) {
dependencies.push_back(genVars.ExecutableTarget->Target->GetName());
} else if (this->MultiConfig && this->UseBetterGraph) {
cm::string_view const& configGenexWithCommandConfig =
"$<COMMAND_CONFIG:$<$<CONFIG:";
cm::string_view const& configGenex = "$<$<CONFIG:";
cm::string_view const& configGenexEnd = ">";
cm::string_view const& configGenexEndWithCommandConfig = ">>";
auto genexBegin =
this->CrossConfig ? configGenexWithCommandConfig : configGenex;
auto genexEnd =
this->CrossConfig ? configGenexEndWithCommandConfig : configGenexEnd;
for (auto const& config : genVars.Executable.Config) {
auto executableWithConfig =
cmStrCat(genexBegin, config.first, ">:", config.second, genexEnd);
dependencies.emplace_back(std::move(executableWithConfig));
}
} else {
if (!genVars.Executable.Default.empty()) {
dependencies.push_back(genVars.Executable.Default);
}
}
}
bool cmQtAutoGenInitializer::InitCustomTargets()
{
// Configurations
this->MultiConfig = this->GlobalGen->IsMultiConfig();
this->ConfigDefault = this->Makefile->GetDefaultConfiguration();
this->ConfigsList =
this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
// Verbosity
{
std::string const def =
this->Makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE");
if (!def.empty()) {
unsigned long iVerb = 0;
if (cmStrToULong(def, &iVerb)) {
// Numeric verbosity
this->Verbosity = static_cast<unsigned int>(iVerb);
} else {
// Non numeric verbosity
if (cmIsOn(def)) {
this->Verbosity = 1;
}
}
}
}
// Targets FOLDER
{
cmValue folder =
this->Makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
if (!folder) {
folder = this->Makefile->GetState()->GetGlobalProperty(
"AUTOGEN_TARGETS_FOLDER");
}
// Inherit FOLDER property from target (#13688)
if (!folder) {
folder = this->GenTarget->GetProperty("FOLDER");
}
if (folder) {
this->TargetsFolder = *folder;
}
}
// Check status of policy CMP0071 regarding handling of GENERATED files
switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0071)) {
case cmPolicies::WARN:
// Ignore GENERATED files but warn
this->CMP0071Warn = true;
CM_FALLTHROUGH;
case cmPolicies::OLD:
// Ignore GENERATED files
break;
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS:
case cmPolicies::NEW:
// Process GENERATED files
this->CMP0071Accept = true;
break;
}
// Check status of policy CMP0100 regarding handling of .hh headers
switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0100)) {
case cmPolicies::WARN:
// Ignore but .hh files but warn
this->CMP0100Warn = true;
CM_FALLTHROUGH;
case cmPolicies::OLD:
// Ignore .hh files
break;
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS:
case cmPolicies::NEW:
// Process .hh file
this->CMP0100Accept = true;
break;
}
// Common directories
std::string relativeBuildDir;
{
// Collapsed current binary directory
std::string const cbd = cmSystemTools::CollapseFullPath(
std::string(), this->Makefile->GetCurrentBinaryDirectory());
// Info directory
this->Dir.Info = cmStrCat(cbd, "/CMakeFiles/", this->GenTarget->GetName(),
"_autogen.dir");
cmSystemTools::ConvertToUnixSlashes(this->Dir.Info);
// Build directory
this->Dir.Build = this->GenTarget->GetSafeProperty("AUTOGEN_BUILD_DIR");
if (this->Dir.Build.empty()) {
this->Dir.Build =
cmStrCat(cbd, '/', this->GenTarget->GetName(), "_autogen");
}
cmSystemTools::ConvertToUnixSlashes(this->Dir.Build);
this->Dir.RelativeBuild =
cmSystemTools::RelativePath(cbd, this->Dir.Build);
// Cleanup build directory
this->AddCleanFile(this->Dir.Build);
// Working directory
this->Dir.Work = cbd;
cmSystemTools::ConvertToUnixSlashes(this->Dir.Work);
// Include directory
this->ConfigFileNamesAndGenex(this->Dir.Include, this->Dir.IncludeGenExp,
cmStrCat(this->Dir.Build, "/include"), "");
}
// Moc, Uic and _autogen target settings
if (this->MocOrUicEnabled()) {
// Init moc specific settings
if (this->Moc.Enabled && !this->InitMoc()) {
return false;
}
// Init uic specific settings
if (this->Uic.Enabled && !this->InitUic()) {
return false;
}
// Autogen target name
this->AutogenTarget.Name =
cmStrCat(this->GenTarget->GetName(), "_autogen");
// Autogen target parallel processing
{
using ParallelType = decltype(this->AutogenTarget.Parallel);
unsigned long propInt = 0;
std::string const& prop =
this->GenTarget->GetSafeProperty("AUTOGEN_PARALLEL");
if (prop.empty() || (prop == "AUTO")) {
// Autodetect number of CPUs
this->AutogenTarget.Parallel = GetParallelCPUCount();
} else if (cmStrToULong(prop, &propInt) && propInt > 0 &&
propInt <= std::numeric_limits<ParallelType>::max()) {
this->AutogenTarget.Parallel = static_cast<ParallelType>(propInt);
} else {
// Warn the project author that AUTOGEN_PARALLEL is not valid.
this->Makefile->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat("AUTOGEN_PARALLEL=\"", prop, "\" for target \"",
this->GenTarget->GetName(),
"\" is not valid. Using AUTOGEN_PARALLEL=1"));
this->AutogenTarget.Parallel = 1;
}
}
#ifdef _WIN32
{
const auto& value =
this->GenTarget->GetProperty("AUTOGEN_COMMAND_LINE_LENGTH_MAX");
if (value.IsSet()) {
using maxCommandLineLengthType =
decltype(this->AutogenTarget.MaxCommandLineLength);
unsigned long propInt = 0;
if (cmStrToULong(value, &propInt) && propInt > 0 &&
propInt <= std::numeric_limits<maxCommandLineLengthType>::max()) {
this->AutogenTarget.MaxCommandLineLength =
static_cast<maxCommandLineLengthType>(propInt);
} else {
// Warn the project author that AUTOGEN_PARALLEL is not valid.
this->Makefile->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat("AUTOGEN_COMMAND_LINE_LENGTH_MAX=\"", *value,
"\" for target \"", this->GenTarget->GetName(),
"\" is not valid. Using no limit for "
"AUTOGEN_COMMAND_LINE_LENGTH_MAX"));
this->AutogenTarget.MaxCommandLineLength =
std::numeric_limits<maxCommandLineLengthType>::max();
}
} else {
// Actually 32767 (see
// https://devblogs.microsoft.com/oldnewthing/20031210-00/?p=41553) but
// we allow for a small margin
this->AutogenTarget.MaxCommandLineLength = 32000;
}
}
#endif
// Autogen target info and settings files
{
// Info file
this->AutogenTarget.InfoFile =
cmStrCat(this->Dir.Info, "/AutogenInfo.json");
// Used settings file
this->ConfigFileNames(this->AutogenTarget.SettingsFile,
cmStrCat(this->Dir.Info, "/AutogenUsed"), ".txt");
this->ConfigFileClean(this->AutogenTarget.SettingsFile);
// Parse cache file
this->ConfigFileNames(this->AutogenTarget.ParseCacheFile,
cmStrCat(this->Dir.Info, "/ParseCache"), ".txt");
this->ConfigFileClean(this->AutogenTarget.ParseCacheFile);
}
// Autogen target: Compute user defined dependencies
{
this->AutogenTarget.DependOrigin =
this->GenTarget->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS");
std::string const& deps =
this->GenTarget->GetSafeProperty("AUTOGEN_TARGET_DEPENDS");
if (!deps.empty()) {
for (auto const& depName : cmList{ deps }) {
// Allow target and file dependencies
auto* depTarget = this->Makefile->FindTargetToUse(depName);
if (depTarget) {
this->AutogenTarget.DependTargets.insert(depTarget);
} else {
this->AutogenTarget.DependFiles.insert(depName);
}
}
}
}
if (this->Moc.Enabled) {
// Path prefix
if (this->GenTarget->GetProperty("AUTOMOC_PATH_PREFIX").IsOn()) {
this->Moc.PathPrefix = true;
}
// CMAKE_AUTOMOC_RELAXED_MODE
if (this->Makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE")) {
this->Moc.RelaxedMode = true;
this->Makefile->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat("AUTOMOC: CMAKE_AUTOMOC_RELAXED_MODE is "
"deprecated an will be removed in the future. Consider "
"disabling it and converting the target ",
this->GenTarget->GetName(), " to regular mode."));
}
// Options
cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MOC_OPTIONS"),
this->Moc.Options);
// Filters
cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MACRO_NAMES"),
this->Moc.MacroNames);
this->Moc.MacroNames.erase(cmRemoveDuplicates(this->Moc.MacroNames),
this->Moc.MacroNames.end());
{
cmList const filterList = { this->GenTarget->GetSafeProperty(
"AUTOMOC_DEPEND_FILTERS") };
if ((filterList.size() % 2) != 0) {
cmSystemTools::Error(
cmStrCat("AutoMoc: AUTOMOC_DEPEND_FILTERS predefs size ",
filterList.size(), " is not a multiple of 2."));
return false;
}
this->Moc.DependFilters.reserve(1 + (filterList.size() / 2));
this->Moc.DependFilters.emplace_back(
"Q_PLUGIN_METADATA",
"[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\("
"[^\\)]*FILE[ \t]*\"([^\"]+)\"");
for (cmList::size_type ii = 0; ii != filterList.size(); ii += 2) {
this->Moc.DependFilters.emplace_back(filterList[ii],
filterList[ii + 1]);
}
}
}
}
// Init rcc specific settings
if (this->Rcc.Enabled && !this->InitRcc()) {
return false;
}
// Add autogen include directory to the origin target INCLUDE_DIRECTORIES
if (this->MocOrUicEnabled() || (this->Rcc.Enabled && this->MultiConfig)) {
auto addBefore = false;
auto const& value =
this->GenTarget->GetProperty("AUTOGEN_USE_SYSTEM_INCLUDE");
if (value.IsSet()) {
if (value.IsOn()) {
this->GenTarget->AddSystemIncludeDirectory(this->Dir.IncludeGenExp,
"CXX");
} else {
addBefore = true;
}
} else {
switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0151)) {
case cmPolicies::WARN:
case cmPolicies::OLD:
addBefore = true;
break;
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::REQUIRED_ALWAYS:
case cmPolicies::NEW:
this->GenTarget->AddSystemIncludeDirectory(this->Dir.IncludeGenExp,
"CXX");
break;
}
}
this->GenTarget->AddIncludeDirectory(this->Dir.IncludeGenExp, addBefore);
}
// Scan files
if (!this->InitScanFiles()) {
return false;
}
// Create autogen target
if (this->MocOrUicEnabled() && !this->InitAutogenTarget()) {
return false;
}
// Create rcc targets
if (this->Rcc.Enabled && !this->InitRccTargets()) {
return false;
}
return true;
}
bool cmQtAutoGenInitializer::InitMoc()
{
// Mocs compilation file
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 {
this->ConfigFileNamesAndGenex(
this->Moc.CompilationFile, this->Moc.CompilationFileGenex,
cmStrCat(this->Dir.Build, "/mocs_compilation"_s), ".cpp"_s);
}
// Moc predefs
if (this->GenTarget->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
(this->QtVersion >= IntegerVersion(5, 8))) {
// Command
cmList::assign(
this->Moc.PredefsCmd,
this->Makefile->GetDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND"));
// Header
if (!this->Moc.PredefsCmd.empty()) {
this->ConfigFileNames(this->Moc.PredefsFile,
cmStrCat(this->Dir.Build, "/moc_predefs"), ".h");
}
}
// Moc includes
{
SearchPathSanitizer const sanitizer(this->Makefile);
auto getDirs =
[this, &sanitizer](std::string const& cfg) -> std::vector<std::string> {
// Get the include dirs for this target, without stripping the implicit
// include dirs off, see issue #13667.
std::vector<std::string> dirs;
bool const appendImplicit = (this->QtVersion.Major >= 5);
this->LocalGen->GetIncludeDirectoriesImplicit(
dirs, this->GenTarget, "CXX", cfg, false, appendImplicit);
return sanitizer(dirs);
};
// Other configuration settings
if (this->MultiConfig) {
for (std::string const& cfg : this->ConfigsList) {
std::vector<std::string> dirs = getDirs(cfg);
if (dirs == this->Moc.Includes.Default) {
continue;
}
this->Moc.Includes.Config[cfg] = std::move(dirs);
}
} else {
// Default configuration include directories
this->Moc.Includes.Default = getDirs(this->ConfigDefault);
}
}
// Moc compile definitions
{
auto getDefs = [this](std::string const& cfg) -> std::set<std::string> {
std::set<std::string> defines;
this->LocalGen->GetTargetDefines(this->GenTarget, cfg, "CXX", defines);
if (this->Moc.PredefsCmd.empty() &&
this->Makefile->GetSafeDefinition("CMAKE_SYSTEM_NAME") ==
"Windows") {
// Add WIN32 definition if we don't have a moc_predefs.h
defines.insert("WIN32");
}
return defines;
};
// Other configuration defines
if (this->MultiConfig) {
for (std::string const& cfg : this->ConfigsList) {
std::set<std::string> defines = getDefs(cfg);
if (defines == this->Moc.Defines.Default) {
continue;
}
this->Moc.Defines.Config[cfg] = std::move(defines);
}
} else {
// Default configuration defines
this->Moc.Defines.Default = getDefs(this->ConfigDefault);
}
}
// Moc executable
{
if (!this->GetQtExecutable(this->Moc, "moc", false)) {
return false;
}
// Let the _autogen target depend on the moc executable
if (this->Moc.ExecutableTarget) {
this->AutogenTarget.DependTargets.insert(
this->Moc.ExecutableTarget->Target);
}
}
return true;
}
bool cmQtAutoGenInitializer::InitUic()
{
// Uic search paths
{
std::string const& usp =
this->GenTarget->GetSafeProperty("AUTOUIC_SEARCH_PATHS");
if (!usp.empty()) {
this->Uic.SearchPaths =
SearchPathSanitizer(this->Makefile)(cmList{ usp });
}
}
// Uic target options
{
auto getOpts = [this](std::string const& cfg) -> std::vector<std::string> {
std::vector<std::string> opts;
this->GenTarget->GetAutoUicOptions(opts, cfg);
return opts;
};
// Default options
this->Uic.Options.Default = getOpts(this->ConfigDefault);
// Configuration specific options
if (this->MultiConfig) {
for (std::string const& cfg : this->ConfigsList) {
std::vector<std::string> options = getOpts(cfg);
if (options == this->Uic.Options.Default) {
continue;
}
this->Uic.Options.Config[cfg] = std::move(options);
}
}
}
// Uic executable
{
if (!this->GetQtExecutable(this->Uic, "uic", true)) {
return false;
}
// Let the _autogen target depend on the uic executable
if (this->Uic.ExecutableTarget) {
this->AutogenTarget.DependTargets.insert(
this->Uic.ExecutableTarget->Target);
}
}
return true;
}
bool cmQtAutoGenInitializer::InitRcc()
{
// Rcc executable
{
if (!this->GetQtExecutable(this->Rcc, "rcc", false)) {
return false;
}
// Evaluate test output on demand
auto& features = this->Rcc.ExecutableFeatures;
auto checkAndAddOptions = [this](CompilerFeaturesHandle& feature) {
if (!feature->Evaluated) {
// Look for list options
if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) {
static std::array<std::string, 2> const listOptions{ { "--list",
"-list" } };
for (std::string const& opt : listOptions) {
if (feature->HelpOutput.find(opt) != std::string::npos) {
feature->ListOptions.emplace_back(opt);
break;
}
}
}
// Evaluation finished
feature->Evaluated = true;
}
};
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
checkAndAddOptions(features.Config[config]);
}
} else {
checkAndAddOptions(features.Default);
}
}
// Disable zstd if it is not supported
{
std::string const qtFeatureZSTD = "QT_FEATURE_zstd";
if (this->GenTarget->Target->GetMakefile()->IsDefinitionSet(
qtFeatureZSTD)) {
const auto zstdDef =
this->GenTarget->Target->GetMakefile()->GetSafeDefinition(
qtFeatureZSTD);
const auto zstdVal = cmValue(zstdDef);
if (zstdVal.IsOff()) {
auto const& kw = this->GlobalInitializer->kw();
auto rccOptions = this->GenTarget->GetSafeProperty(kw.AUTORCC_OPTIONS);
std::string const nozstd = "--no-zstd";
if (rccOptions.find(nozstd) == std::string::npos) {
rccOptions.append(";" + nozstd + ";");
}
this->GenTarget->Target->SetProperty(kw.AUTORCC_OPTIONS, rccOptions);
}
}
}
return true;
}
bool cmQtAutoGenInitializer::InitScanFiles()
{
cmake const* cm = this->Makefile->GetCMakeInstance();
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 &&
(skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOMOC));
muf->SkipUic = this->Uic.Enabled &&
(skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC));
if (muIt) {
muf->MocIt = this->Moc.Enabled && !muf->SkipMoc;
muf->UicIt = this->Uic.Enabled && !muf->SkipUic;
}
return muf;
};
auto addMUHeader = [this](MUFileHandle&& muf, cm::string_view extension) {
cmSourceFile* sf = muf->SF;
const bool muIt = (muf->MocIt || muf->UicIt);
if (this->CMP0100Accept || (extension != "hh")) {
// Accept
if (muIt && muf->Generated) {
this->AutogenTarget.FilesGenerated.emplace_back(muf.get());
}
this->AutogenTarget.Headers.emplace(sf, std::move(muf));
} else if (muIt && this->CMP0100Warn) {
// Store file for warning message
this->AutogenTarget.CMP0100HeadersWarn.push_back(sf);
}
};
auto addMUSource = [this](MUFileHandle&& muf) {
if ((muf->MocIt || muf->UicIt) && muf->Generated) {
this->AutogenTarget.FilesGenerated.emplace_back(muf.get());
}
this->AutogenTarget.Sources.emplace(muf->SF, std::move(muf));
};
// Scan through target files
{
// Scan through target files
for (cmGeneratorTarget::AllConfigSource const& acs :
this->GenTarget->GetAllConfigSources()) {
std::string const& fullPath = acs.Source->GetFullPath();
std::string const& extLower =
cmSystemTools::LowerCase(acs.Source->GetExtension());
// Register files that will be scanned by moc or uic
if (this->MocOrUicEnabled()) {
if (cm->IsAHeaderExtension(extLower)) {
addMUHeader(makeMUFile(acs.Source, fullPath, acs.Configs, true),
extLower);
} else if (cm->IsACLikeSourceExtension(extLower)) {
addMUSource(makeMUFile(acs.Source, fullPath, acs.Configs, true));
}
}
// Register rcc enabled files
if (this->Rcc.Enabled) {
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 = acs.Source->GetIsGenerated();
// RCC options
{
std::string const& opts =
acs.Source->GetSafeProperty(kw.AUTORCC_OPTIONS);
if (!opts.empty()) {
cmExpandList(opts, qrc.Options);
}
}
this->Rcc.Qrcs.push_back(std::move(qrc));
}
}
}
}
// 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.
this->GenTarget->ClearSourcesCache();
// For source files find additional headers and private headers
if (this->MocOrUicEnabled()) {
// Header search suffixes and extensions
static std::initializer_list<cm::string_view> const suffixes{ "", "_p" };
auto const& exts = cm->GetHeaderExtensions();
// Scan through sources
for (auto const& pair : this->AutogenTarget.Sources) {
MUFile const& muf = *pair.second;
if (muf.MocIt || muf.UicIt) {
// Search for the default header file and a private header
std::string const& srcFullPath = muf.SF->ResolveFullPath();
std::string const basePath = cmStrCat(
cmQtAutoGen::SubDirPrefix(srcFullPath),
cmSystemTools::GetFilenameWithoutLastExtension(srcFullPath));
for (auto const& suffix : suffixes) {
std::string const suffixedPath = cmStrCat(basePath, suffix);
for (auto const& ext : exts) {
std::string const fullPath = cmStrCat(suffixedPath, '.', ext);
auto constexpr locationKind = cmSourceFileLocationKind::Known;
cmSourceFile* sf =
this->Makefile->GetSource(fullPath, locationKind);
if (sf) {
// Check if we know about this header already
if (cm::contains(this->AutogenTarget.Headers, sf)) {
continue;
}
// We only accept not-GENERATED files that do exist.
if (!sf->GetIsGenerated() &&
!cmSystemTools::FileExists(fullPath)) {
continue;
}
} else if (cmSystemTools::FileExists(fullPath)) {
// Create a new source file for the existing file
sf = this->Makefile->CreateSource(fullPath, false, locationKind);
}
if (sf) {
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;
}
if (!muf.UicIt) {
eMuf->UicIt = false;
}
addMUHeader(std::move(eMuf), ext);
}
}
}
}
}
}
// Scan through all source files in the makefile to extract moc and uic
// parameters. Historically we support non target source file parameters.
// The reason is that their file names might be discovered from source files
// at generation time.
if (this->MocOrUicEnabled()) {
for (const auto& sf : this->Makefile->GetSourceFiles()) {
// 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;
}
std::string const& extLower =
cmSystemTools::LowerCase(sf->GetExtension());
if (cm->IsAHeaderExtension(extLower)) {
if (!cm::contains(this->AutogenTarget.Headers, sf.get())) {
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);
if (muf->SkipMoc || muf->SkipUic) {
addMUSource(std::move(muf));
}
}
} else if (this->Uic.Enabled && (extLower == kw.ui)) {
// .ui file
bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN);
bool const skipUic =
(skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC));
if (!skipUic) {
// Check if the .ui file has uic options
std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS);
if (uicOpts.empty()) {
this->Uic.UiFilesNoOptions.emplace_back(fullPath);
} else {
this->Uic.UiFilesWithOptions.emplace_back(
fullPath, std::move(cmList{ uicOpts }.data()));
}
auto uiHeaderRelativePath = cmSystemTools::RelativePath(
this->LocalGen->GetCurrentSourceDirectory(),
cmSystemTools::GetFilenamePath(fullPath));
// Avoid creating a path containing adjacent slashes
if (!uiHeaderRelativePath.empty() &&
uiHeaderRelativePath.back() != '/') {
uiHeaderRelativePath += '/';
}
auto uiHeaderFilePath = cmStrCat(
'/', uiHeaderRelativePath, "ui_"_s,
cmSystemTools::GetFilenameWithoutLastExtension(fullPath), ".h"_s);
ConfigString uiHeader;
std::string uiHeaderGenex;
this->ConfigFileNamesAndGenex(
uiHeader, uiHeaderGenex, cmStrCat(this->Dir.Build, "/include"_s),
uiHeaderFilePath);
this->Uic.UiHeaders.emplace_back(uiHeader, uiHeaderGenex);
} else {
// Register skipped .ui file
this->Uic.SkipUi.insert(fullPath);
}
}
}
}
// Process GENERATED sources and headers
if (this->MocOrUicEnabled() && !this->AutogenTarget.FilesGenerated.empty()) {
if (this->CMP0071Accept) {
// Let the autogen target depend on the GENERATED files
if (this->MultiConfig && !this->CrossConfig) {
for (MUFile const* muf : this->AutogenTarget.FilesGenerated) {
if (muf->Configs.empty()) {
this->AutogenTarget.DependFiles.insert(muf->FullPath);
} else {
for (size_t ci : muf->Configs) {
std::string const& config = this->ConfigsList[ci];
std::string const& pathWithConfig =
cmStrCat("$<$<CONFIG:", config, ">:", muf->FullPath, '>');
this->AutogenTarget.DependFiles.insert(pathWithConfig);
}
}
}
} else {
for (MUFile const* muf : this->AutogenTarget.FilesGenerated) {
this->AutogenTarget.DependFiles.insert(muf->FullPath);
}
}
} else if (this->CMP0071Warn) {
cm::string_view property;
if (this->Moc.Enabled && this->Uic.Enabled) {
property = "SKIP_AUTOGEN";
} else if (this->Moc.Enabled) {
property = "SKIP_AUTOMOC";
} else if (this->Uic.Enabled) {
property = "SKIP_AUTOUIC";
}
std::string files;
for (MUFile const* muf : this->AutogenTarget.FilesGenerated) {
files += cmStrCat(" ", Quoted(muf->FullPath), '\n');
}
this->Makefile->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat(
cmPolicies::GetPolicyWarning(cmPolicies::CMP0071), '\n',
"For compatibility, CMake is excluding the GENERATED source "
"file(s):\n",
files, "from processing by ",
cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false),
". If any of the files should be processed, set CMP0071 to NEW. "
"If any of the files should not be processed, "
"explicitly exclude them by setting the source file property ",
property, ":\n set_property(SOURCE file.h PROPERTY ", property,
" ON)\n"));
}
}
// Generate CMP0100 warning
if (this->MocOrUicEnabled() &&
!this->AutogenTarget.CMP0100HeadersWarn.empty()) {
cm::string_view property;
if (this->Moc.Enabled && this->Uic.Enabled) {
property = "SKIP_AUTOGEN";
} else if (this->Moc.Enabled) {
property = "SKIP_AUTOMOC";
} else if (this->Uic.Enabled) {
property = "SKIP_AUTOUIC";
}
std::string files;
for (cmSourceFile const* sf : this->AutogenTarget.CMP0100HeadersWarn) {
files += cmStrCat(" ", Quoted(sf->GetFullPath()), '\n');
}
this->Makefile->IssueMessage(
MessageType::AUTHOR_WARNING,
cmStrCat(
cmPolicies::GetPolicyWarning(cmPolicies::CMP0100), '\n',
"For compatibility, CMake is excluding the header file(s):\n", files,
"from processing by ",
cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false),
". If any of the files should be processed, set CMP0100 to NEW. "
"If any of the files should not be processed, "
"explicitly exclude them by setting the source file property ",
property, ":\n set_property(SOURCE file.hh PROPERTY ", property,
" ON)\n"));
}
// Process qrc files
if (!this->Rcc.Qrcs.empty()) {
const bool modernQt = (this->QtVersion.Major >= 5);
// Target rcc options
cmList const optionsTarget{ this->GenTarget->GetSafeProperty(
kw.AUTORCC_OPTIONS) };
// Check if file name is unique
for (Qrc& qrc : this->Rcc.Qrcs) {
qrc.Unique = true;
for (Qrc const& qrc2 : this->Rcc.Qrcs) {
if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
qrc.Unique = false;
break;
}
}
}
// Path checksum and file names
for (Qrc& qrc : this->Rcc.Qrcs) {
// Path checksum
qrc.QrcPathChecksum = this->PathCheckSum.getPart(qrc.QrcFile);
// Output file name
if (this->MultiConfig && !this->GlobalGen->IsXcode() &&
this->UseBetterGraph) {
qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
"_$<CONFIG>", "/qrc_", qrc.QrcName, ".cpp");
} else {
qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum,
"/qrc_", qrc.QrcName, ".cpp");
}
std::string const base = cmStrCat(this->Dir.Info, "/AutoRcc_",
qrc.QrcName, '_', qrc.QrcPathChecksum);
qrc.LockFile = cmStrCat(base, "_Lock.lock");
qrc.InfoFile = cmStrCat(base, "_Info.json");
this->ConfigFileNames(qrc.SettingsFile, cmStrCat(base, "_Used"), ".txt");
}
// rcc options
for (Qrc& qrc : this->Rcc.Qrcs) {
// Target options
std::vector<std::string> opts = optionsTarget;
// Merge computed "-name XYZ" option
{
std::string name = qrc.QrcName;
// Replace '-' with '_'. The former is not valid for symbol names.
std::replace(name.begin(), name.end(), '-', '_');
if (!qrc.Unique) {
name += cmStrCat('_', qrc.QrcPathChecksum);
}
std::vector<std::string> nameOpts;
nameOpts.emplace_back("-name");
nameOpts.emplace_back(std::move(name));
RccMergeOptions(opts, nameOpts, modernQt);
}
// Merge file option
RccMergeOptions(opts, qrc.Options, modernQt);
qrc.Options = std::move(opts);
}
// rcc resources
for (Qrc& qrc : this->Rcc.Qrcs) {
if (!qrc.Generated) {
std::string error;
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
RccLister const lister(
this->Rcc.Executable.Config[config],
this->Rcc.ExecutableFeatures.Config[config]->ListOptions);
if (!lister.list(qrc.QrcFile, qrc.Resources.Config[config],
error)) {
cmSystemTools::Error(error);
return false;
}
}
} else {
RccLister const lister(
this->Rcc.Executable.Default,
this->Rcc.ExecutableFeatures.Default->ListOptions);
if (!lister.list(qrc.QrcFile, qrc.Resources.Default, error)) {
cmSystemTools::Error(error);
return false;
}
}
}
}
}
return true;
}
bool cmQtAutoGenInitializer::InitAutogenTarget()
{
// Register info file as generated by CMake
this->Makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
// Determine whether to use a depfile for the AUTOGEN target.
bool const useDepfile = [this]() -> bool {
auto const& gen = this->GlobalGen->GetName();
return this->QtVersion >= IntegerVersion(5, 15) &&
(gen.find("Ninja") != std::string::npos ||
gen.find("Make") != std::string::npos);
}();
// Files provided by the autogen target
std::vector<std::string> autogenByproducts;
std::vector<std::string> timestampByproducts;
if (this->Moc.Enabled) {
this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true);
if (useDepfile) {
if (this->CrossConfig &&
this->GlobalGen->GetName().find("Ninja") != std::string::npos &&
!this->UseBetterGraph) {
// Make all mocs_compilation_<CONFIG>.cpp files byproducts of the
// ${target}_autogen/timestamp custom command.
// We cannot just use Moc.CompilationFileGenex here, because that
// custom command runs cmake_autogen for each configuration.
for (const auto& p : this->Moc.CompilationFile.Config) {
timestampByproducts.push_back(p.second);
}
} else {
timestampByproducts.push_back(this->Moc.CompilationFileGenex);
}
} else {
autogenByproducts.push_back(this->Moc.CompilationFileGenex);
}
}
if (this->Uic.Enabled) {
for (auto const& file : this->Uic.UiHeaders) {
this->AddGeneratedSource(file.first, this->Uic);
autogenByproducts.push_back(file.second);
}
}
// Compose target comment
std::string autogenComment;
{
std::string tools;
if (this->Moc.Enabled) {
tools += "MOC";
}
if (this->Uic.Enabled) {
if (!tools.empty()) {
tools += " and ";
}
tools += "UIC";
}
autogenComment = cmStrCat("Automatic ", tools, " for target ",
this->GenTarget->GetName());
}
// Compose command lines
// FIXME: Take advantage of our per-config mocs_compilation_$<CONFIG>.cpp
// instead of fiddling with the include directories
bool constexpr stdPipesUTF8 = true;
cmCustomCommandLines commandLines;
AddCMakeProcessToCommandLines(this->AutogenTarget.InfoFile, "cmake_autogen",
commandLines);
// Use PRE_BUILD on demand
bool usePRE_BUILD = false;
if (this->GlobalGen->GetName().find("Visual Studio") != std::string::npos) {
// Under VS use a PRE_BUILD event instead of a separate target to
// reduce the number of targets loaded into the IDE.
// This also works around a VS 11 bug that may skip updating the target:
// https://connect.microsoft.com/VisualStudio/feedback/details/769495
usePRE_BUILD = true;
}
// Disable PRE_BUILD in some cases
if (usePRE_BUILD) {
// Cannot use PRE_BUILD with file depends
if (!this->AutogenTarget.DependFiles.empty()) {
usePRE_BUILD = false;
}
// Cannot use PRE_BUILD when a global autogen target is in place
if (this->AutogenTarget.GlobalTarget) {
usePRE_BUILD = false;
}
}
// Create the autogen target/command
if (usePRE_BUILD) {
// Add additional autogen target dependencies to origin target
for (cmTarget const* depTarget : this->AutogenTarget.DependTargets) {
this->GenTarget->Target->AddUtility(depTarget->GetName(), false,
this->Makefile);
}
if (!this->Uic.UiFilesNoOptions.empty() ||
!this->Uic.UiFilesWithOptions.empty()) {
// Add a generated timestamp file
ConfigString timestampFile;
std::string timestampFileGenex;
ConfigFileNamesAndGenex(timestampFile, timestampFileGenex,
cmStrCat(this->Dir.Build, "/autouic"_s),
".stamp"_s);
this->AddGeneratedSource(timestampFile, this->Uic);
// Add a step in the pre-build command to touch the timestamp file
commandLines.push_back(
cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", "touch",
timestampFileGenex }));
// UIC needs to be re-run if any of the known UI files change or the
// executable itself has been updated
auto uicDependencies = this->Uic.UiFilesNoOptions;
for (auto const& uiFile : this->Uic.UiFilesWithOptions) {
uicDependencies.push_back(uiFile.first);
}
AddAutogenExecutableToDependencies(this->Uic, uicDependencies);
// Add a rule file to cause the target to build if a dependency has
// changed, which will trigger the pre-build command to run autogen
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetOutputs(timestampFileGenex);
cc->SetDepends(uicDependencies);
cc->SetComment("");
cc->SetWorkingDirectory(this->Dir.Work.c_str());
cc->SetEscapeOldStyle(false);
cc->SetStdPipesUTF8(stdPipesUTF8);
this->LocalGen->AddCustomCommandToOutput(std::move(cc));
}
// Add the pre-build command directly to bypass the OBJECT_LIBRARY
// rejection in cmMakefile::AddCustomCommandToTarget because we know
// PRE_BUILD will work for an OBJECT_LIBRARY in this specific case.
//
// PRE_BUILD does not support file dependencies!
cmCustomCommand cc;
cc.SetByproducts(autogenByproducts);
cc.SetCommandLines(commandLines);
cc.SetComment(autogenComment.c_str());
cc.SetBacktrace(this->Makefile->GetBacktrace());
cc.SetWorkingDirectory(this->Dir.Work.c_str());
cc.SetStdPipesUTF8(stdPipesUTF8);
cc.SetEscapeOldStyle(false);
cc.SetEscapeAllowMakeVars(true);
this->GenTarget->Target->AddPreBuildCommand(std::move(cc));
} else {
// Add link library target dependencies to the autogen target
// dependencies
if (this->AutogenTarget.DependOrigin) {
// add_dependencies/addUtility do not support generator expressions.
// We depend only on the libraries found in all configs therefore.
std::map<cmGeneratorTarget const*, std::size_t> commonTargets;
for (std::string const& config : this->ConfigsList) {
cmLinkImplementationLibraries const* libs =
this->GenTarget->GetLinkImplementationLibraries(
config, cmGeneratorTarget::UseTo::Link);
if (libs) {
for (cmLinkItem const& item : libs->Libraries) {
cmGeneratorTarget const* libTarget = item.Target;
if (libTarget &&
!StaticLibraryCycle(this->GenTarget, libTarget, config)) {
// Increment target config count
commonTargets[libTarget]++;
}
}
}
}
for (auto const& item : commonTargets) {
if (item.second == this->ConfigsList.size()) {
this->AutogenTarget.DependTargets.insert(item.first->Target);
}
}
}
cmTarget* timestampTarget = nullptr;
std::vector<std::string> dependencies(
this->AutogenTarget.DependFiles.begin(),
this->AutogenTarget.DependFiles.end());
if (useDepfile) {
// Create a custom command that generates a timestamp file and
// has a depfile assigned. The depfile is created by JobDepFilesMergeT.
//
// Also create an additional '_autogen_timestamp_deps' that the custom
// command will depend on. It will have no sources or commands to
// execute, but it will have dependencies that would originally be
// assigned to the pre-Qt 5.15 'autogen' target. These dependencies will
// serve as a list of order-only dependencies for the custom command,
// without forcing the custom command to re-execute.
//
// The dependency tree would then look like
// '_autogen_timestamp_deps (order-only)' <- '/timestamp' file <-
// '_autogen' target.
const auto timestampTargetName =
cmStrCat(this->GenTarget->GetName(), "_autogen_timestamp_deps");
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetWorkingDirectory(this->Dir.Work.c_str());
cc->SetDepends(dependencies);
cc->SetEscapeOldStyle(false);
timestampTarget = this->LocalGen->AddUtilityCommand(timestampTargetName,
true, std::move(cc));
this->LocalGen->AddGeneratorTarget(
cm::make_unique<cmGeneratorTarget>(timestampTarget, this->LocalGen));
// Set FOLDER property on the timestamp target, so it appears in the
// appropriate folder in an IDE or in the file api.
if (!this->TargetsFolder.empty()) {
timestampTarget->SetProperty("FOLDER", this->TargetsFolder);
}
// Make '/timestamp' file depend on '_autogen_timestamp_deps' and on the
// moc and uic executables (whichever are enabled).
dependencies.clear();
dependencies.push_back(timestampTargetName);
AddAutogenExecutableToDependencies(this->Moc, dependencies);
AddAutogenExecutableToDependencies(this->Uic, dependencies);
std::string outputFile;
std::string depFile;
// Create the custom command that outputs the timestamp file.
if (this->MultiConfig && this->UseBetterGraph) {
// create timestamp file with $<CONFIG> in the name so that
// every cmake_autogen target has its own timestamp file
std::string const configView = "$<CONFIG>";
std::string const timestampFileWithoutConfig = "timestamp_";
std::string const depFileWithoutConfig =
cmStrCat(this->Dir.Build, "/deps_");
std::string const timestampFileName =
timestampFileWithoutConfig + configView;
outputFile = cmStrCat(this->Dir.Build, "/", timestampFileName);
auto const depFileWithConfig =
cmStrCat(depFileWithoutConfig, configView);
depFile = depFileWithConfig;
commandLines.push_back(cmMakeCommandLine(
{ cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
ConfigString outputFileWithConfig;
for (std::string const& config : this->ConfigsList) {
auto tempTimestampFileName = timestampFileWithoutConfig + config;
auto tempDepFile = depFileWithoutConfig + config;
outputFileWithConfig.Config[config] = tempTimestampFileName;
this->AutogenTarget.DepFileRuleName.Config[config] =
cmStrCat(this->Dir.RelativeBuild, "/", tempTimestampFileName);
this->AutogenTarget.DepFile.Config[config] = tempDepFile;
}
this->AddGeneratedSource(outputFileWithConfig, this->Moc);
} else {
cm::string_view const timestampFileName = "timestamp";
outputFile = cmStrCat(this->Dir.Build, "/", timestampFileName);
this->AutogenTarget.DepFile.Default =
cmStrCat(this->Dir.Build, "/deps");
depFile = this->AutogenTarget.DepFile.Default;
this->AutogenTarget.DepFileRuleName.Default =
cmStrCat(this->Dir.RelativeBuild, "/", timestampFileName);
commandLines.push_back(cmMakeCommandLine(
{ cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile }));
this->AddGeneratedSource(outputFile, this->Moc);
}
cc = cm::make_unique<cmCustomCommand>();
cc->SetOutputs(outputFile);
cc->SetByproducts(timestampByproducts);
cc->SetDepends(dependencies);
cc->SetCommandLines(commandLines);
cc->SetComment(autogenComment.c_str());
cc->SetWorkingDirectory(this->Dir.Work.c_str());
cc->SetEscapeOldStyle(false);
cc->SetDepfile(depFile);
cc->SetStdPipesUTF8(stdPipesUTF8);
this->LocalGen->AddCustomCommandToOutput(std::move(cc));
dependencies.clear();
dependencies.emplace_back(std::move(outputFile));
commandLines.clear();
autogenComment.clear();
}
// Create autogen target
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetWorkingDirectory(this->Dir.Work.c_str());
cc->SetByproducts(autogenByproducts);
cc->SetDepends(dependencies);
cc->SetCommandLines(commandLines);
cc->SetEscapeOldStyle(false);
cc->SetComment(autogenComment.c_str());
cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand(
this->AutogenTarget.Name, true, std::move(cc));
// Create autogen generator target
this->LocalGen->AddGeneratorTarget(
cm::make_unique<cmGeneratorTarget>(autogenTarget, this->LocalGen));
// Order the autogen target(s) just before the original target.
cmTarget* orderTarget = timestampTarget ? timestampTarget : autogenTarget;
// Forward origin utilities to autogen target
if (this->AutogenTarget.DependOrigin) {
for (BT<std::pair<std::string, bool>> const& depName :
this->GenTarget->GetUtilities()) {
orderTarget->AddUtility(depName.Value.first, false, this->Makefile);
}
}
// Add additional autogen target dependencies to autogen target
for (cmTarget const* depTarget : this->AutogenTarget.DependTargets) {
orderTarget->AddUtility(depTarget->GetName(), false, this->Makefile);
}
// Set FOLDER property in autogen target
if (!this->TargetsFolder.empty()) {
autogenTarget->SetProperty("FOLDER", this->TargetsFolder);
}
// Add autogen target to the origin target dependencies
this->GenTarget->Target->AddUtility(this->AutogenTarget.Name, false,
this->Makefile);
// Add autogen target to the global autogen target dependencies
if (this->AutogenTarget.GlobalTarget) {
this->GlobalInitializer->AddToGlobalAutoGen(this->LocalGen,
this->AutogenTarget.Name);
}
}
return true;
}
void cmQtAutoGenInitializer::AddCMakeProcessToCommandLines(
std::string const& infoFile, std::string const& processName,
cmCustomCommandLines& commandLines)
{
std::vector<std::string> autogenConfigs;
this->GlobalGen->GetQtAutoGenConfigs(autogenConfigs);
if (this->CrossConfig && this->UseBetterGraph) {
commandLines.push_back(cmMakeCommandLine(
{ cmSystemTools::GetCMakeCommand(), "-E", processName, infoFile,
"$<CONFIG>", "$<COMMAND_CONFIG:$<CONFIG>>" }));
} else if ((this->MultiConfig && this->GlobalGen->IsXcode()) ||
this->CrossConfig) {
const auto& configs =
processName == "cmake_autorcc" ? this->ConfigsList : autogenConfigs;
for (std::string const& config : configs) {
commandLines.push_back(
cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
processName, infoFile, config }));
}
} else {
std::string autoInfoFileConfig;
if (this->MultiConfig) {
autoInfoFileConfig = "$<CONFIG>";
} else {
autoInfoFileConfig = autogenConfigs[0];
}
commandLines.push_back(
cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", processName,
infoFile, autoInfoFileConfig }));
}
}
bool cmQtAutoGenInitializer::InitRccTargets()
{
for (Qrc const& qrc : this->Rcc.Qrcs) {
// Register info file as generated by CMake
this->Makefile->AddCMakeOutputFile(qrc.InfoFile);
// Register file at target
{
cmSourceFile* sf = this->AddGeneratedSource(qrc.OutputFile, this->Rcc);
sf->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "On");
}
std::vector<std::string> ccOutput{ qrc.OutputFile };
// Add the .qrc and info file to the custom command dependencies
std::vector<std::string> ccDepends{ qrc.QrcFile, qrc.InfoFile };
cmCustomCommandLines commandLines;
AddCMakeProcessToCommandLines(qrc.InfoFile, "cmake_autorcc", commandLines);
std::string const ccComment =
cmStrCat("Automatic RCC for ",
FileProjectRelativePath(this->Makefile, qrc.QrcFile));
auto cc = cm::make_unique<cmCustomCommand>();
cc->SetWorkingDirectory(this->Dir.Work.c_str());
cc->SetCommandLines(commandLines);
cc->SetComment(ccComment.c_str());
cc->SetStdPipesUTF8(true);
if (qrc.Generated || this->Rcc.GlobalTarget) {
// Create custom rcc target
std::string ccName;
{
ccName = cmStrCat(this->GenTarget->GetName(), "_arcc_", qrc.QrcName);
if (!qrc.Unique) {
ccName += cmStrCat('_', qrc.QrcPathChecksum);
}
cc->SetByproducts(ccOutput);
cc->SetDepends(ccDepends);
cc->SetEscapeOldStyle(false);
cmTarget* autoRccTarget =
this->LocalGen->AddUtilityCommand(ccName, true, std::move(cc));
// Create autogen generator target
this->LocalGen->AddGeneratorTarget(
cm::make_unique<cmGeneratorTarget>(autoRccTarget, this->LocalGen));
// Set FOLDER property in autogen target
if (!this->TargetsFolder.empty()) {
autoRccTarget->SetProperty("FOLDER", this->TargetsFolder);
}
if (!this->Rcc.ExecutableTargetName.empty()) {
autoRccTarget->AddUtility(this->Rcc.ExecutableTargetName, false,
this->Makefile);
}
}
// Add autogen target to the origin target dependencies
this->GenTarget->Target->AddUtility(ccName, false, this->Makefile);
// Add autogen target to the global autogen target dependencies
if (this->Rcc.GlobalTarget) {
this->GlobalInitializer->AddToGlobalAutoRcc(this->LocalGen, ccName);
}
} else {
// Create custom rcc command
{
// Add the resource files to the dependencies
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
// Add resource file to the custom command dependencies
auto resourceFilesWithConfig = cmStrCat(
"$<$<CONFIG:", config,
">:", cmList{ qrc.Resources.Config.at(config) }.to_string(),
">");
ccDepends.emplace_back(std::move(resourceFilesWithConfig));
}
} else {
for (std::string const& fileName : qrc.Resources.Default) {
// Add resource file to the custom command dependencies
ccDepends.push_back(fileName);
}
}
if (!this->Rcc.ExecutableTargetName.empty()) {
ccDepends.push_back(this->Rcc.ExecutableTargetName);
}
AddAutogenExecutableToDependencies(this->Rcc, ccDepends);
cc->SetOutputs(ccOutput);
cc->SetDepends(ccDepends);
this->LocalGen->AddCustomCommandToOutput(std::move(cc));
}
// Reconfigure when .qrc file changes
this->Makefile->AddCMakeDependFile(qrc.QrcFile);
}
}
return true;
}
bool cmQtAutoGenInitializer::SetupCustomTargets()
{
// Create info directory on demand
if (!cmSystemTools::MakeDirectory(this->Dir.Info)) {
cmSystemTools::Error(cmStrCat("AutoGen: Could not create directory: ",
Quoted(this->Dir.Info)));
return false;
}
// Generate autogen target info file
if (this->MocOrUicEnabled()) {
// Write autogen target info files
if (!this->SetupWriteAutogenInfo()) {
return false;
}
}
// Write AUTORCC info files
return !this->Rcc.Enabled || this->SetupWriteRccInfo();
}
bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
{
// Utility lambdas
auto MfDef = [this](std::string const& key) {
return this->Makefile->GetSafeDefinition(key);
};
// Filtered headers and sources
std::set<std::string> moc_skip;
std::set<std::string> uic_skip;
std::vector<MUFile const*> headers;
std::vector<MUFile const*> sources;
// Filter headers
{
headers.reserve(this->AutogenTarget.Headers.size());
for (auto const& pair : this->AutogenTarget.Headers) {
MUFile const* const muf = pair.second.get();
if (muf->SkipMoc) {
moc_skip.insert(muf->FullPath);
}
if (muf->SkipUic) {
uic_skip.insert(muf->FullPath);
}
if (muf->Generated && !this->CMP0071Accept) {
continue;
}
if (muf->MocIt || muf->UicIt) {
headers.emplace_back(muf);
}
}
std::sort(headers.begin(), headers.end(),
[](MUFile const* a, MUFile const* b) {
return (a->FullPath < b->FullPath);
});
}
// Filter sources
{
sources.reserve(this->AutogenTarget.Sources.size());
for (auto const& pair : this->AutogenTarget.Sources) {
MUFile const* const muf = pair.second.get();
if (muf->Generated && !this->CMP0071Accept) {
continue;
}
if (muf->SkipMoc) {
moc_skip.insert(muf->FullPath);
}
if (muf->SkipUic) {
uic_skip.insert(muf->FullPath);
}
if (muf->MocIt || muf->UicIt) {
sources.emplace_back(muf);
}
}
std::sort(sources.begin(), sources.end(),
[](MUFile const* a, MUFile const* b) {
return (a->FullPath < b->FullPath);
});
}
// Info writer
InfoWriter info;
// General
info.SetBool("MULTI_CONFIG", this->MultiConfig);
info.SetBool("CROSS_CONFIG", this->CrossConfig);
info.SetBool("USE_BETTER_GRAPH", this->UseBetterGraph);
info.SetUInt("PARALLEL", this->AutogenTarget.Parallel);
#ifdef _WIN32
info.SetUInt("AUTOGEN_COMMAND_LINE_LENGTH_MAX",
this->AutogenTarget.MaxCommandLineLength);
#endif
info.SetUInt("VERBOSITY", this->Verbosity);
// Directories
info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR"));
info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR"));
info.Set("BUILD_DIR", this->Dir.Build);
info.SetConfig("INCLUDE_DIR", this->Dir.Include);
info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major);
info.SetUInt("QT_VERSION_MINOR", this->QtVersion.Minor);
info.SetConfig("QT_MOC_EXECUTABLE", this->Moc.Executable);
info.SetConfig("QT_UIC_EXECUTABLE", this->Uic.Executable);
info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand());
info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile);
info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile);
info.SetConfig("DEP_FILE", this->AutogenTarget.DepFile);
info.SetConfig("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName);
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] = this->GetMocBuildPath(*muf);
jval[3u] = cfgArray(muf->Configs);
});
info.SetArrayArray(
"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] = cfgArray(muf->Configs);
});
// Write moc settings
if (this->Moc.Enabled) {
info.SetArray("MOC_SKIP", moc_skip);
info.SetConfigArray("MOC_DEFINITIONS", this->Moc.Defines);
info.SetConfigArray("MOC_INCLUDES", this->Moc.Includes);
info.SetArray("MOC_OPTIONS", this->Moc.Options);
info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode);
info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix);
EvaluatedTargetPropertyEntries InterfaceAutoMocMacroNamesEntries;
if (this->MultiConfig) {
for (auto const& cfg : this->ConfigsList) {
if (!cfg.empty()) {
cmGeneratorExpressionDAGChecker dagChecker(
this->GenTarget, "AUTOMOC_MACRO_NAMES", nullptr, nullptr,
this->LocalGen, cfg);
AddInterfaceEntries(this->GenTarget, cfg,
"INTERFACE_AUTOMOC_MACRO_NAMES", "CXX",
&dagChecker, InterfaceAutoMocMacroNamesEntries,
IncludeRuntimeInterface::Yes);
}
}
} else {
cmGeneratorExpressionDAGChecker dagChecker(
this->GenTarget, "AUTOMOC_MACRO_NAMES", nullptr, nullptr,
this->LocalGen, this->ConfigDefault);
AddInterfaceEntries(this->GenTarget, this->ConfigDefault,
"INTERFACE_AUTOMOC_MACRO_NAMES", "CXX", &dagChecker,
InterfaceAutoMocMacroNamesEntries,
IncludeRuntimeInterface::Yes);
}
for (auto const& entry : InterfaceAutoMocMacroNamesEntries.Entries) {
this->Moc.MacroNames.insert(this->Moc.MacroNames.end(),
entry.Values.begin(), entry.Values.end());
}
this->Moc.MacroNames.erase(cmRemoveDuplicates(this->Moc.MacroNames),
this->Moc.MacroNames.end());
info.SetArray("MOC_MACRO_NAMES", this->Moc.MacroNames);
info.SetArrayArray(
"MOC_DEPEND_FILTERS", this->Moc.DependFilters,
[](Json::Value& jval, std::pair<std::string, std::string> const& pair) {
jval.resize(2u);
jval[0u] = pair.first;
jval[1u] = pair.second;
});
info.SetConfig("MOC_COMPILATION_FILE", this->Moc.CompilationFile);
info.SetConfig("MOC_PREDEFS_FILE", this->Moc.PredefsFile);
cmStandardLevelResolver const resolver{ this->Makefile };
auto const CompileOptionFlag =
resolver.GetCompileOptionDef(this->GenTarget, "CXX", "");
auto const CompileOptionValue =
this->GenTarget->Makefile->GetSafeDefinition(CompileOptionFlag);
if (!CompileOptionValue.empty()) {
if (this->Moc.PredefsCmd.size() >= 3) {
this->Moc.PredefsCmd.insert(this->Moc.PredefsCmd.begin() + 1,
CompileOptionValue);
}
}
info.SetArray("MOC_PREDEFS_CMD", this->Moc.PredefsCmd);
}
// Write uic settings
if (this->Uic.Enabled) {
// Add skipped .ui files
uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end());
info.SetArray("UIC_SKIP", uic_skip);
info.SetArrayArray("UIC_UI_FILES", this->Uic.UiFilesWithOptions,
[](Json::Value& jval, UicT::UiFileT const& uiFile) {
jval.resize(2u);
jval[0u] = uiFile.first;
InfoWriter::MakeStringArray(jval[1u], uiFile.second);
});
info.SetConfigArray("UIC_OPTIONS", this->Uic.Options);
info.SetArray("UIC_SEARCH_PATHS", this->Uic.SearchPaths);
}
info.Save(this->AutogenTarget.InfoFile);
return true;
}
bool cmQtAutoGenInitializer::SetupWriteRccInfo()
{
for (Qrc const& qrc : this->Rcc.Qrcs) {
// Utility lambdas
auto MfDef = [this](std::string const& key) {
return this->Makefile->GetSafeDefinition(key);
};
InfoWriter info;
// General
info.SetBool("MULTI_CONFIG", this->MultiConfig);
info.SetBool("CROSS_CONFIG", this->CrossConfig);
info.SetBool("USE_BETTER_GRAPH", this->UseBetterGraph);
info.SetUInt("VERBOSITY", this->Verbosity);
info.Set("GENERATOR", this->GlobalGen->GetName());
// Files
info.Set("LOCK_FILE", qrc.LockFile);
info.SetConfig("SETTINGS_FILE", qrc.SettingsFile);
// Directories
info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR"));
info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR"));
info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR"));
info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR"));
info.Set("BUILD_DIR", this->Dir.Build);
info.SetConfig("INCLUDE_DIR", this->Dir.Include);
// rcc executable
info.SetConfig("RCC_EXECUTABLE", this->Rcc.Executable);
info.SetConfigArray(
"RCC_LIST_OPTIONS",
generateListOptions(this->Rcc.ExecutableFeatures, this->MultiConfig));
// qrc file
info.Set("SOURCE", qrc.QrcFile);
info.Set("OUTPUT_CHECKSUM", qrc.QrcPathChecksum);
info.Set("OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.OutputFile));
info.SetArray("OPTIONS", qrc.Options);
info.SetConfigArray("INPUTS", qrc.Resources);
info.Save(qrc.InfoFile);
}
return true;
}
cmSourceFile* cmQtAutoGenInitializer::RegisterGeneratedSource(
std::string const& filename)
{
cmSourceFile* gFile = this->Makefile->GetOrCreateSource(filename, true);
gFile->MarkAsGenerated();
gFile->SetProperty("SKIP_AUTOGEN", "1");
gFile->SetProperty("SKIP_LINTING", "ON");
gFile->SetProperty("CXX_SCAN_FOR_MODULES", "0");
return gFile;
}
cmSourceFile* cmQtAutoGenInitializer::AddGeneratedSource(
std::string const& filename, GenVarsT const& genVars, bool prepend)
{
// Register source at makefile
cmSourceFile* gFile = this->RegisterGeneratedSource(filename);
// Add source file to target
this->GenTarget->AddSource(filename, prepend);
// Add source file to source group
this->AddToSourceGroup(filename, genVars.GenNameUpper);
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()) {
cmSourceFile* sf =
this->AddGeneratedSource(filename.Default, genVars, prepend);
handleSkipPch(sf);
return;
}
for (auto const& cfg : this->ConfigsList) {
std::string const& filenameCfg = filename.Config.at(cfg);
// Register source at makefile
cmSourceFile* sf = this->RegisterGeneratedSource(filenameCfg);
handleSkipPch(sf);
// 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)
{
cmSourceGroup* sourceGroup = nullptr;
// Acquire source group
{
std::string property;
std::string groupName;
{
// Prefer generator specific source group name
std::initializer_list<std::string> const props{
cmStrCat(genNameUpper, "_SOURCE_GROUP"), "AUTOGEN_SOURCE_GROUP"
};
for (std::string const& prop : props) {
cmValue propName = this->Makefile->GetState()->GetGlobalProperty(prop);
if (cmNonempty(propName)) {
groupName = *propName;
property = prop;
break;
}
}
}
// Generate a source group on demand
if (!groupName.empty()) {
sourceGroup = this->Makefile->GetOrCreateSourceGroup(groupName);
if (!sourceGroup) {
cmSystemTools::Error(
cmStrCat(genNameUpper, " error in ", property,
": Could not find or create the source group ",
cmQtAutoGen::Quoted(groupName)));
}
}
}
if (sourceGroup) {
sourceGroup->AddGroupFile(fileName);
}
}
void cmQtAutoGenInitializer::AddCleanFile(std::string const& fileName)
{
this->GenTarget->Target->AppendProperty("ADDITIONAL_CLEAN_FILES", fileName);
}
void cmQtAutoGenInitializer::ConfigFileNames(ConfigString& configString,
cm::string_view prefix,
cm::string_view suffix)
{
configString.Default = cmStrCat(prefix, suffix);
if (this->MultiConfig) {
for (auto const& cfg : this->ConfigsList) {
configString.Config[cfg] = cmStrCat(prefix, '_', cfg, suffix);
}
}
}
void cmQtAutoGenInitializer::ConfigFileNamesAndGenex(
ConfigString& configString, std::string& genex, cm::string_view const prefix,
cm::string_view const suffix)
{
this->ConfigFileNames(configString, prefix, suffix);
if (this->MultiConfig) {
genex = cmStrCat(prefix, "_$<CONFIG>"_s, suffix);
} else {
genex = configString.Default;
}
}
void cmQtAutoGenInitializer::ConfigFileClean(ConfigString& configString)
{
this->AddCleanFile(configString.Default);
if (this->MultiConfig) {
for (auto const& pair : configString.Config) {
this->AddCleanFile(pair.second);
}
}
}
static cmQtAutoGen::IntegerVersion parseMocVersion(std::string str)
{
cmQtAutoGen::IntegerVersion result;
static const std::string prelude = "moc ";
size_t const pos = str.find(prelude);
if (pos == std::string::npos) {
return result;
}
str.erase(0, prelude.size() + pos);
std::istringstream iss(str);
std::string major;
std::string minor;
if (!std::getline(iss, major, '.') || !std::getline(iss, minor, '.')) {
return result;
}
result.Major = static_cast<unsigned int>(std::stoi(major));
result.Minor = static_cast<unsigned int>(std::stoi(minor));
return result;
}
static cmQtAutoGen::IntegerVersion GetMocVersion(
const std::string& mocExecutablePath)
{
std::string capturedStdOut;
int exitCode;
if (!cmSystemTools::RunSingleCommand({ mocExecutablePath, "--version" },
&capturedStdOut, nullptr, &exitCode,
nullptr, cmSystemTools::OUTPUT_NONE)) {
return {};
}
if (exitCode != 0) {
return {};
}
return parseMocVersion(capturedStdOut);
}
static std::string FindMocExecutableFromMocTarget(cmMakefile const* makefile,
unsigned int qtMajorVersion)
{
std::string result;
const std::string mocTargetName =
"Qt" + std::to_string(qtMajorVersion) + "::moc";
cmTarget const* mocTarget = makefile->FindTargetToUse(mocTargetName);
if (mocTarget) {
result = mocTarget->GetSafeProperty("IMPORTED_LOCATION");
}
return result;
}
std::pair<cmQtAutoGen::IntegerVersion, unsigned int>
cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target,
std::string mocExecutable)
{
// Converts a char ptr to an unsigned int value
auto toUInt = [](const char* const input) -> unsigned int {
unsigned long tmp = 0;
if (input && cmStrToULong(input, &tmp)) {
return static_cast<unsigned int>(tmp);
}
return 0u;
};
auto toUInt2 = [](cmValue input) -> unsigned int {
unsigned long tmp = 0;
if (input && cmStrToULong(*input, &tmp)) {
return static_cast<unsigned int>(tmp);
}
return 0u;
};
// Initialize return value to a default
std::pair<IntegerVersion, unsigned int> res(
IntegerVersion(),
toUInt(target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION",
"")));
// Acquire known Qt versions
std::vector<cmQtAutoGen::IntegerVersion> knownQtVersions;
{
// Qt version variable prefixes
static std::initializer_list<
std::pair<cm::string_view, cm::string_view>> const keys{
{ "Qt6Core_VERSION_MAJOR", "Qt6Core_VERSION_MINOR" },
{ "Qt5Core_VERSION_MAJOR", "Qt5Core_VERSION_MINOR" },
{ "QT_VERSION_MAJOR", "QT_VERSION_MINOR" },
};
knownQtVersions.reserve(keys.size() * 2);
// Adds a version to the result (nullptr safe)
auto addVersion = [&knownQtVersions, &toUInt2](cmValue major,
cmValue minor) {
cmQtAutoGen::IntegerVersion ver(toUInt2(major), toUInt2(minor));
if (ver.Major != 0) {
knownQtVersions.emplace_back(ver);
}
};
// Read versions from variables
for (auto const& keyPair : keys) {
addVersion(target->Makefile->GetDefinition(std::string(keyPair.first)),
target->Makefile->GetDefinition(std::string(keyPair.second)));
}
// Read versions from directory properties
for (auto const& keyPair : keys) {
addVersion(target->Makefile->GetProperty(std::string(keyPair.first)),
target->Makefile->GetProperty(std::string(keyPair.second)));
}
}
// Evaluate known Qt versions
if (!knownQtVersions.empty()) {
if (res.second == 0) {
// No specific version was requested by the target:
// Use highest known Qt version.
res.first = knownQtVersions.at(0);
} else {
// Pick a version from the known versions:
for (auto const& it : knownQtVersions) {
if (it.Major == res.second) {
res.first = it;
break;
}
}
}
}
if (res.first.Major == 0) {
// We could not get the version number from variables or directory
// properties. This might happen if the find_package call for Qt is wrapped
// in a function. Try to find the moc executable path from the available
// targets and call "moc --version" to get the Qt version.
if (mocExecutable.empty()) {
mocExecutable =
FindMocExecutableFromMocTarget(target->Makefile, res.second);
}
if (!mocExecutable.empty()) {
res.first = GetMocVersion(mocExecutable);
}
}
return res;
}
std::string cmQtAutoGenInitializer::GetMocBuildPath(MUFile const& muf)
{
std::string res;
if (!muf.MocIt) {
return res;
}
std::string basePath =
cmStrCat(this->PathCheckSum.getPart(muf.FullPath), "/moc_",
FileNameWithoutLastExtension(muf.FullPath));
res = cmStrCat(basePath, ".cpp");
if (this->Moc.EmittedBuildPaths.emplace(res).second) {
return res;
}
// File name already emitted.
// Try appending the header suffix to the base path.
basePath = cmStrCat(basePath, '_', muf.SF->GetExtension());
res = cmStrCat(basePath, ".cpp");
if (this->Moc.EmittedBuildPaths.emplace(res).second) {
return res;
}
// File name with header extension already emitted.
// Try adding a number to the base path.
constexpr std::size_t number_begin = 2;
constexpr std::size_t number_end = 256;
for (std::size_t ii = number_begin; ii != number_end; ++ii) {
res = cmStrCat(basePath, '_', ii, ".cpp");
if (this->Moc.EmittedBuildPaths.emplace(res).second) {
return res;
}
}
// Output file name conflict (unlikely, but still...)
cmSystemTools::Error(
cmStrCat("moc output file name conflict for ", muf.FullPath));
return res;
}
bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars,
const std::string& executable,
bool ignoreMissingTarget) const
{
auto print_err = [this, &genVars](std::string const& err) {
cmSystemTools::Error(cmStrCat(genVars.GenNameUpper, " for target ",
this->GenTarget->GetName(), ": ", err));
};
// Custom executable
{
std::string const prop = cmStrCat(genVars.GenNameUpper, "_EXECUTABLE");
std::string const& val = this->GenTarget->Target->GetSafeProperty(prop);
if (!val.empty()) {
// Evaluate generator expression
{
cmListFileBacktrace lfbt = this->Makefile->GetBacktrace();
cmGeneratorExpression ge(*this->Makefile->GetCMakeInstance(), lfbt);
std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(val);
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
genVars.Executable.Config[config] =
cge->Evaluate(this->LocalGen, config);
}
} else {
genVars.Executable.Default = cge->Evaluate(this->LocalGen, "");
}
}
if (genVars.Executable.Default.empty() &&
genVars.Executable.Config.empty() && !ignoreMissingTarget) {
print_err(prop + " evaluates to an empty value");
return false;
}
// Create empty compiler features.
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
genVars.ExecutableFeatures.Config[config] =
std::make_shared<cmQtAutoGen::CompilerFeatures>();
}
} else {
genVars.ExecutableFeatures.Default =
std::make_shared<cmQtAutoGen::CompilerFeatures>();
}
return true;
}
}
// Find executable target
{
// Find executable target name
cm::string_view prefix;
if (this->QtVersion.Major == 4) {
prefix = "Qt4::";
} else if (this->QtVersion.Major == 5) {
prefix = "Qt5::";
} else if (this->QtVersion.Major == 6) {
prefix = "Qt6::";
}
std::string const targetName = cmStrCat(prefix, executable);
// Find target
cmGeneratorTarget* genTarget =
this->LocalGen->FindGeneratorTargetToUse(targetName);
if (genTarget) {
genVars.ExecutableTargetName = targetName;
genVars.ExecutableTarget = genTarget;
if (genTarget->IsImported()) {
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
genVars.Executable.Config[config] =
genTarget->ImportedGetLocation(config);
}
} else {
genVars.Executable.Default =
genTarget->ImportedGetLocation(this->ConfigDefault);
}
} else {
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
genVars.Executable.Config[config] = genTarget->GetLocation(config);
}
} else {
genVars.Executable.Default =
genTarget->GetLocation(this->ConfigDefault);
}
}
} else {
if (ignoreMissingTarget) {
// Create empty compiler features.
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
genVars.ExecutableFeatures.Config[config] =
std::make_shared<cmQtAutoGen::CompilerFeatures>();
}
} else {
genVars.ExecutableFeatures.Default =
std::make_shared<cmQtAutoGen::CompilerFeatures>();
}
return true;
}
print_err(cmStrCat("Could not find ", executable, " executable target ",
targetName));
return false;
}
}
// Get executable features
{
std::string err;
genVars.ExecutableFeatures = this->GlobalInitializer->GetCompilerFeatures(
executable, genVars.Executable, err, this->MultiConfig,
this->UseBetterGraph);
if (this->MultiConfig && this->UseBetterGraph) {
for (auto const& config : this->ConfigsList) {
if (!genVars.ExecutableFeatures.Config[config]) {
if (!genVars.ExecutableFeatures.Config[config]) {
print_err(err);
return false;
}
}
}
} else {
if (!genVars.ExecutableFeatures.Default) {
print_err(err);
return false;
}
}
}
return true;
}
void cmQtAutoGenInitializer::handleSkipPch(cmSourceFile* sf)
{
bool skipPch = true;
for (auto const& pair : this->AutogenTarget.Sources) {
if (!pair.first->GetIsGenerated() &&
!pair.first->GetProperty("SKIP_PRECOMPILE_HEADERS")) {
skipPch = false;
}
}
if (skipPch) {
sf->SetProperty("SKIP_PRECOMPILE_HEADERS", "ON");
}
}