summaryrefslogtreecommitdiff
path: root/Source/cmQtAutoGenInitializer.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Source/cmQtAutoGenInitializer.cxx')
-rw-r--r--Source/cmQtAutoGenInitializer.cxx238
1 files changed, 191 insertions, 47 deletions
diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx
index a1816f19f..2894201c9 100644
--- a/Source/cmQtAutoGenInitializer.cxx
+++ b/Source/cmQtAutoGenInitializer.cxx
@@ -6,8 +6,8 @@
#include <deque>
#include <initializer_list>
#include <map>
-#include <ostream>
#include <set>
+#include <sstream> // for basic_ios, istringstream
#include <string>
#include <unordered_set>
#include <utility>
@@ -114,10 +114,10 @@ bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin,
// Collect all static_library dependencies from the test target
cmLinkImplementationLibraries const* libs =
testTarget->GetLinkImplementationLibraries(config);
- if (libs != nullptr) {
+ if (libs) {
for (cmLinkItem const& item : libs->Libraries) {
cmGeneratorTarget const* depTarget = item.Target;
- if ((depTarget != nullptr) &&
+ if (depTarget &&
(depTarget->GetType() == cmStateEnums::STATIC_LIBRARY) &&
knownLibs.insert(depTarget).second) {
testLibs.push_back(depTarget);
@@ -294,6 +294,17 @@ bool InfoWriter::Save(std::string const& filename)
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(
@@ -346,15 +357,15 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
{
cmProp folder =
this->Makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER");
- if (folder == nullptr) {
+ if (!folder) {
folder = this->Makefile->GetState()->GetGlobalProperty(
"AUTOGEN_TARGETS_FOLDER");
}
// Inherit FOLDER property from target (#13688)
- if (folder == nullptr) {
+ if (!folder) {
folder = this->GenTarget->GetProperty("FOLDER");
}
- if (folder != nullptr) {
+ if (folder) {
this->TargetsFolder = *folder;
}
}
@@ -419,12 +430,8 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
cmSystemTools::ConvertToUnixSlashes(this->Dir.Work);
// Include directory
- this->ConfigFileNames(this->Dir.Include,
- cmStrCat(this->Dir.Build, "/include"), "");
- this->Dir.IncludeGenExp = this->Dir.Include.Default;
- if (this->MultiConfig) {
- this->Dir.IncludeGenExp += "_$<CONFIG>";
- }
+ this->ConfigFileNamesAndGenex(this->Dir.Include, this->Dir.IncludeGenExp,
+ cmStrCat(this->Dir.Build, "/include"), "");
}
// Moc, Uic and _autogen target settings
@@ -483,7 +490,7 @@ bool cmQtAutoGenInitializer::InitCustomTargets()
for (std::string const& depName : cmExpandedList(deps)) {
// Allow target and file dependencies
auto* depTarget = this->Makefile->FindTargetToUse(depName);
- if (depTarget != nullptr) {
+ if (depTarget) {
this->AutogenTarget.DependTargets.insert(depTarget);
} else {
this->AutogenTarget.DependFiles.insert(depName);
@@ -575,15 +582,9 @@ bool cmQtAutoGenInitializer::InitMoc()
cmStrCat(this->Dir.Build, "/mocs_compilation.cpp");
this->Moc.CompilationFileGenex = this->Moc.CompilationFile.Default;
} else {
- this->ConfigFileNames(this->Moc.CompilationFile,
- cmStrCat(this->Dir.Build, "/mocs_compilation"),
- ".cpp");
- if (this->MultiConfig) {
- this->Moc.CompilationFileGenex =
- cmStrCat(this->Dir.Build, "/mocs_compilation_$<CONFIG>.cpp"_s);
- } else {
- this->Moc.CompilationFileGenex = this->Moc.CompilationFile.Default;
- }
+ this->ConfigFileNamesAndGenex(
+ this->Moc.CompilationFile, this->Moc.CompilationFileGenex,
+ cmStrCat(this->Dir.Build, "/mocs_compilation"_s), ".cpp"_s);
}
// Moc predefs
@@ -661,7 +662,7 @@ bool cmQtAutoGenInitializer::InitMoc()
return false;
}
// Let the _autogen target depend on the moc executable
- if (this->Moc.ExecutableTarget != nullptr) {
+ if (this->Moc.ExecutableTarget) {
this->AutogenTarget.DependTargets.insert(
this->Moc.ExecutableTarget->Target);
}
@@ -709,7 +710,7 @@ bool cmQtAutoGenInitializer::InitUic()
return false;
}
// Let the _autogen target depend on the uic executable
- if (this->Uic.ExecutableTarget != nullptr) {
+ if (this->Uic.ExecutableTarget) {
this->AutogenTarget.DependTargets.insert(
this->Uic.ExecutableTarget->Target);
}
@@ -864,7 +865,7 @@ bool cmQtAutoGenInitializer::InitScanFiles()
auto constexpr locationKind = cmSourceFileLocationKind::Known;
cmSourceFile* sf =
this->Makefile->GetSource(fullPath, locationKind);
- if (sf != nullptr) {
+ if (sf) {
// Check if we know about this header already
if (cm::contains(this->AutogenTarget.Headers, sf)) {
continue;
@@ -879,7 +880,7 @@ bool cmQtAutoGenInitializer::InitScanFiles()
sf = this->Makefile->CreateSource(fullPath, false, locationKind);
}
- if (sf != nullptr) {
+ if (sf) {
auto eMuf = makeMUFile(sf, fullPath, muf.Configs, true);
// Only process moc/uic when the parent is processed as well
if (!muf.MocIt) {
@@ -935,9 +936,35 @@ bool cmQtAutoGenInitializer::InitScanFiles()
if (!skipUic) {
// Check if the .ui file has uic options
std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS);
- if (!uicOpts.empty()) {
- this->Uic.UiFiles.emplace_back(fullPath, cmExpandedList(uicOpts));
+ 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(
+ std::make_pair(uiHeader, uiHeaderGenex));
} else {
// Register skipped .ui file
this->Uic.SkipUi.insert(fullPath);
@@ -1091,6 +1118,13 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
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;
{
@@ -1149,6 +1183,42 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
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
+ std::string no_main_dependency;
+ cmCustomCommandLines no_command_lines;
+ this->LocalGen->AddCustomCommandToOutput(
+ timestampFileGenex, uicDependencies, no_main_dependency,
+ no_command_lines, /*comment=*/"", this->Dir.Work.c_str(),
+ /*cmp0116=*/cmPolicies::NEW, /*replace=*/false,
+ /*escapeOldStyle=*/false, /*uses_terminal=*/false,
+ /*command_expand_lists=*/false, /*depfile=*/"", /*job_pool=*/"",
+ stdPipesUTF8);
+ }
+
// 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.
@@ -1173,10 +1243,10 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
for (std::string const& config : this->ConfigsList) {
cmLinkImplementationLibraries const* libs =
this->GenTarget->GetLinkImplementationLibraries(config);
- if (libs != nullptr) {
+ if (libs) {
for (cmLinkItem const& item : libs->Libraries) {
cmGeneratorTarget const* libTarget = item.Target;
- if ((libTarget != nullptr) &&
+ if (libTarget &&
!StaticLibraryCycle(this->GenTarget, libTarget, config)) {
// Increment target config count
commonTargets[libTarget]++;
@@ -1241,16 +1311,8 @@ bool cmQtAutoGenInitializer::InitAutogenTarget()
dependencies.clear();
dependencies.push_back(timestampTargetName);
- if (this->Moc.ExecutableTarget != nullptr) {
- dependencies.push_back(this->Moc.ExecutableTarget->Target->GetName());
- } else if (!this->Moc.Executable.empty()) {
- dependencies.push_back(this->Moc.Executable);
- }
- if (this->Uic.ExecutableTarget != nullptr) {
- dependencies.push_back(this->Uic.ExecutableTarget->Target->GetName());
- } else if (!this->Uic.Executable.empty()) {
- dependencies.push_back(this->Uic.Executable);
- }
+ AddAutogenExecutableToDependencies(this->Moc, dependencies);
+ AddAutogenExecutableToDependencies(this->Uic, dependencies);
// Create the custom command that outputs the timestamp file.
const char timestampFileName[] = "timestamp";
@@ -1589,7 +1651,7 @@ bool cmQtAutoGenInitializer::SetupWriteAutogenInfo()
uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end());
info.SetArray("UIC_SKIP", uic_skip);
- info.SetArrayArray("UIC_UI_FILES", this->Uic.UiFiles,
+ info.SetArrayArray("UIC_UI_FILES", this->Uic.UiFilesWithOptions,
[](Json::Value& jval, UicT::UiFileT const& uiFile) {
jval.resize(2u);
jval[0u] = uiFile.first;
@@ -1718,7 +1780,7 @@ void cmQtAutoGenInitializer::AddToSourceGroup(std::string const& fileName,
// Generate a source group on demand
if (!groupName.empty()) {
sourceGroup = this->Makefile->GetOrCreateSourceGroup(groupName);
- if (sourceGroup == nullptr) {
+ if (!sourceGroup) {
cmSystemTools::Error(
cmStrCat(genNameUpper, " error in ", property,
": Could not find or create the source group ",
@@ -1726,7 +1788,7 @@ void cmQtAutoGenInitializer::AddToSourceGroup(std::string const& fileName,
}
}
}
- if (sourceGroup != nullptr) {
+ if (sourceGroup) {
sourceGroup->AddGroupFile(fileName);
}
}
@@ -1749,6 +1811,18 @@ void cmQtAutoGenInitializer::ConfigFileNames(ConfigString& configString,
}
}
+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);
@@ -1759,20 +1833,75 @@ void cmQtAutoGenInitializer::ConfigFileClean(ConfigString& configString)
}
}
+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)
+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 != nullptr && cmStrToULong(input, &tmp)) {
+ if (input && cmStrToULong(input, &tmp)) {
return static_cast<unsigned int>(tmp);
}
return 0u;
};
auto toUInt2 = [](cmProp input) -> unsigned int {
unsigned long tmp = 0;
- if (input != nullptr && cmStrToULong(*input, &tmp)) {
+ if (input && cmStrToULong(*input, &tmp)) {
return static_cast<unsigned int>(tmp);
}
return 0u;
@@ -1835,6 +1964,21 @@ cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target)
}
}
}
+
+ 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;
}
@@ -1929,7 +2073,7 @@ bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars,
// Find target
cmGeneratorTarget* genTarget =
this->LocalGen->FindGeneratorTargetToUse(targetName);
- if (genTarget != nullptr) {
+ if (genTarget) {
genVars.ExecutableTargetName = targetName;
genVars.ExecutableTarget = genTarget;
if (genTarget->IsImported()) {