mirror of
https://github.com/Kitware/CMake.git
synced 2026-05-04 13:19:51 -05:00
513eb014eb
Until CMake 3.10 a list of source files that had the AUTOUIC_OPTIONS property populated was kept in `cmMakefile::QtUiFilesWithOptions`. In the process to remove all AUTOUIC related code from `cmMakefile` for CMake 3.10, the pre filtered list was replaced by a loop in `cmQtAutoGeneratorInitializer` over all source files in the `cmMakefile`. This loop introduced the problem that file paths were computed for source files that weren't in the target's sources and that might not even have existed. If the path for an unused and not existing file was computed a `cmake::FATAL_ERROR` with the error message "Cannot find source file:" was thrown nevertheless. This caused some projects to fail in CMake 3.10. This patch adds a test for path errors in the loops in `cmQtAutoGeneratorInitializer` that iterate over all source files in a `cmMakefile`. If a path error appears, the file is silently ignored. If the file is part of the target's sources, the path error will still be caught in the loop over all the target's sources. Closes #17573 Closes #17589
1359 lines
46 KiB
C++
1359 lines
46 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmQtAutoGen.h"
|
|
#include "cmQtAutoGeneratorInitializer.h"
|
|
|
|
#include "cmAlgorithms.h"
|
|
#include "cmCustomCommand.h"
|
|
#include "cmCustomCommandLines.h"
|
|
#include "cmFilePathChecksum.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmLinkItem.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmOutputConverter.h"
|
|
#include "cmPolicies.h"
|
|
#include "cmProcessOutput.h"
|
|
#include "cmSourceFile.h"
|
|
#include "cmSourceGroup.h"
|
|
#include "cmState.h"
|
|
#include "cmStateTypes.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmTarget.h"
|
|
#include "cm_sys_stat.h"
|
|
#include "cmake.h"
|
|
#include "cmsys/FStream.hxx"
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <deque>
|
|
#include <map>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
inline static const char* SafeString(const char* value)
|
|
{
|
|
return (value != nullptr) ? value : "";
|
|
}
|
|
|
|
inline static std::string GetSafeProperty(cmGeneratorTarget const* target,
|
|
const char* key)
|
|
{
|
|
return std::string(SafeString(target->GetProperty(key)));
|
|
}
|
|
|
|
inline static std::string GetSafeProperty(cmSourceFile const* sf,
|
|
const char* key)
|
|
{
|
|
return std::string(SafeString(sf->GetProperty(key)));
|
|
}
|
|
|
|
static void AddDefinitionEscaped(cmMakefile* makefile, const char* key,
|
|
std::string const& value)
|
|
{
|
|
makefile->AddDefinition(key,
|
|
cmOutputConverter::EscapeForCMake(value).c_str());
|
|
}
|
|
|
|
static void AddDefinitionEscaped(cmMakefile* makefile, const char* key,
|
|
const std::vector<std::string>& values)
|
|
{
|
|
makefile->AddDefinition(
|
|
key, cmOutputConverter::EscapeForCMake(cmJoin(values, ";")).c_str());
|
|
}
|
|
|
|
static void AddDefinitionEscaped(cmMakefile* makefile, const char* key,
|
|
const std::set<std::string>& values)
|
|
{
|
|
makefile->AddDefinition(
|
|
key, cmOutputConverter::EscapeForCMake(cmJoin(values, ";")).c_str());
|
|
}
|
|
|
|
static void AddDefinitionEscaped(
|
|
cmMakefile* makefile, const char* key,
|
|
const std::vector<std::vector<std::string>>& lists)
|
|
{
|
|
std::vector<std::string> seplist;
|
|
for (const std::vector<std::string>& list : lists) {
|
|
std::string blist = "{";
|
|
blist += cmJoin(list, ";");
|
|
blist += "}";
|
|
seplist.push_back(std::move(blist));
|
|
}
|
|
makefile->AddDefinition(key, cmOutputConverter::EscapeForCMake(
|
|
cmJoin(seplist, cmQtAutoGen::listSep))
|
|
.c_str());
|
|
}
|
|
|
|
static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName,
|
|
cmQtAutoGen::Generator genType)
|
|
{
|
|
cmSourceGroup* sourceGroup = nullptr;
|
|
// Acquire source group
|
|
{
|
|
std::string property;
|
|
std::string groupName;
|
|
{
|
|
std::array<std::string, 2> props;
|
|
// Use generator specific group name
|
|
switch (genType) {
|
|
case cmQtAutoGen::MOC:
|
|
props[0] = "AUTOMOC_SOURCE_GROUP";
|
|
break;
|
|
case cmQtAutoGen::RCC:
|
|
props[0] = "AUTORCC_SOURCE_GROUP";
|
|
break;
|
|
default:
|
|
props[0] = "AUTOGEN_SOURCE_GROUP";
|
|
break;
|
|
}
|
|
props[1] = "AUTOGEN_SOURCE_GROUP";
|
|
for (std::string& prop : props) {
|
|
const char* propName = makefile->GetState()->GetGlobalProperty(prop);
|
|
if ((propName != nullptr) && (*propName != '\0')) {
|
|
groupName = propName;
|
|
property = std::move(prop);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Generate a source group on demand
|
|
if (!groupName.empty()) {
|
|
sourceGroup = makefile->GetOrCreateSourceGroup(groupName);
|
|
if (sourceGroup == nullptr) {
|
|
std::ostringstream ost;
|
|
ost << cmQtAutoGen::GeneratorNameUpper(genType);
|
|
ost << ": " << property;
|
|
ost << ": Could not find or create the source group ";
|
|
ost << cmQtAutoGen::Quoted(groupName);
|
|
cmSystemTools::Error(ost.str().c_str());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
if (sourceGroup != nullptr) {
|
|
sourceGroup->AddGroupFile(fileName);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void AddCleanFile(cmMakefile* makefile, std::string const& fileName)
|
|
{
|
|
makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fileName.c_str(),
|
|
false);
|
|
}
|
|
|
|
static std::string FileProjectRelativePath(cmMakefile* makefile,
|
|
std::string const& fileName)
|
|
{
|
|
std::string res;
|
|
{
|
|
std::string pSource = cmSystemTools::RelativePath(
|
|
makefile->GetCurrentSourceDirectory(), fileName.c_str());
|
|
std::string pBinary = cmSystemTools::RelativePath(
|
|
makefile->GetCurrentBinaryDirectory(), fileName.c_str());
|
|
if (pSource.size() < pBinary.size()) {
|
|
res = std::move(pSource);
|
|
} else if (pBinary.size() < fileName.size()) {
|
|
res = std::move(pBinary);
|
|
} else {
|
|
res = fileName;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/* @brief Tests if targetDepend is a STATIC_LIBRARY and if any of its
|
|
* recursive STATIC_LIBRARY dependencies depends on targetOrigin
|
|
* (STATIC_LIBRARY cycle).
|
|
*/
|
|
static 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;
|
|
}
|
|
|
|
cmQtAutoGeneratorInitializer::cmQtAutoGeneratorInitializer(
|
|
cmGeneratorTarget* target, bool mocEnabled, bool uicEnabled, bool rccEnabled,
|
|
std::string const& qtVersionMajor)
|
|
: Target(target)
|
|
, MocEnabled(mocEnabled)
|
|
, UicEnabled(uicEnabled)
|
|
, RccEnabled(rccEnabled)
|
|
, QtVersionMajor(qtVersionMajor)
|
|
, MultiConfig(cmQtAutoGen::WRAP)
|
|
{
|
|
this->QtVersionMinor = cmQtAutoGeneratorInitializer::GetQtMinorVersion(
|
|
target, this->QtVersionMajor);
|
|
}
|
|
|
|
void cmQtAutoGeneratorInitializer::InitCustomTargets()
|
|
{
|
|
cmMakefile* makefile = this->Target->Target->GetMakefile();
|
|
cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
|
|
cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator();
|
|
|
|
// Configurations
|
|
this->ConfigDefault = makefile->GetConfigurations(this->ConfigsList);
|
|
if (this->ConfigsList.empty()) {
|
|
this->ConfigsList.push_back(this->ConfigDefault);
|
|
}
|
|
|
|
// Multi configuration
|
|
{
|
|
if (!globalGen->IsMultiConfig()) {
|
|
this->MultiConfig = cmQtAutoGen::SINGLE;
|
|
}
|
|
|
|
// FIXME: Xcode does not support per-config sources, yet.
|
|
// (EXCLUDED_SOURCE_FILE_NAMES)
|
|
// if (globalGen->GetName().find("Xcode") != std::string::npos) {
|
|
// return cmQtAutoGen::FULL;
|
|
//}
|
|
|
|
// FIXME: Visual Studio does not support per-config sources, yet.
|
|
// (EXCLUDED_SOURCE_FILE_NAMES)
|
|
// if (globalGen->GetName().find("Visual Studio") != std::string::npos) {
|
|
// return cmQtAutoGen::FULL;
|
|
//}
|
|
}
|
|
|
|
// Autogen target name
|
|
this->AutogenTargetName = this->Target->GetName();
|
|
this->AutogenTargetName += "_autogen";
|
|
|
|
// Autogen directories
|
|
{
|
|
// Collapsed current binary directory
|
|
std::string const cbd = cmSystemTools::CollapseFullPath(
|
|
"", makefile->GetCurrentBinaryDirectory());
|
|
|
|
// Autogen info dir
|
|
this->DirInfo = cbd;
|
|
this->DirInfo += makefile->GetCMakeInstance()->GetCMakeFilesDirectory();
|
|
this->DirInfo += "/";
|
|
this->DirInfo += this->AutogenTargetName;
|
|
this->DirInfo += ".dir";
|
|
cmSystemTools::ConvertToUnixSlashes(this->DirInfo);
|
|
|
|
// Autogen build dir
|
|
this->DirBuild = GetSafeProperty(this->Target, "AUTOGEN_BUILD_DIR");
|
|
if (this->DirBuild.empty()) {
|
|
this->DirBuild = cbd;
|
|
this->DirBuild += "/";
|
|
this->DirBuild += this->AutogenTargetName;
|
|
}
|
|
cmSystemTools::ConvertToUnixSlashes(this->DirBuild);
|
|
|
|
// Working directory
|
|
this->DirWork = cbd;
|
|
cmSystemTools::ConvertToUnixSlashes(this->DirWork);
|
|
}
|
|
|
|
// Autogen files
|
|
{
|
|
this->AutogenInfoFile = this->DirInfo;
|
|
this->AutogenInfoFile += "/AutogenInfo.cmake";
|
|
|
|
this->AutogenSettingsFile = this->DirInfo;
|
|
this->AutogenSettingsFile += "/AutogenOldSettings.cmake";
|
|
}
|
|
|
|
// Autogen target FOLDER property
|
|
{
|
|
const char* folder =
|
|
makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
|
|
if (folder == nullptr) {
|
|
folder =
|
|
makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER");
|
|
}
|
|
// Inherit FOLDER property from target (#13688)
|
|
if (folder == nullptr) {
|
|
folder = SafeString(this->Target->Target->GetProperty("FOLDER"));
|
|
}
|
|
if (folder != nullptr) {
|
|
this->AutogenFolder = folder;
|
|
}
|
|
}
|
|
|
|
std::set<std::string> autogenDependFiles;
|
|
std::set<cmTarget*> autogenDependTargets;
|
|
std::vector<std::string> autogenProvides;
|
|
|
|
// Remove build directories on cleanup
|
|
AddCleanFile(makefile, this->DirBuild);
|
|
// Remove old settings on cleanup
|
|
{
|
|
std::string base = this->DirInfo;
|
|
base += "/AutogenOldSettings";
|
|
if (this->MultiConfig == cmQtAutoGen::SINGLE) {
|
|
AddCleanFile(makefile, base.append(".cmake"));
|
|
} else {
|
|
for (std::string const& cfg : this->ConfigsList) {
|
|
std::string filename = base;
|
|
filename += "_";
|
|
filename += cfg;
|
|
filename += ".cmake";
|
|
AddCleanFile(makefile, filename);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add moc compilation to generated files list
|
|
if (this->MocEnabled) {
|
|
std::string const mocsComp = this->DirBuild + "/mocs_compilation.cpp";
|
|
auto files = this->AddGeneratedSource(mocsComp, cmQtAutoGen::MOC);
|
|
for (std::string& file : files) {
|
|
autogenProvides.push_back(std::move(file));
|
|
}
|
|
}
|
|
|
|
// Add autogen includes directory to the origin target INCLUDE_DIRECTORIES
|
|
if (this->MocEnabled || this->UicEnabled) {
|
|
std::string includeDir = this->DirBuild + "/include";
|
|
if (this->MultiConfig != cmQtAutoGen::SINGLE) {
|
|
includeDir += "_$<CONFIG>";
|
|
}
|
|
this->Target->AddIncludeDirectory(includeDir, true);
|
|
}
|
|
|
|
// Acquire rcc executable and features
|
|
if (this->RccEnabled) {
|
|
{
|
|
std::string err;
|
|
if (this->QtVersionMajor == "5") {
|
|
cmGeneratorTarget* tgt =
|
|
localGen->FindGeneratorTargetToUse("Qt5::rcc");
|
|
if (tgt != nullptr) {
|
|
this->RccExecutable = SafeString(tgt->ImportedGetLocation(""));
|
|
} else {
|
|
err = "AUTORCC: Qt5::rcc target not found";
|
|
}
|
|
} else if (QtVersionMajor == "4") {
|
|
cmGeneratorTarget* tgt =
|
|
localGen->FindGeneratorTargetToUse("Qt4::rcc");
|
|
if (tgt != nullptr) {
|
|
this->RccExecutable = SafeString(tgt->ImportedGetLocation(""));
|
|
} else {
|
|
err = "AUTORCC: Qt4::rcc target not found";
|
|
}
|
|
} else {
|
|
err = "The AUTORCC feature supports only Qt 4 and Qt 5";
|
|
}
|
|
if (!err.empty()) {
|
|
err += " (";
|
|
err += this->Target->GetName();
|
|
err += ")";
|
|
cmSystemTools::Error(err.c_str());
|
|
}
|
|
}
|
|
// Detect if rcc supports (-)-list
|
|
if (!this->RccExecutable.empty() && (this->QtVersionMajor == "5")) {
|
|
std::vector<std::string> command;
|
|
command.push_back(this->RccExecutable);
|
|
command.push_back("--help");
|
|
std::string rccStdOut;
|
|
std::string rccStdErr;
|
|
int retVal = 0;
|
|
bool result = cmSystemTools::RunSingleCommand(
|
|
command, &rccStdOut, &rccStdErr, &retVal, nullptr,
|
|
cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto);
|
|
if (result && retVal == 0 &&
|
|
rccStdOut.find("--list") != std::string::npos) {
|
|
this->RccListOptions.push_back("--list");
|
|
} else {
|
|
this->RccListOptions.push_back("-list");
|
|
}
|
|
}
|
|
}
|
|
|
|
// Extract relevant source files
|
|
std::vector<std::string> generatedSources;
|
|
std::vector<std::string> generatedHeaders;
|
|
{
|
|
std::string const qrcExt = "qrc";
|
|
std::vector<cmSourceFile*> srcFiles;
|
|
this->Target->GetConfigCommonSourceFiles(srcFiles);
|
|
for (cmSourceFile* sf : srcFiles) {
|
|
if (sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
|
|
continue;
|
|
}
|
|
// sf->GetExtension() is only valid after sf->GetFullPath() ...
|
|
std::string const& fPath = sf->GetFullPath();
|
|
std::string const& ext = sf->GetExtension();
|
|
// Register generated files that will be scanned by moc or uic
|
|
if (this->MocEnabled || this->UicEnabled) {
|
|
cmSystemTools::FileFormat const fileType =
|
|
cmSystemTools::GetFileFormat(ext.c_str());
|
|
if ((fileType == cmSystemTools::CXX_FILE_FORMAT) ||
|
|
(fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
|
|
std::string const absPath = cmSystemTools::GetRealPath(fPath);
|
|
if ((this->MocEnabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) ||
|
|
(this->UicEnabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) {
|
|
// Register source
|
|
const bool generated = sf->GetPropertyAsBool("GENERATED");
|
|
if (fileType == cmSystemTools::HEADER_FILE_FORMAT) {
|
|
if (generated) {
|
|
generatedHeaders.push_back(absPath);
|
|
} else {
|
|
this->Headers.push_back(absPath);
|
|
}
|
|
} else {
|
|
if (generated) {
|
|
generatedSources.push_back(absPath);
|
|
} else {
|
|
this->Sources.push_back(absPath);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Register rcc enabled files
|
|
if (this->RccEnabled && (ext == qrcExt) &&
|
|
!sf->GetPropertyAsBool("SKIP_AUTORCC")) {
|
|
// Register qrc file
|
|
{
|
|
Qrc qrc;
|
|
qrc.QrcFile = cmSystemTools::GetRealPath(fPath);
|
|
qrc.QrcName =
|
|
cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile);
|
|
qrc.Generated = sf->GetPropertyAsBool("GENERATED");
|
|
// RCC options
|
|
{
|
|
std::string const opts = GetSafeProperty(sf, "AUTORCC_OPTIONS");
|
|
if (!opts.empty()) {
|
|
cmSystemTools::ExpandListArgument(opts, qrc.Options);
|
|
}
|
|
}
|
|
this->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->Target->ClearSourcesCache();
|
|
}
|
|
// Read skip files from makefile sources
|
|
if (this->MocEnabled || this->UicEnabled) {
|
|
std::string pathError;
|
|
for (cmSourceFile* sf : makefile->GetSourceFiles()) {
|
|
// sf->GetExtension() is only valid after sf->GetFullPath() ...
|
|
// 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 const& fPath = sf->GetFullPath(&pathError);
|
|
if (!pathError.empty()) {
|
|
pathError.clear();
|
|
continue;
|
|
}
|
|
cmSystemTools::FileFormat const fileType =
|
|
cmSystemTools::GetFileFormat(sf->GetExtension().c_str());
|
|
if (!(fileType == cmSystemTools::CXX_FILE_FORMAT) &&
|
|
!(fileType == cmSystemTools::HEADER_FILE_FORMAT)) {
|
|
continue;
|
|
}
|
|
const bool skipAll = sf->GetPropertyAsBool("SKIP_AUTOGEN");
|
|
const bool mocSkip =
|
|
this->MocEnabled && (skipAll || sf->GetPropertyAsBool("SKIP_AUTOMOC"));
|
|
const bool uicSkip =
|
|
this->UicEnabled && (skipAll || sf->GetPropertyAsBool("SKIP_AUTOUIC"));
|
|
if (mocSkip || uicSkip) {
|
|
std::string const absFile = cmSystemTools::GetRealPath(fPath);
|
|
if (mocSkip) {
|
|
this->MocSkip.insert(absFile);
|
|
}
|
|
if (uicSkip) {
|
|
this->UicSkip.insert(absFile);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Process GENERATED sources and headers
|
|
if (!generatedSources.empty() || !generatedHeaders.empty()) {
|
|
// Check status of policy CMP0071
|
|
bool policyAccept = false;
|
|
bool policyWarn = false;
|
|
cmPolicies::PolicyStatus const CMP0071_status =
|
|
makefile->GetPolicyStatus(cmPolicies::CMP0071);
|
|
switch (CMP0071_status) {
|
|
case cmPolicies::WARN:
|
|
policyWarn = true;
|
|
CM_FALLTHROUGH;
|
|
case cmPolicies::OLD:
|
|
// Ignore GENERATED file
|
|
break;
|
|
case cmPolicies::REQUIRED_IF_USED:
|
|
case cmPolicies::REQUIRED_ALWAYS:
|
|
case cmPolicies::NEW:
|
|
// Process GENERATED file
|
|
policyAccept = true;
|
|
break;
|
|
}
|
|
|
|
if (policyAccept) {
|
|
// Accept GENERATED sources
|
|
for (std::string const& absFile : generatedHeaders) {
|
|
this->Headers.push_back(absFile);
|
|
autogenDependFiles.insert(absFile);
|
|
}
|
|
for (std::string const& absFile : generatedSources) {
|
|
this->Sources.push_back(absFile);
|
|
autogenDependFiles.insert(absFile);
|
|
}
|
|
} else {
|
|
if (policyWarn) {
|
|
std::string msg;
|
|
msg += cmPolicies::GetPolicyWarning(cmPolicies::CMP0071);
|
|
msg += "\n";
|
|
std::string tools;
|
|
std::string property;
|
|
if (this->MocEnabled && this->UicEnabled) {
|
|
tools = "AUTOMOC and AUTOUIC";
|
|
property = "SKIP_AUTOGEN";
|
|
} else if (this->MocEnabled) {
|
|
tools = "AUTOMOC";
|
|
property = "SKIP_AUTOMOC";
|
|
} else if (this->UicEnabled) {
|
|
tools = "AUTOUIC";
|
|
property = "SKIP_AUTOUIC";
|
|
}
|
|
msg += "For compatibility, CMake is excluding the GENERATED source "
|
|
"file(s):\n";
|
|
for (const std::string& absFile : generatedHeaders) {
|
|
msg.append(" ").append(cmQtAutoGen::Quoted(absFile)).append("\n");
|
|
}
|
|
for (const std::string& absFile : generatedSources) {
|
|
msg.append(" ").append(cmQtAutoGen::Quoted(absFile)).append("\n");
|
|
}
|
|
msg += "from processing by ";
|
|
msg += tools;
|
|
msg +=
|
|
". 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 ";
|
|
msg += property;
|
|
msg += ":\n set_property(SOURCE file.h PROPERTY ";
|
|
msg += property;
|
|
msg += " ON)\n";
|
|
makefile->IssueMessage(cmake::AUTHOR_WARNING, msg);
|
|
}
|
|
}
|
|
// Clear lists
|
|
generatedSources.clear();
|
|
generatedHeaders.clear();
|
|
}
|
|
// Sort headers and sources
|
|
if (this->MocEnabled || this->UicEnabled) {
|
|
std::sort(this->Headers.begin(), this->Headers.end());
|
|
std::sort(this->Sources.begin(), this->Sources.end());
|
|
}
|
|
|
|
// Process qrc files
|
|
if (!this->Qrcs.empty()) {
|
|
const bool QtV5 = (this->QtVersionMajor == "5");
|
|
// Target rcc options
|
|
std::vector<std::string> optionsTarget;
|
|
cmSystemTools::ExpandListArgument(
|
|
GetSafeProperty(this->Target, "AUTORCC_OPTIONS"), optionsTarget);
|
|
|
|
// Check if file name is unique
|
|
for (Qrc& qrc : this->Qrcs) {
|
|
qrc.Unique = true;
|
|
for (Qrc const& qrc2 : this->Qrcs) {
|
|
if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) {
|
|
qrc.Unique = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Path checksum and file names
|
|
{
|
|
cmFilePathChecksum const fpathCheckSum(makefile);
|
|
for (Qrc& qrc : this->Qrcs) {
|
|
qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile);
|
|
// RCC output file name
|
|
{
|
|
std::string rccFile = this->DirBuild + "/";
|
|
rccFile += qrc.PathChecksum;
|
|
rccFile += "/qrc_";
|
|
rccFile += qrc.QrcName;
|
|
rccFile += ".cpp";
|
|
qrc.RccFile = std::move(rccFile);
|
|
}
|
|
{
|
|
std::string base = this->DirInfo;
|
|
base += "/RCC";
|
|
base += qrc.QrcName;
|
|
if (!qrc.Unique) {
|
|
base += qrc.PathChecksum;
|
|
}
|
|
qrc.InfoFile = base;
|
|
qrc.InfoFile += "Info.cmake";
|
|
qrc.SettingsFile = base;
|
|
qrc.SettingsFile += "Settings.cmake";
|
|
}
|
|
}
|
|
}
|
|
// RCC options
|
|
for (Qrc& qrc : this->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 += "_";
|
|
name += qrc.PathChecksum;
|
|
}
|
|
std::vector<std::string> nameOpts;
|
|
nameOpts.emplace_back("-name");
|
|
nameOpts.emplace_back(std::move(name));
|
|
cmQtAutoGen::RccMergeOptions(opts, nameOpts, QtV5);
|
|
}
|
|
// Merge file option
|
|
cmQtAutoGen::RccMergeOptions(opts, qrc.Options, QtV5);
|
|
qrc.Options = std::move(opts);
|
|
}
|
|
for (Qrc& qrc : this->Qrcs) {
|
|
// Register file at target
|
|
std::vector<std::string> const ccOutput =
|
|
this->AddGeneratedSource(qrc.RccFile, cmQtAutoGen::RCC);
|
|
|
|
cmCustomCommandLines commandLines;
|
|
{
|
|
cmCustomCommandLine currentLine;
|
|
currentLine.push_back(cmSystemTools::GetCMakeCommand());
|
|
currentLine.push_back("-E");
|
|
currentLine.push_back("cmake_autorcc");
|
|
currentLine.push_back(qrc.InfoFile);
|
|
currentLine.push_back("$<CONFIGURATION>");
|
|
commandLines.push_back(std::move(currentLine));
|
|
}
|
|
std::string ccComment = "Automatic RCC for ";
|
|
ccComment += FileProjectRelativePath(makefile, qrc.QrcFile);
|
|
|
|
if (qrc.Generated) {
|
|
// Create custom rcc target
|
|
std::string ccName;
|
|
{
|
|
ccName = this->Target->GetName();
|
|
ccName += "_arcc_";
|
|
ccName += qrc.QrcName;
|
|
if (!qrc.Unique) {
|
|
ccName += "_";
|
|
ccName += qrc.PathChecksum;
|
|
}
|
|
std::vector<std::string> ccDepends;
|
|
// Add the .qrc file to the custom target dependencies
|
|
ccDepends.push_back(qrc.QrcFile);
|
|
|
|
cmTarget* autoRccTarget = makefile->AddUtilityCommand(
|
|
ccName, cmMakefile::TargetOrigin::Generator, true,
|
|
this->DirWork.c_str(), ccOutput, ccDepends, commandLines, false,
|
|
ccComment.c_str());
|
|
// Create autogen generator target
|
|
localGen->AddGeneratorTarget(
|
|
new cmGeneratorTarget(autoRccTarget, localGen));
|
|
|
|
// Set FOLDER property in autogen target
|
|
if (!this->AutogenFolder.empty()) {
|
|
autoRccTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
|
|
}
|
|
}
|
|
// Add autogen target to the origin target dependencies
|
|
this->Target->Target->AddUtility(ccName, makefile);
|
|
} else {
|
|
// Create custom rcc command
|
|
{
|
|
std::vector<std::string> ccByproducts;
|
|
std::vector<std::string> ccDepends;
|
|
// Add the .qrc file to the custom command dependencies
|
|
ccDepends.push_back(qrc.QrcFile);
|
|
|
|
// Add the resource files to the dependencies
|
|
{
|
|
std::string error;
|
|
if (cmQtAutoGen::RccListInputs(this->RccExecutable,
|
|
this->RccListOptions, qrc.QrcFile,
|
|
qrc.Resources, &error)) {
|
|
for (std::string const& fileName : qrc.Resources) {
|
|
// Add resource file to the custom command dependencies
|
|
ccDepends.push_back(fileName);
|
|
}
|
|
} else {
|
|
cmSystemTools::Error(error.c_str());
|
|
}
|
|
}
|
|
makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends,
|
|
/*main_dependency*/ std::string(),
|
|
commandLines, ccComment.c_str(),
|
|
this->DirWork.c_str());
|
|
}
|
|
// Reconfigure when .qrc file changes
|
|
makefile->AddCMakeDependFile(qrc.QrcFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create _autogen target
|
|
if (this->MocEnabled || this->UicEnabled) {
|
|
// Add user defined autogen target dependencies
|
|
{
|
|
std::string const deps =
|
|
GetSafeProperty(this->Target, "AUTOGEN_TARGET_DEPENDS");
|
|
if (!deps.empty()) {
|
|
std::vector<std::string> extraDeps;
|
|
cmSystemTools::ExpandListArgument(deps, extraDeps);
|
|
for (std::string const& depName : extraDeps) {
|
|
// Allow target and file dependencies
|
|
auto* depTarget = makefile->FindTargetToUse(depName);
|
|
if (depTarget != nullptr) {
|
|
autogenDependTargets.insert(depTarget);
|
|
} else {
|
|
autogenDependFiles.insert(depName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compose target comment
|
|
std::string autogenComment;
|
|
{
|
|
std::string tools;
|
|
if (this->MocEnabled) {
|
|
tools += "MOC";
|
|
}
|
|
if (this->UicEnabled) {
|
|
if (!tools.empty()) {
|
|
tools += " and ";
|
|
}
|
|
tools += "UIC";
|
|
}
|
|
autogenComment = "Automatic ";
|
|
autogenComment += tools;
|
|
autogenComment += " for target ";
|
|
autogenComment += this->Target->GetName();
|
|
}
|
|
|
|
// Compose command lines
|
|
cmCustomCommandLines commandLines;
|
|
{
|
|
cmCustomCommandLine currentLine;
|
|
currentLine.push_back(cmSystemTools::GetCMakeCommand());
|
|
currentLine.push_back("-E");
|
|
currentLine.push_back("cmake_autogen");
|
|
currentLine.push_back(this->AutogenInfoFile);
|
|
currentLine.push_back("$<CONFIGURATION>");
|
|
commandLines.push_back(std::move(currentLine));
|
|
}
|
|
|
|
// Use PRE_BUILD on demand
|
|
bool usePRE_BUILD = false;
|
|
if (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 (!autogenDependFiles.empty()) {
|
|
usePRE_BUILD = false;
|
|
}
|
|
}
|
|
// Create the autogen target/command
|
|
if (usePRE_BUILD) {
|
|
// Add additional autogen target dependencies to origin target
|
|
for (cmTarget* depTarget : autogenDependTargets) {
|
|
this->Target->Target->AddUtility(depTarget->GetName(), 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(makefile, no_output, autogenProvides, no_deps,
|
|
commandLines, autogenComment.c_str(),
|
|
this->DirWork.c_str());
|
|
cc.SetEscapeOldStyle(false);
|
|
cc.SetEscapeAllowMakeVars(true);
|
|
this->Target->Target->AddPreBuildCommand(cc);
|
|
} else {
|
|
|
|
// Add link library target dependencies to the autogen target
|
|
// dependencies
|
|
{
|
|
// 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->Target->GetLinkImplementationLibraries(config);
|
|
if (libs != nullptr) {
|
|
for (cmLinkItem const& item : libs->Libraries) {
|
|
cmGeneratorTarget const* libTarget = item.Target;
|
|
if ((libTarget != nullptr) &&
|
|
!StaticLibraryCycle(this->Target, libTarget, config)) {
|
|
// Increment target config count
|
|
commonTargets[libTarget]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (auto const& item : commonTargets) {
|
|
if (item.second == this->ConfigsList.size()) {
|
|
autogenDependTargets.insert(item.first->Target);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create autogen target
|
|
cmTarget* autogenTarget = makefile->AddUtilityCommand(
|
|
this->AutogenTargetName, cmMakefile::TargetOrigin::Generator, true,
|
|
this->DirWork.c_str(), /*byproducts=*/autogenProvides,
|
|
std::vector<std::string>(autogenDependFiles.begin(),
|
|
autogenDependFiles.end()),
|
|
commandLines, false, autogenComment.c_str());
|
|
// Create autogen generator target
|
|
localGen->AddGeneratorTarget(
|
|
new cmGeneratorTarget(autogenTarget, localGen));
|
|
|
|
// Forward origin utilities to autogen target
|
|
for (std::string const& depName : this->Target->Target->GetUtilities()) {
|
|
autogenTarget->AddUtility(depName, makefile);
|
|
}
|
|
// Add additional autogen target dependencies to autogen target
|
|
for (cmTarget* depTarget : autogenDependTargets) {
|
|
autogenTarget->AddUtility(depTarget->GetName(), makefile);
|
|
}
|
|
|
|
// Set FOLDER property in autogen target
|
|
if (!this->AutogenFolder.empty()) {
|
|
autogenTarget->SetProperty("FOLDER", this->AutogenFolder.c_str());
|
|
}
|
|
|
|
// Add autogen target to the origin target dependencies
|
|
this->Target->Target->AddUtility(this->AutogenTargetName, makefile);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorInitializer::SetupCustomTargets()
|
|
{
|
|
cmMakefile* makefile = this->Target->Target->GetMakefile();
|
|
|
|
// forget the variables added here afterwards again:
|
|
cmMakefile::ScopePushPop varScope(makefile);
|
|
static_cast<void>(varScope);
|
|
|
|
// Configuration suffixes
|
|
std::map<std::string, std::string> configSuffixes;
|
|
for (std::string const& cfg : this->ConfigsList) {
|
|
std::string& suffix = configSuffixes[cfg];
|
|
suffix = "_";
|
|
suffix += cfg;
|
|
}
|
|
|
|
// Basic setup
|
|
AddDefinitionEscaped(makefile, "_multi_config",
|
|
cmQtAutoGen::MultiConfigName(this->MultiConfig));
|
|
AddDefinitionEscaped(makefile, "_build_dir", this->DirBuild);
|
|
|
|
if (this->MocEnabled || this->UicEnabled) {
|
|
AddDefinitionEscaped(makefile, "_qt_version_major", this->QtVersionMajor);
|
|
AddDefinitionEscaped(makefile, "_settings_file",
|
|
this->AutogenSettingsFile);
|
|
AddDefinitionEscaped(makefile, "_sources", this->Sources);
|
|
AddDefinitionEscaped(makefile, "_headers", this->Headers);
|
|
|
|
if (this->MocEnabled) {
|
|
this->SetupCustomTargetsMoc();
|
|
}
|
|
if (this->UicEnabled) {
|
|
this->SetupCustomTargetsUic();
|
|
}
|
|
}
|
|
if (this->RccEnabled) {
|
|
AddDefinitionEscaped(makefile, "_qt_rcc_executable", this->RccExecutable);
|
|
AddDefinitionEscaped(makefile, "_qt_rcc_list_options",
|
|
this->RccListOptions);
|
|
}
|
|
|
|
// Create info directory on demand
|
|
if (!cmSystemTools::MakeDirectory(this->DirInfo)) {
|
|
std::string emsg = ("Could not create directory: ");
|
|
emsg += cmQtAutoGen::Quoted(this->DirInfo);
|
|
cmSystemTools::Error(emsg.c_str());
|
|
}
|
|
|
|
auto ReOpenInfoFile = [](cmsys::ofstream& ofs,
|
|
std::string const& fileName) -> bool {
|
|
// Ensure we have write permission
|
|
mode_t perm = 0;
|
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
mode_t mode_write = S_IWRITE;
|
|
#else
|
|
mode_t mode_write = S_IWUSR;
|
|
#endif
|
|
cmSystemTools::GetPermissions(fileName, perm);
|
|
if (!(perm & mode_write)) {
|
|
cmSystemTools::SetPermissions(fileName, perm | mode_write);
|
|
}
|
|
|
|
ofs.open(fileName.c_str(), std::ios::app);
|
|
if (!ofs) {
|
|
// File open error
|
|
std::string error = "Internal CMake error when trying to open file: ";
|
|
error += cmQtAutoGen::Quoted(fileName);
|
|
error += " for writing.";
|
|
cmSystemTools::Error(error.c_str());
|
|
}
|
|
return static_cast<bool>(ofs);
|
|
};
|
|
|
|
// Generate autogen target info file
|
|
if (this->MocEnabled || this->UicEnabled) {
|
|
{
|
|
std::string infoFileIn = cmSystemTools::GetCMakeRoot();
|
|
infoFileIn += "/Modules/AutogenInfo.cmake.in";
|
|
makefile->ConfigureFile(
|
|
infoFileIn.c_str(), this->AutogenInfoFile.c_str(), false, true, false);
|
|
}
|
|
|
|
// Append custom definitions to info file
|
|
// --------------------------------------
|
|
cmsys::ofstream ofs;
|
|
if (ReOpenInfoFile(ofs, this->AutogenInfoFile)) {
|
|
auto OfsWriteMap = [&ofs](
|
|
const char* key, std::map<std::string, std::string> const& map) {
|
|
for (auto const& item : map) {
|
|
ofs << "set(" << key << "_" << item.first << " "
|
|
<< cmOutputConverter::EscapeForCMake(item.second) << ")\n";
|
|
}
|
|
};
|
|
ofs << "# Configurations options\n";
|
|
OfsWriteMap("AM_CONFIG_SUFFIX", configSuffixes);
|
|
OfsWriteMap("AM_MOC_DEFINITIONS", this->ConfigMocDefines);
|
|
OfsWriteMap("AM_MOC_INCLUDES", this->ConfigMocIncludes);
|
|
OfsWriteMap("AM_UIC_TARGET_OPTIONS", this->ConfigUicOptions);
|
|
// Settings files (only require for multi configuration generators)
|
|
if (this->MultiConfig != cmQtAutoGen::SINGLE) {
|
|
std::map<std::string, std::string> settingsFiles;
|
|
for (std::string const& cfg : this->ConfigsList) {
|
|
settingsFiles[cfg] = cmQtAutoGen::AppendFilenameSuffix(
|
|
this->AutogenSettingsFile, "_" + cfg);
|
|
}
|
|
OfsWriteMap("AM_SETTINGS_FILE", settingsFiles);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate auto RCC info files
|
|
if (this->RccEnabled) {
|
|
std::string infoFileIn = cmSystemTools::GetCMakeRoot();
|
|
infoFileIn += "/Modules/AutoRccInfo.cmake.in";
|
|
for (Qrc const& qrc : this->Qrcs) {
|
|
// Configure info file
|
|
makefile->ConfigureFile(infoFileIn.c_str(), qrc.InfoFile.c_str(), false,
|
|
true, false);
|
|
|
|
// Append custom definitions to info file
|
|
// --------------------------------------
|
|
cmsys::ofstream ofs;
|
|
if (ReOpenInfoFile(ofs, qrc.InfoFile)) {
|
|
{
|
|
ofs << "# Job\n";
|
|
auto OfsWrite = [&ofs](const char* key, std::string const& value) {
|
|
ofs << "set(" << key << " "
|
|
<< cmOutputConverter::EscapeForCMake(value) << ")\n";
|
|
|
|
};
|
|
OfsWrite("ARCC_SETTINGS_FILE", qrc.SettingsFile);
|
|
OfsWrite("ARCC_SOURCE", qrc.QrcFile);
|
|
OfsWrite("ARCC_OUTPUT", qrc.RccFile);
|
|
OfsWrite("ARCC_OPTIONS", cmJoin(qrc.Options, ";"));
|
|
OfsWrite("ARCC_INPUTS", cmJoin(qrc.Resources, ";"));
|
|
}
|
|
{
|
|
ofs << "# Configurations options\n";
|
|
auto OfsWriteMap = [&ofs](
|
|
const char* key, std::map<std::string, std::string> const& map) {
|
|
for (auto const& item : map) {
|
|
ofs << "set(" << key << "_" << item.first << " "
|
|
<< cmOutputConverter::EscapeForCMake(item.second) << ")\n";
|
|
}
|
|
};
|
|
OfsWriteMap("ARCC_CONFIG_SUFFIX", configSuffixes);
|
|
|
|
// Settings files (only require for multi configuration generators)
|
|
if (this->MultiConfig != cmQtAutoGen::SINGLE) {
|
|
std::map<std::string, std::string> settingsFiles;
|
|
for (std::string const& cfg : this->ConfigsList) {
|
|
settingsFiles[cfg] =
|
|
cmQtAutoGen::AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg);
|
|
}
|
|
OfsWriteMap("ARCC_SETTINGS_FILE", settingsFiles);
|
|
}
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorInitializer::SetupCustomTargetsMoc()
|
|
{
|
|
cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
|
|
cmMakefile* makefile = this->Target->Target->GetMakefile();
|
|
|
|
AddDefinitionEscaped(makefile, "_moc_skip", this->MocSkip);
|
|
AddDefinitionEscaped(makefile, "_moc_options",
|
|
GetSafeProperty(this->Target, "AUTOMOC_MOC_OPTIONS"));
|
|
AddDefinitionEscaped(makefile, "_moc_relaxed_mode",
|
|
makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE") ? "TRUE"
|
|
: "FALSE");
|
|
AddDefinitionEscaped(makefile, "_moc_macro_names",
|
|
GetSafeProperty(this->Target, "AUTOMOC_MACRO_NAMES"));
|
|
AddDefinitionEscaped(
|
|
makefile, "_moc_depend_filters",
|
|
GetSafeProperty(this->Target, "AUTOMOC_DEPEND_FILTERS"));
|
|
|
|
// Compiler predefines
|
|
if (this->Target->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") &&
|
|
this->QtVersionGreaterOrEqual(5, 8)) {
|
|
AddDefinitionEscaped(
|
|
makefile, "_moc_predefs_cmd",
|
|
makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND"));
|
|
}
|
|
// Moc includes and compile definitions
|
|
{
|
|
auto GetIncludeDirs = [this,
|
|
localGen](std::string const& cfg) -> std::string {
|
|
// Get the include dirs for this target, without stripping the implicit
|
|
// include dirs off, see
|
|
// https://gitlab.kitware.com/cmake/cmake/issues/13667
|
|
std::vector<std::string> includeDirs;
|
|
localGen->GetIncludeDirectories(includeDirs, this->Target, "CXX", cfg,
|
|
false);
|
|
return cmJoin(includeDirs, ";");
|
|
};
|
|
auto GetCompileDefinitions =
|
|
[this, localGen](std::string const& cfg) -> std::string {
|
|
std::set<std::string> defines;
|
|
localGen->AddCompileDefinitions(defines, this->Target, cfg, "CXX");
|
|
return cmJoin(defines, ";");
|
|
};
|
|
|
|
// Default configuration settings
|
|
std::string const includeDirs = GetIncludeDirs(this->ConfigDefault);
|
|
std::string const compileDefs = GetCompileDefinitions(this->ConfigDefault);
|
|
// Other configuration settings
|
|
for (std::string const& cfg : this->ConfigsList) {
|
|
{
|
|
std::string const configIncludeDirs = GetIncludeDirs(cfg);
|
|
if (configIncludeDirs != includeDirs) {
|
|
this->ConfigMocIncludes[cfg] = configIncludeDirs;
|
|
}
|
|
}
|
|
{
|
|
std::string const configCompileDefs = GetCompileDefinitions(cfg);
|
|
if (configCompileDefs != compileDefs) {
|
|
this->ConfigMocDefines[cfg] = configCompileDefs;
|
|
}
|
|
}
|
|
}
|
|
AddDefinitionEscaped(makefile, "_moc_include_dirs", includeDirs);
|
|
AddDefinitionEscaped(makefile, "_moc_compile_defs", compileDefs);
|
|
}
|
|
|
|
// Moc executable
|
|
{
|
|
std::string mocExec;
|
|
std::string err;
|
|
|
|
if (this->QtVersionMajor == "5") {
|
|
cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::moc");
|
|
if (tgt != nullptr) {
|
|
mocExec = SafeString(tgt->ImportedGetLocation(""));
|
|
} else {
|
|
err = "AUTOMOC: Qt5::moc target not found";
|
|
}
|
|
} else if (this->QtVersionMajor == "4") {
|
|
cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::moc");
|
|
if (tgt != nullptr) {
|
|
mocExec = SafeString(tgt->ImportedGetLocation(""));
|
|
} else {
|
|
err = "AUTOMOC: Qt4::moc target not found";
|
|
}
|
|
} else {
|
|
err = "The AUTOMOC feature supports only Qt 4 and Qt 5";
|
|
}
|
|
|
|
if (err.empty()) {
|
|
AddDefinitionEscaped(makefile, "_qt_moc_executable", mocExec);
|
|
} else {
|
|
err += " (";
|
|
err += this->Target->GetName();
|
|
err += ")";
|
|
cmSystemTools::Error(err.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmQtAutoGeneratorInitializer::SetupCustomTargetsUic()
|
|
{
|
|
cmMakefile* makefile = this->Target->Target->GetMakefile();
|
|
|
|
// Uic search paths
|
|
{
|
|
std::vector<std::string> uicSearchPaths;
|
|
{
|
|
std::string const usp =
|
|
GetSafeProperty(this->Target, "AUTOUIC_SEARCH_PATHS");
|
|
if (!usp.empty()) {
|
|
cmSystemTools::ExpandListArgument(usp, uicSearchPaths);
|
|
std::string const srcDir = makefile->GetCurrentSourceDirectory();
|
|
for (std::string& path : uicSearchPaths) {
|
|
path = cmSystemTools::CollapseFullPath(path, srcDir);
|
|
}
|
|
}
|
|
}
|
|
AddDefinitionEscaped(makefile, "_uic_search_paths", uicSearchPaths);
|
|
}
|
|
// Uic target options
|
|
{
|
|
auto UicGetOpts = [this](std::string const& cfg) -> std::string {
|
|
std::vector<std::string> opts;
|
|
this->Target->GetAutoUicOptions(opts, cfg);
|
|
return cmJoin(opts, ";");
|
|
};
|
|
|
|
// Default settings
|
|
std::string const uicOpts = UicGetOpts(this->ConfigDefault);
|
|
AddDefinitionEscaped(makefile, "_uic_target_options", uicOpts);
|
|
|
|
// Configuration specific settings
|
|
for (std::string const& cfg : this->ConfigsList) {
|
|
std::string const configUicOpts = UicGetOpts(cfg);
|
|
if (configUicOpts != uicOpts) {
|
|
this->ConfigUicOptions[cfg] = configUicOpts;
|
|
}
|
|
}
|
|
}
|
|
// .ui files skip and options
|
|
{
|
|
std::vector<std::string> uiFileFiles;
|
|
std::vector<std::vector<std::string>> uiFileOptions;
|
|
{
|
|
std::string const uiExt = "ui";
|
|
std::string pathError;
|
|
for (cmSourceFile* sf : makefile->GetSourceFiles()) {
|
|
// sf->GetExtension() is only valid after sf->GetFullPath() ...
|
|
// 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 const& fPath = sf->GetFullPath(&pathError);
|
|
if (!pathError.empty()) {
|
|
pathError.clear();
|
|
continue;
|
|
}
|
|
if (sf->GetExtension() == uiExt) {
|
|
std::string const absFile = cmSystemTools::GetRealPath(fPath);
|
|
// Check if the .ui file should be skipped
|
|
if (sf->GetPropertyAsBool("SKIP_AUTOUIC") ||
|
|
sf->GetPropertyAsBool("SKIP_AUTOGEN")) {
|
|
this->UicSkip.insert(absFile);
|
|
}
|
|
// Check if the .ui file has uic options
|
|
std::string const uicOpts = GetSafeProperty(sf, "AUTOUIC_OPTIONS");
|
|
if (!uicOpts.empty()) {
|
|
// Check if file isn't skipped
|
|
if (this->UicSkip.count(absFile) == 0) {
|
|
uiFileFiles.push_back(absFile);
|
|
std::vector<std::string> optsVec;
|
|
cmSystemTools::ExpandListArgument(uicOpts, optsVec);
|
|
uiFileOptions.push_back(std::move(optsVec));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
AddDefinitionEscaped(makefile, "_qt_uic_options_files", uiFileFiles);
|
|
AddDefinitionEscaped(makefile, "_qt_uic_options_options", uiFileOptions);
|
|
}
|
|
|
|
AddDefinitionEscaped(makefile, "_uic_skip", this->UicSkip);
|
|
|
|
// Uic executable
|
|
{
|
|
std::string err;
|
|
std::string uicExec;
|
|
|
|
cmLocalGenerator* localGen = this->Target->GetLocalGenerator();
|
|
if (this->QtVersionMajor == "5") {
|
|
cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::uic");
|
|
if (tgt != nullptr) {
|
|
uicExec = SafeString(tgt->ImportedGetLocation(""));
|
|
} else {
|
|
// Project does not use Qt5Widgets, but has AUTOUIC ON anyway
|
|
}
|
|
} else if (this->QtVersionMajor == "4") {
|
|
cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::uic");
|
|
if (tgt != nullptr) {
|
|
uicExec = SafeString(tgt->ImportedGetLocation(""));
|
|
} else {
|
|
err = "AUTOUIC: Qt4::uic target not found";
|
|
}
|
|
} else {
|
|
err = "The AUTOUIC feature supports only Qt 4 and Qt 5";
|
|
}
|
|
|
|
if (err.empty()) {
|
|
AddDefinitionEscaped(makefile, "_qt_uic_executable", uicExec);
|
|
} else {
|
|
err += " (";
|
|
err += this->Target->GetName();
|
|
err += ")";
|
|
cmSystemTools::Error(err.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource(
|
|
std::string const& filename, cmQtAutoGen::Generator genType)
|
|
{
|
|
std::vector<std::string> genFiles;
|
|
// Register source file in makefile and source group
|
|
if (this->MultiConfig != cmQtAutoGen::FULL) {
|
|
genFiles.push_back(filename);
|
|
} else {
|
|
for (std::string const& cfg : this->ConfigsList) {
|
|
genFiles.push_back(
|
|
cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg));
|
|
}
|
|
}
|
|
{
|
|
cmMakefile* makefile = this->Target->Target->GetMakefile();
|
|
for (std::string const& genFile : genFiles) {
|
|
{
|
|
cmSourceFile* gFile = makefile->GetOrCreateSource(genFile, true);
|
|
gFile->SetProperty("GENERATED", "1");
|
|
gFile->SetProperty("SKIP_AUTOGEN", "On");
|
|
}
|
|
AddToSourceGroup(makefile, genFile, genType);
|
|
}
|
|
}
|
|
|
|
// Add source file to target
|
|
if (this->MultiConfig != cmQtAutoGen::FULL) {
|
|
this->Target->AddSource(filename);
|
|
} else {
|
|
for (std::string const& cfg : this->ConfigsList) {
|
|
std::string src = "$<$<CONFIG:";
|
|
src += cfg;
|
|
src += ">:";
|
|
src += cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg);
|
|
src += ">";
|
|
this->Target->AddSource(src);
|
|
}
|
|
}
|
|
|
|
return genFiles;
|
|
}
|
|
|
|
std::string cmQtAutoGeneratorInitializer::GetQtMajorVersion(
|
|
cmGeneratorTarget const* target)
|
|
{
|
|
cmMakefile* makefile = target->Target->GetMakefile();
|
|
std::string qtMajor = makefile->GetSafeDefinition("QT_VERSION_MAJOR");
|
|
if (qtMajor.empty()) {
|
|
qtMajor = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR");
|
|
}
|
|
const char* targetQtVersion =
|
|
target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", "");
|
|
if (targetQtVersion != nullptr) {
|
|
qtMajor = targetQtVersion;
|
|
}
|
|
return qtMajor;
|
|
}
|
|
|
|
std::string cmQtAutoGeneratorInitializer::GetQtMinorVersion(
|
|
cmGeneratorTarget const* target, std::string const& qtVersionMajor)
|
|
{
|
|
cmMakefile* makefile = target->Target->GetMakefile();
|
|
std::string qtMinor;
|
|
if (qtVersionMajor == "5") {
|
|
qtMinor = makefile->GetSafeDefinition("Qt5Core_VERSION_MINOR");
|
|
}
|
|
if (qtMinor.empty()) {
|
|
qtMinor = makefile->GetSafeDefinition("QT_VERSION_MINOR");
|
|
}
|
|
|
|
const char* targetQtVersion =
|
|
target->GetLinkInterfaceDependentStringProperty("QT_MINOR_VERSION", "");
|
|
if (targetQtVersion != nullptr) {
|
|
qtMinor = targetQtVersion;
|
|
}
|
|
return qtMinor;
|
|
}
|
|
|
|
bool cmQtAutoGeneratorInitializer::QtVersionGreaterOrEqual(
|
|
unsigned long requestMajor, unsigned long requestMinor) const
|
|
{
|
|
unsigned long majorUL(0);
|
|
unsigned long minorUL(0);
|
|
if (cmSystemTools::StringToULong(this->QtVersionMajor.c_str(), &majorUL) &&
|
|
cmSystemTools::StringToULong(this->QtVersionMinor.c_str(), &minorUL)) {
|
|
return (majorUL > requestMajor) ||
|
|
(majorUL == requestMajor && minorUL >= requestMinor);
|
|
}
|
|
return false;
|
|
}
|