mirror of
https://github.com/reactos/CMake.git
synced 2024-11-28 05:50:42 +00:00
6a5a23ea92
086d9b2bab
Autogen: Enable SKIP_UNITY_BUILD_INCLUSION on AUTORCC generated files
Acked-by: Kitware Robot <kwrobot@kitware.com>
Acked-by: Cristian Adam <cristian.adam@gmail.com>
Merge-request: !4237
1807 lines
58 KiB
C++
1807 lines
58 KiB
C++
/* 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 <cstddef>
|
|
#include <deque>
|
|
#include <initializer_list>
|
|
#include <map>
|
|
#include <ostream>
|
|
#include <set>
|
|
#include <string>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <cm/algorithm>
|
|
#include <cm/iterator>
|
|
#include <cm/memory>
|
|
|
|
#include "cmsys/SystemInformation.hxx"
|
|
|
|
#include "cm_jsoncpp_value.h"
|
|
#include "cm_jsoncpp_writer.h"
|
|
|
|
#include "cmAlgorithms.h"
|
|
#include "cmCustomCommand.h"
|
|
#include "cmCustomCommandLines.h"
|
|
#include "cmGeneratedFileStream.h"
|
|
#include "cmGeneratorExpression.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmLinkItem.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 "cmState.h"
|
|
#include "cmStateTypes.h"
|
|
#include "cmString.hxx"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmTarget.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* 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);
|
|
if (libs != nullptr) {
|
|
for (cmLinkItem const& item : libs->Libraries) {
|
|
cmGeneratorTarget const* depTarget = item.Target;
|
|
if ((depTarget != nullptr) &&
|
|
(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, 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)
|
|
{
|
|
Value_[key] = value;
|
|
}
|
|
void SetConfig(std::string const& key,
|
|
cmQtAutoGenInitializer::ConfigString const& cfgStr);
|
|
void SetBool(std::string const& key, bool value) { Value_[key] = value; }
|
|
void SetUInt(std::string const& key, unsigned int value)
|
|
{
|
|
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)
|
|
{
|
|
Set(key, cfgStr.Default);
|
|
for (auto const& item : cfgStr.Config) {
|
|
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(Value_[key], container);
|
|
}
|
|
|
|
template <typename CONT, typename FUNC>
|
|
void InfoWriter::SetArrayArray(std::string const& key, CONT const& container,
|
|
FUNC func)
|
|
{
|
|
Json::Value& jval = 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)
|
|
{
|
|
SetArray(key, cfgStr.Default);
|
|
for (auto const& item : cfgStr.Config) {
|
|
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, Value_);
|
|
} catch (...) {
|
|
return false;
|
|
}
|
|
|
|
return fileStream.Close();
|
|
}
|
|
|
|
} // 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)
|
|
{
|
|
AutogenTarget.GlobalTarget = globalAutogenTarget;
|
|
Moc.Enabled = mocEnabled;
|
|
Uic.Enabled = uicEnabled;
|
|
Rcc.Enabled = rccEnabled;
|
|
Rcc.GlobalTarget = globalAutoRccTarget;
|
|
}
|
|
|
|
bool cmQtAutoGenInitializer::InitCustomTargets()
|
|
{
|
|
// Configurations
|
|
this->MultiConfig = this->GlobalGen->IsMultiConfig();
|
|
this->ConfigDefault = this->Makefile->GetConfigurations(this->ConfigsList);
|
|
if (this->ConfigsList.empty()) {
|
|
this->ConfigsList.push_back(this->ConfigDefault);
|
|
}
|
|
|
|
// Verbosity
|
|
{
|
|
std::string 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
|
|
{
|
|
const char* folder =
|
|
this->Makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
|
|
if (folder == nullptr) {
|
|
folder = this->Makefile->GetState()->GetGlobalProperty(
|
|
"AUTOGEN_TARGETS_FOLDER");
|
|
}
|
|
// Inherit FOLDER property from target (#13688)
|
|
if (folder == nullptr) {
|
|
folder = this->GenTarget->GetProperty("FOLDER");
|
|
}
|
|
if (folder != nullptr) {
|
|
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
|
|
{
|
|
// 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);
|
|
// Cleanup build directory
|
|
this->AddCleanFile(this->Dir.Build);
|
|
|
|
// Working directory
|
|
this->Dir.Work = cbd;
|
|
cmSystemTools::ConvertToUnixSlashes(this->Dir.Work);
|
|
|
|
// Include directory
|
|
ConfigFileNames(this->Dir.Include, cmStrCat(this->Dir.Build, "/include"),
|
|
"");
|
|
this->Dir.IncludeGenExp = this->Dir.Include.Default;
|
|
if (this->MultiConfig) {
|
|
this->Dir.IncludeGenExp += "_$<CONFIG>";
|
|
}
|
|
}
|
|
|
|
// Moc, Uic and _autogen target settings
|
|
if (this->MocOrUicEnabled()) {
|
|
// Init moc specific settings
|
|
if (this->Moc.Enabled && !InitMoc()) {
|
|
return false;
|
|
}
|
|
|
|
// Init uic specific settings
|
|
if (this->Uic.Enabled && !InitUic()) {
|
|
return false;
|
|
}
|
|
|
|
// Autogen target name
|
|
this->AutogenTarget.Name =
|
|
cmStrCat(this->GenTarget->GetName(), "_autogen");
|
|
|
|
// Autogen target parallel processing
|
|
{
|
|
std::string prop = this->GenTarget->GetSafeProperty("AUTOGEN_PARALLEL");
|
|
if (prop.empty() || (prop == "AUTO")) {
|
|
// Autodetect number of CPUs
|
|
this->AutogenTarget.Parallel = GetParallelCPUCount();
|
|
} else {
|
|
this->AutogenTarget.Parallel = 1;
|
|
}
|
|
}
|
|
|
|
// Autogen target info and settings files
|
|
{
|
|
// Info file
|
|
this->AutogenTarget.InfoFile =
|
|
cmStrCat(this->Dir.Info, "/AutogenInfo.json");
|
|
|
|
// Used settings file
|
|
ConfigFileNames(this->AutogenTarget.SettingsFile,
|
|
cmStrCat(this->Dir.Info, "/AutogenUsed"), ".txt");
|
|
ConfigFileClean(this->AutogenTarget.SettingsFile);
|
|
|
|
// Parse cache file
|
|
ConfigFileNames(this->AutogenTarget.ParseCacheFile,
|
|
cmStrCat(this->Dir.Info, "/ParseCache"), ".txt");
|
|
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 (std::string const& depName : cmExpandedList(deps)) {
|
|
// Allow target and file dependencies
|
|
auto* depTarget = this->Makefile->FindTargetToUse(depName);
|
|
if (depTarget != nullptr) {
|
|
this->AutogenTarget.DependTargets.insert(depTarget);
|
|
} else {
|
|
this->AutogenTarget.DependFiles.insert(depName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this->Moc.Enabled) {
|
|
// Path prefix
|
|
if (cmIsOn(this->GenTarget->GetSafeProperty("AUTOMOC_PATH_PREFIX"))) {
|
|
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);
|
|
{
|
|
auto filterList = cmExpandedList(
|
|
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 (std::size_t 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 && !InitRcc()) {
|
|
return false;
|
|
}
|
|
|
|
// Add autogen include directory to the origin target INCLUDE_DIRECTORIES
|
|
if (this->MocOrUicEnabled() || (this->Rcc.Enabled && this->MultiConfig)) {
|
|
this->GenTarget->AddIncludeDirectory(this->Dir.IncludeGenExp, true);
|
|
}
|
|
|
|
// 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
|
|
this->Moc.CompilationFile =
|
|
cmStrCat(this->Dir.Build, "/mocs_compilation.cpp");
|
|
|
|
// Moc predefs
|
|
if (this->GenTarget->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
|
|
(this->QtVersion >= IntegerVersion(5, 8))) {
|
|
// Command
|
|
cmExpandList(this->Makefile->GetSafeDefinition(
|
|
"CMAKE_CXX_COMPILER_PREDEFINES_COMMAND"),
|
|
this->Moc.PredefsCmd);
|
|
// Header
|
|
if (!this->Moc.PredefsCmd.empty()) {
|
|
ConfigFileNames(this->Moc.PredefsFile,
|
|
cmStrCat(this->Dir.Build, "/moc_predefs"), ".h");
|
|
}
|
|
}
|
|
|
|
// Moc includes
|
|
{
|
|
SearchPathSanitizer 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);
|
|
};
|
|
|
|
// Default configuration include directories
|
|
this->Moc.Includes.Default = getDirs(this->ConfigDefault);
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
#ifdef _WIN32
|
|
if (this->Moc.PredefsCmd.empty()) {
|
|
// Add WIN32 definition if we don't have a moc_predefs.h
|
|
defines.insert("WIN32");
|
|
}
|
|
#endif
|
|
return defines;
|
|
};
|
|
|
|
// Default configuration defines
|
|
this->Moc.Defines.Default = getDefs(this->ConfigDefault);
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Moc executable
|
|
{
|
|
if (!this->GetQtExecutable(this->Moc, "moc", false)) {
|
|
return false;
|
|
}
|
|
// Let the _autogen target depend on the moc executable
|
|
if (this->Moc.ExecutableTarget != nullptr) {
|
|
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)(cmExpandedList(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 != nullptr) {
|
|
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
|
|
CompilerFeatures& features = *this->Rcc.ExecutableFeatures;
|
|
if (!features.Evaluated) {
|
|
// Look for list options
|
|
if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) {
|
|
if (features.HelpOutput.find("--list") != std::string::npos) {
|
|
features.ListOptions.emplace_back("--list");
|
|
} else if (features.HelpOutput.find("-list") != std::string::npos) {
|
|
features.ListOptions.emplace_back("-list");
|
|
}
|
|
}
|
|
// Evaluation finished
|
|
features.Evaluated = true;
|
|
}
|
|
}
|
|
|
|
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,
|
|
bool muIt) -> MUFileHandle {
|
|
MUFileHandle muf = cm::make_unique<MUFile>();
|
|
muf->FullPath = fullPath;
|
|
muf->SF = sf;
|
|
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
|
|
std::vector<cmSourceFile*> srcFiles;
|
|
this->GenTarget->GetConfigCommonSourceFiles(srcFiles);
|
|
for (cmSourceFile* sf : srcFiles) {
|
|
// 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());
|
|
|
|
// Register files that will be scanned by moc or uic
|
|
if (this->MocOrUicEnabled()) {
|
|
if (cm->IsHeaderExtension(extLower)) {
|
|
addMUHeader(makeMUFile(sf, fullPath, true), extLower);
|
|
} else if (cm->IsSourceExtension(extLower)) {
|
|
addMUSource(makeMUFile(sf, fullPath, true));
|
|
}
|
|
}
|
|
|
|
// Register rcc enabled files
|
|
if (this->Rcc.Enabled) {
|
|
if ((extLower == kw.qrc) && !sf->GetPropertyAsBool(kw.SKIP_AUTOGEN) &&
|
|
!sf->GetPropertyAsBool(kw.SKIP_AUTORCC)) {
|
|
// Register qrc file
|
|
Qrc qrc;
|
|
qrc.QrcFile = fullPath;
|
|
qrc.QrcName =
|
|
cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
|
|
qrc.Generated = sf->GetIsGenerated();
|
|
// RCC options
|
|
{
|
|
std::string const opts = sf->GetSafeProperty(kw.AUTORCC_OPTIONS);
|
|
if (!opts.empty()) {
|
|
cmExpandList(opts, qrc.Options);
|
|
}
|
|
}
|
|
this->Rcc.Qrcs.push_back(std::move(qrc));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// cmGeneratorTarget::GetConfigCommonSourceFiles 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 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 fullPath = cmStrCat(suffixedPath, '.', ext);
|
|
|
|
auto constexpr locationKind = cmSourceFileLocationKind::Known;
|
|
cmSourceFile* sf =
|
|
this->Makefile->GetSource(fullPath, locationKind);
|
|
if (sf != nullptr) {
|
|
// Check if we know about this header already
|
|
if (cmContains(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 != nullptr) {
|
|
auto eMuf = makeMUFile(sf, fullPath, true);
|
|
// Ony 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->IsHeaderExtension(extLower)) {
|
|
if (!cmContains(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->IsSourceExtension(extLower)) {
|
|
if (!cmContains(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.UiFiles.emplace_back(fullPath, cmExpandedList(uicOpts));
|
|
}
|
|
} 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
|
|
for (MUFile* 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* 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* 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
|
|
std::vector<std::string> optionsTarget =
|
|
cmExpandedList(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
|
|
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");
|
|
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;
|
|
RccLister const lister(this->Rcc.Executable,
|
|
this->Rcc.ExecutableFeatures->ListOptions);
|
|
if (!lister.list(qrc.QrcFile, qrc.Resources, error)) {
|
|
cmSystemTools::Error(error);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool cmQtAutoGenInitializer::InitAutogenTarget()
|
|
{
|
|
// Register info file as generated by CMake
|
|
this->Makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile);
|
|
|
|
// Files provided by the autogen target
|
|
std::vector<std::string> autogenProvides;
|
|
if (this->Moc.Enabled) {
|
|
this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true);
|
|
autogenProvides.push_back(this->Moc.CompilationFile);
|
|
}
|
|
|
|
// 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
|
|
// TODO: Refactor autogen to output a per-config mocs_compilation.cpp instead
|
|
// of fiddling with the include directories
|
|
std::vector<std::string> configs;
|
|
this->GlobalGen->GetQtAutoGenConfigs(configs);
|
|
cmCustomCommandLines commandLines;
|
|
for (auto const& config : configs) {
|
|
commandLines.push_back(cmMakeCommandLine(
|
|
{ cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen",
|
|
this->AutogenTarget.InfoFile, config }));
|
|
}
|
|
|
|
// 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 (AutogenTarget.GlobalTarget) {
|
|
usePRE_BUILD = false;
|
|
}
|
|
}
|
|
// Create the autogen target/command
|
|
if (usePRE_BUILD) {
|
|
// Add additional autogen target dependencies to origin target
|
|
for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
|
|
this->GenTarget->Target->AddUtility(depTarget->GetName(),
|
|
this->Makefile);
|
|
}
|
|
|
|
// 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!
|
|
const std::vector<std::string> no_output;
|
|
const std::vector<std::string> no_deps;
|
|
cmCustomCommand cc(no_output, autogenProvides, no_deps, commandLines,
|
|
this->Makefile->GetBacktrace(), autogenComment.c_str(),
|
|
this->Dir.Work.c_str());
|
|
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);
|
|
if (libs != nullptr) {
|
|
for (cmLinkItem const& item : libs->Libraries) {
|
|
cmGeneratorTarget const* libTarget = item.Target;
|
|
if ((libTarget != nullptr) &&
|
|
!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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create autogen target
|
|
cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand(
|
|
this->AutogenTarget.Name, true, this->Dir.Work.c_str(),
|
|
/*byproducts=*/autogenProvides,
|
|
std::vector<std::string>(this->AutogenTarget.DependFiles.begin(),
|
|
this->AutogenTarget.DependFiles.end()),
|
|
commandLines, false, autogenComment.c_str());
|
|
// Create autogen generator target
|
|
this->LocalGen->AddGeneratorTarget(
|
|
cm::make_unique<cmGeneratorTarget>(autogenTarget, this->LocalGen));
|
|
|
|
// Forward origin utilities to autogen target
|
|
if (this->AutogenTarget.DependOrigin) {
|
|
for (BT<std::string> const& depName : this->GenTarget->GetUtilities()) {
|
|
autogenTarget->AddUtility(depName.Value, this->Makefile);
|
|
}
|
|
}
|
|
// Add additional autogen target dependencies to autogen target
|
|
for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
|
|
autogenTarget->AddUtility(depTarget->GetName(), 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,
|
|
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;
|
|
}
|
|
|
|
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;
|
|
ccOutput.push_back(qrc.OutputFile);
|
|
|
|
std::vector<std::string> ccDepends;
|
|
// Add the .qrc and info file to the custom command dependencies
|
|
ccDepends.push_back(qrc.QrcFile);
|
|
ccDepends.push_back(qrc.InfoFile);
|
|
|
|
cmCustomCommandLines commandLines;
|
|
if (this->MultiConfig) {
|
|
// Build for all configurations
|
|
for (std::string const& config : this->ConfigsList) {
|
|
commandLines.push_back(
|
|
cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
|
|
"cmake_autorcc", qrc.InfoFile, config }));
|
|
}
|
|
} else {
|
|
commandLines.push_back(
|
|
cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E",
|
|
"cmake_autorcc", qrc.InfoFile, "$<CONFIG>" }));
|
|
}
|
|
std::string ccComment =
|
|
cmStrCat("Automatic RCC for ",
|
|
FileProjectRelativePath(this->Makefile, qrc.QrcFile));
|
|
|
|
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);
|
|
}
|
|
|
|
cmTarget* autoRccTarget = this->LocalGen->AddUtilityCommand(
|
|
ccName, true, this->Dir.Work.c_str(), ccOutput, ccDepends,
|
|
commandLines, false, ccComment.c_str());
|
|
|
|
// 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,
|
|
this->Makefile);
|
|
}
|
|
}
|
|
// Add autogen target to the origin target dependencies
|
|
this->GenTarget->Target->AddUtility(ccName, 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
|
|
{
|
|
std::vector<std::string> ccByproducts;
|
|
|
|
// Add the resource files to the dependencies
|
|
for (std::string const& fileName : qrc.Resources) {
|
|
// Add resource file to the custom command dependencies
|
|
ccDepends.push_back(fileName);
|
|
}
|
|
if (!this->Rcc.ExecutableTargetName.empty()) {
|
|
ccDepends.push_back(this->Rcc.ExecutableTargetName);
|
|
}
|
|
std::string no_main_dependency;
|
|
cmImplicitDependsList no_implicit_depends;
|
|
this->LocalGen->AddCustomCommandToOutput(
|
|
ccOutput, ccByproducts, ccDepends, no_main_dependency,
|
|
no_implicit_depends, commandLines, ccComment.c_str(),
|
|
this->Dir.Work.c_str());
|
|
}
|
|
// 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->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) {
|
|
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.SetUInt("PARALLEL", this->AutogenTarget.Parallel);
|
|
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.Set("QT_MOC_EXECUTABLE", this->Moc.Executable);
|
|
info.Set("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.SetArray("HEADER_EXTENSIONS",
|
|
this->Makefile->GetCMakeInstance()->GetHeaderExtensions());
|
|
info.SetArrayArray(
|
|
"HEADERS", headers, [this](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] = this->GetMocBuildPath(*muf);
|
|
});
|
|
info.SetArrayArray(
|
|
"SOURCES", sources, [](Json::Value& jval, MUFile const* muf) {
|
|
jval.resize(2u);
|
|
jval[0u] = muf->FullPath;
|
|
jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u');
|
|
});
|
|
|
|
// 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);
|
|
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.Set("MOC_COMPILATION_FILE", this->Moc.CompilationFile);
|
|
info.SetArray("MOC_PREDEFS_CMD", this->Moc.PredefsCmd);
|
|
info.SetConfig("MOC_PREDEFS_FILE", this->Moc.PredefsFile);
|
|
}
|
|
|
|
// 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.UiFiles,
|
|
[](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.SetUInt("VERBOSITY", this->Verbosity);
|
|
|
|
// 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.Set("RCC_EXECUTABLE", this->Rcc.Executable);
|
|
info.SetArray("RCC_LIST_OPTIONS",
|
|
this->Rcc.ExecutableFeatures->ListOptions);
|
|
|
|
// 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.SetArray("INPUTS", qrc.Resources);
|
|
|
|
info.Save(qrc.InfoFile);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
cmSourceFile* cmQtAutoGenInitializer::RegisterGeneratedSource(
|
|
std::string const& filename)
|
|
{
|
|
cmSourceFile* gFile = this->Makefile->GetOrCreateSource(filename, true);
|
|
gFile->SetProperty("GENERATED", "1");
|
|
gFile->SetProperty("SKIP_AUTOGEN", "1");
|
|
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::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) {
|
|
const char* propName =
|
|
this->Makefile->GetState()->GetGlobalProperty(prop);
|
|
if ((propName != nullptr) && (*propName != '\0')) {
|
|
groupName = propName;
|
|
property = prop;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Generate a source group on demand
|
|
if (!groupName.empty()) {
|
|
sourceGroup = this->Makefile->GetOrCreateSourceGroup(groupName);
|
|
if (sourceGroup == nullptr) {
|
|
cmSystemTools::Error(
|
|
cmStrCat(genNameUpper, " error in ", property,
|
|
": Could not find or create the source group ",
|
|
cmQtAutoGen::Quoted(groupName)));
|
|
}
|
|
}
|
|
}
|
|
if (sourceGroup != nullptr) {
|
|
sourceGroup->AddGroupFile(fileName);
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGenInitializer::AddCleanFile(std::string const& fileName)
|
|
{
|
|
this->GenTarget->Target->AppendProperty("ADDITIONAL_CLEAN_FILES", fileName,
|
|
false);
|
|
}
|
|
|
|
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::ConfigFileClean(ConfigString& configString)
|
|
{
|
|
this->AddCleanFile(configString.Default);
|
|
if (this->MultiConfig) {
|
|
for (auto const& pair : configString.Config) {
|
|
this->AddCleanFile(pair.second);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::pair<cmQtAutoGen::IntegerVersion, unsigned int>
|
|
cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target)
|
|
{
|
|
// Converts a char ptr to an unsigned int value
|
|
auto toUInt = [](const char* const input) -> unsigned int {
|
|
unsigned long tmp = 0;
|
|
if (input != nullptr && 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, &toUInt](const char* major,
|
|
const char* minor) {
|
|
cmQtAutoGen::IntegerVersion ver(toUInt(major), toUInt(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 it : knownQtVersions) {
|
|
if (it.Major == res.second) {
|
|
res.first = it;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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(lfbt);
|
|
std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(val);
|
|
genVars.Executable = cge->Evaluate(this->LocalGen, "");
|
|
}
|
|
if (genVars.Executable.empty() && !ignoreMissingTarget) {
|
|
print_err(prop + " evaluates to an empty value");
|
|
return false;
|
|
}
|
|
|
|
// Create empty compiler features.
|
|
genVars.ExecutableFeatures =
|
|
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 != nullptr) {
|
|
genVars.ExecutableTargetName = targetName;
|
|
genVars.ExecutableTarget = genTarget;
|
|
if (genTarget->IsImported()) {
|
|
genVars.Executable = genTarget->ImportedGetLocation("");
|
|
} else {
|
|
genVars.Executable = genTarget->GetLocation("");
|
|
}
|
|
} else {
|
|
if (ignoreMissingTarget) {
|
|
// Create empty compiler features.
|
|
genVars.ExecutableFeatures =
|
|
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);
|
|
if (!genVars.ExecutableFeatures) {
|
|
print_err(err);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|