
Many custom commands are created by CMake itself rather than by the user. These custom commands should always have their policies set to NEW, and user-created custom commands should have their policy values set only from the state snapshot. In addition, we want to genericize the mechanism of recording a policy at the time of custom command creation. Add a CM_FOR_EACH_CUSTOM_COMMAND_POLICY macro to genericize custom command policies. Use this to define all custom command policies. Make all such policies NEW instead of WARN by default. Remove individual policy modifier methods and add a single method that records relevant values from a cmStateSnapshot. Remove the no longer needed explicit policy settings from synthesized custom commands.
2173 lines
72 KiB
C++
2173 lines
72 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 <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 <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 "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 "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* 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::LinkInterfaceFor::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();
|
|
}
|
|
|
|
void AddAutogenExecutableToDependencies(
|
|
cmQtAutoGenInitializer::GenVarsT const& genVars,
|
|
std::vector<std::string>& dependencies)
|
|
{
|
|
if (genVars.ExecutableTarget != nullptr) {
|
|
dependencies.push_back(genVars.ExecutableTarget->Target->GetName());
|
|
} else if (!genVars.Executable.empty()) {
|
|
dependencies.push_back(genVars.Executable);
|
|
}
|
|
}
|
|
|
|
} // 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;
|
|
}
|
|
|
|
bool cmQtAutoGenInitializer::InitCustomTargets()
|
|
{
|
|
// Configurations
|
|
this->MultiConfig = this->GlobalGen->IsMultiConfig();
|
|
this->ConfigDefault = this->Makefile->GetDefaultConfiguration();
|
|
this->ConfigsList =
|
|
this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
|
|
|
|
// 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
|
|
{
|
|
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
|
|
{
|
|
std::string const& 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
|
|
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 (std::string const& depName : cmExpandedList(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 (cmIsOn(this->GenTarget->GetProperty("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);
|
|
this->Moc.MacroNames.erase(cmRemoveDuplicates(this->Moc.MacroNames),
|
|
this->Moc.MacroNames.end());
|
|
{
|
|
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 && !this->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
|
|
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
|
|
this->Makefile->GetDefExpandList("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND",
|
|
this->Moc.PredefsCmd);
|
|
// Header
|
|
if (!this->Moc.PredefsCmd.empty()) {
|
|
this->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);
|
|
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;
|
|
};
|
|
|
|
// 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) {
|
|
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) {
|
|
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,
|
|
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 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) {
|
|
// 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,
|
|
cmExpandedList(uicOpts));
|
|
}
|
|
|
|
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
|
|
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");
|
|
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;
|
|
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);
|
|
|
|
// Determine whether to use a depfile for the AUTOGEN target.
|
|
const bool useNinjaDepfile = this->QtVersion >= IntegerVersion(5, 15) &&
|
|
this->GlobalGen->GetName().find("Ninja") != 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 (useNinjaDepfile) {
|
|
if (this->MultiConfig) {
|
|
// 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 (const auto& 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
|
|
std::vector<std::string> configs;
|
|
this->GlobalGen->GetQtAutoGenConfigs(configs);
|
|
bool stdPipesUTF8 = true;
|
|
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 (this->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(), 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::LinkInterfaceFor::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);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> dependencies(
|
|
this->AutogenTarget.DependFiles.begin(),
|
|
this->AutogenTarget.DependFiles.end());
|
|
|
|
if (useNinjaDepfile) {
|
|
// 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");
|
|
std::vector<std::string> timestampTargetProvides;
|
|
cmCustomCommandLines timestampTargetCommandLines;
|
|
|
|
// Add additional autogen target dependencies to
|
|
// '_autogen_timestamp_deps'.
|
|
for (const cmTarget* t : this->AutogenTarget.DependTargets) {
|
|
std::string depname = t->GetName();
|
|
if (t->IsImported()) {
|
|
auto ttype = t->GetType();
|
|
if (ttype == cmStateEnums::TargetType::STATIC_LIBRARY ||
|
|
ttype == cmStateEnums::TargetType::SHARED_LIBRARY ||
|
|
ttype == cmStateEnums::TargetType::UNKNOWN_LIBRARY) {
|
|
depname = cmStrCat("$<TARGET_LINKER_FILE:", t->GetName(), ">");
|
|
}
|
|
}
|
|
dependencies.push_back(depname);
|
|
}
|
|
|
|
auto cc = cm::make_unique<cmCustomCommand>();
|
|
cc->SetWorkingDirectory(this->Dir.Work.c_str());
|
|
cc->SetByproducts(timestampTargetProvides);
|
|
cc->SetDepends(dependencies);
|
|
cc->SetCommandLines(timestampTargetCommandLines);
|
|
cc->SetEscapeOldStyle(false);
|
|
cmTarget* 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);
|
|
|
|
// Create the custom command that outputs the timestamp file.
|
|
const char timestampFileName[] = "timestamp";
|
|
const std::string outputFile =
|
|
cmStrCat(this->Dir.Build, "/", timestampFileName);
|
|
this->AutogenTarget.DepFile = cmStrCat(this->Dir.Build, "/deps");
|
|
this->AutogenTarget.DepFileRuleName =
|
|
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(this->AutogenTarget.DepFile);
|
|
cc->SetStdPipesUTF8(stdPipesUTF8);
|
|
this->LocalGen->AddCustomCommandToOutput(std::move(cc));
|
|
|
|
// Alter variables for the autogen target which now merely wraps the
|
|
// custom command
|
|
dependencies.clear();
|
|
dependencies.push_back(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));
|
|
|
|
// Forward origin utilities to autogen target
|
|
if (this->AutogenTarget.DependOrigin) {
|
|
for (BT<std::pair<std::string, bool>> const& depName :
|
|
this->GenTarget->GetUtilities()) {
|
|
autogenTarget->AddUtility(depName.Value.first, false, this->Makefile);
|
|
}
|
|
}
|
|
if (!useNinjaDepfile) {
|
|
// Add additional autogen target dependencies to autogen target
|
|
for (cmTarget* depTarget : this->AutogenTarget.DependTargets) {
|
|
autogenTarget->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;
|
|
}
|
|
|
|
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));
|
|
|
|
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
|
|
{
|
|
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);
|
|
}
|
|
cc->SetOutputs(ccOutput);
|
|
cc->SetByproducts(ccByproducts);
|
|
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.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.SetUInt("QT_VERSION_MINOR", this->QtVersion.Minor);
|
|
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.Set("DEP_FILE", this->AutogenTarget.DepFile);
|
|
info.Set("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);
|
|
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.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.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.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->MarkAsGenerated();
|
|
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::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 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* makefile,
|
|
unsigned int qtMajorVersion)
|
|
{
|
|
std::string result;
|
|
const std::string mocTargetName =
|
|
"Qt" + std::to_string(qtMajorVersion) + "::moc";
|
|
cmTarget* 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 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);
|
|
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) {
|
|
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;
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|