diff options
Diffstat (limited to 'Source/cmQtAutoGeneratorInitializer.cxx')
-rw-r--r-- | Source/cmQtAutoGeneratorInitializer.cxx | 2018 |
1 files changed, 1134 insertions, 884 deletions
diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx index cecf165..bd692a2 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGeneratorInitializer.cxx @@ -1,505 +1,1214 @@ /* 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 "cmQtAutoGeneratorCommon.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" - -#if defined(_WIN32) && !defined(__CYGWIN__) -#include "cmGlobalVisualStudioGenerator.h" -#endif - -#include "cmConfigure.h" #include "cmsys/FStream.hxx" + #include <algorithm> +#include <array> +#include <deque> #include <map> #include <set> +#include <sstream> #include <string> #include <utility> #include <vector> -static void utilCopyTargetProperty(cmTarget* destinationTarget, - cmTarget* sourceTarget, - const std::string& propertyName) +inline static const char* SafeString(const char* value) { - const char* propertyValue = sourceTarget->GetProperty(propertyName); - if (propertyValue) { - destinationTarget->SetProperty(propertyName, propertyValue); - } + return (value != nullptr) ? value : ""; } -inline static bool PropertyEnabled(cmSourceFile* sourceFile, const char* key) +inline static std::string GetSafeProperty(cmGeneratorTarget const* target, + const char* key) { - return cmSystemTools::IsOn(sourceFile->GetPropertyForUser(key)); + return std::string(SafeString(target->GetProperty(key))); } -static std::string GetSafeProperty(cmGeneratorTarget const* target, - const char* key) +inline static std::string GetSafeProperty(cmSourceFile const* sf, + const char* key) { - const char* tmp = target->GetProperty(key); - return std::string((tmp != CM_NULLPTR) ? tmp : ""); + return std::string(SafeString(sf->GetProperty(key))); } -static std::string GetAutogenTargetName(cmGeneratorTarget const* target) +static void AddDefinitionEscaped(cmMakefile* makefile, const char* key, + std::string const& value) { - std::string autogenTargetName = target->GetName(); - autogenTargetName += "_autogen"; - return autogenTargetName; + makefile->AddDefinition(key, + cmOutputConverter::EscapeForCMake(value).c_str()); } -static std::string GetAutogenTargetFilesDir(cmGeneratorTarget const* target) +static void AddDefinitionEscaped(cmMakefile* makefile, const char* key, + const std::vector<std::string>& values) { - cmMakefile* makefile = target->Target->GetMakefile(); - std::string targetDir = makefile->GetCurrentBinaryDirectory(); - targetDir += makefile->GetCMakeInstance()->GetCMakeFilesDirectory(); - targetDir += "/"; - targetDir += GetAutogenTargetName(target); - targetDir += ".dir"; - return targetDir; + makefile->AddDefinition( + key, cmOutputConverter::EscapeForCMake(cmJoin(values, ";")).c_str()); } -static std::string GetAutogenTargetBuildDir(cmGeneratorTarget const* target) +static void AddDefinitionEscaped(cmMakefile* makefile, const char* key, + const std::set<std::string>& values) { - std::string targetDir = GetSafeProperty(target, "AUTOGEN_BUILD_DIR"); - if (targetDir.empty()) { - cmMakefile* makefile = target->Target->GetMakefile(); - targetDir = makefile->GetCurrentBinaryDirectory(); - targetDir += "/"; - targetDir += GetAutogenTargetName(target); - } - return targetDir; + makefile->AddDefinition( + key, cmOutputConverter::EscapeForCMake(cmJoin(values, ";")).c_str()); } -static std::string GetQtMajorVersion(cmGeneratorTarget const* target) +static void AddDefinitionEscaped( + cmMakefile* makefile, const char* key, + const std::vector<std::vector<std::string>>& lists) { - cmMakefile* makefile = target->Target->GetMakefile(); - std::string qtMajorVersion = makefile->GetSafeDefinition("QT_VERSION_MAJOR"); - if (qtMajorVersion.empty()) { - qtMajorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR"); + 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)); } - const char* targetQtVersion = - target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", ""); - if (targetQtVersion != CM_NULLPTR) { - qtMajorVersion = targetQtVersion; - } - return qtMajorVersion; + makefile->AddDefinition(key, cmOutputConverter::EscapeForCMake( + cmJoin(seplist, cmQtAutoGen::listSep)) + .c_str()); } -static std::string GetQtMinorVersion(cmGeneratorTarget const* target, - const std::string& qtMajorVersion) +static bool AddToSourceGroup(cmMakefile* makefile, std::string const& fileName, + cmQtAutoGen::Generator genType) { - cmMakefile* makefile = target->Target->GetMakefile(); - std::string qtMinorVersion; - if (qtMajorVersion == "5") { - qtMinorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MINOR"); - } - if (qtMinorVersion.empty()) { - qtMinorVersion = makefile->GetSafeDefinition("QT_VERSION_MINOR"); + 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; + } + } } - - const char* targetQtVersion = - target->GetLinkInterfaceDependentStringProperty("QT_MINOR_VERSION", ""); - if (targetQtVersion != CM_NULLPTR) { - qtMinorVersion = targetQtVersion; + if (sourceGroup != nullptr) { + sourceGroup->AddGroupFile(fileName); } - return qtMinorVersion; + return true; } -static bool QtVersionGreaterOrEqual(const std::string& major, - const std::string& minor, - unsigned long requestMajor, - unsigned long requestMinor) +static void AddCleanFile(cmMakefile* makefile, std::string const& fileName) { - unsigned long majorUL(0); - unsigned long minorUL(0); - if (cmSystemTools::StringToULong(major.c_str(), &majorUL) && - cmSystemTools::StringToULong(minor.c_str(), &minorUL)) { - return (majorUL > requestMajor) || - (majorUL == requestMajor && minorUL >= requestMinor); - } - return false; + makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fileName.c_str(), + false); } -static void GetCompileDefinitionsAndDirectories( - cmGeneratorTarget const* target, const std::string& config, - std::string& incs, std::string& defs) +static std::string FileProjectRelativePath(cmMakefile* makefile, + std::string const& fileName) { - cmLocalGenerator* localGen = target->GetLocalGenerator(); - { - std::vector<std::string> includeDirs; - // Get the include dirs for this target, without stripping the implicit - // include dirs off, see - // https://gitlab.kitware.com/cmake/cmake/issues/13667 - localGen->GetIncludeDirectories(includeDirs, target, "CXX", config, false); - incs = cmJoin(includeDirs, ";"); - } + std::string res; { - std::set<std::string> defines; - localGen->AddCompileDefinitions(defines, target, config, "CXX"); - defs += cmJoin(defines, ";"); + 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; } -static bool IsMultiConfig(cmGlobalGenerator* globalGen) +/* @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) { - // FIXME: Xcode does not support per-config sources, yet. - // (EXCLUDED_SOURCE_FILE_NAMES) - // Treat it as a single configuration generator meanwhile. - if (globalGen->GetName().find("Xcode") != std::string::npos) { - return false; - } - // FIXME: Visual Studio does not fully support per-config sources yet. - if (globalGen->GetName().find("Visual Studio") != std::string::npos) { - return false; + 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 globalGen->IsMultiConfig(); + 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); } -static std::vector<std::string> GetConfigurations( - cmMakefile* makefile, std::string* config = CM_NULLPTR) +void cmQtAutoGeneratorInitializer::InitCustomTargets() { - std::vector<std::string> configs; + 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 { - std::string cfg = makefile->GetConfigurations(configs); - if (config != CM_NULLPTR) { - *config = cfg; + 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; + //} } - // Add empty configuration on demand - if (configs.empty()) { - configs.push_back(""); + + // 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); } - return configs; -} -static std::vector<std::string> GetConfigurationSuffixes(cmMakefile* makefile) -{ - std::vector<std::string> suffixes; - if (IsMultiConfig(makefile->GetGlobalGenerator())) { - makefile->GetConfigurations(suffixes); - for (std::vector<std::string>::iterator it = suffixes.begin(); - it != suffixes.end(); ++it) { - it->insert(0, "_"); + // 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; } } - if (suffixes.empty()) { - suffixes.push_back(""); + + 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); + } + } } - return suffixes; -} -static void AddDefinitionEscaped(cmMakefile* makefile, const char* key, - const std::string& value) -{ - makefile->AddDefinition(key, - cmOutputConverter::EscapeForCMake(value).c_str()); -} + // 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)); + } + } -static void AddDefinitionEscaped(cmMakefile* makefile, const char* key, - const std::vector<std::string>& values) -{ - makefile->AddDefinition( - key, cmOutputConverter::EscapeForCMake(cmJoin(values, ";")).c_str()); -} + // 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); + } -static bool AddToSourceGroup(cmMakefile* makefile, const std::string& fileName, - cmQtAutoGeneratorCommon::GeneratorType genType) -{ - cmSourceGroup* sourceGroup = CM_NULLPTR; - // Acquire source group + // 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; { - const char* groupName = CM_NULLPTR; - // Use generator specific group name - switch (genType) { - case cmQtAutoGeneratorCommon::MOC: - groupName = - makefile->GetState()->GetGlobalProperty("AUTOMOC_SOURCE_GROUP"); - break; - case cmQtAutoGeneratorCommon::RCC: - groupName = - makefile->GetState()->GetGlobalProperty("AUTORCC_SOURCE_GROUP"); + 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) { + const std::vector<cmSourceFile*>& allSources = makefile->GetSourceFiles(); + for (cmSourceFile* sf : allSources) { + // sf->GetExtension() is only valid after sf->GetFullPath() ... + std::string const& fPath = sf->GetFullPath(); + 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; - default: + case cmPolicies::REQUIRED_IF_USED: + case cmPolicies::REQUIRED_ALWAYS: + case cmPolicies::NEW: + // Process GENERATED file + policyAccept = true; break; } - // Use default group name on demand - if ((groupName == CM_NULLPTR) || (*groupName == 0)) { - groupName = - makefile->GetState()->GetGlobalProperty("AUTOGEN_SOURCE_GROUP"); + + 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); + } } - // Generate a source group on demand - if ((groupName != CM_NULLPTR) && (*groupName != 0)) { - { - const char* delimiter = - makefile->GetDefinition("SOURCE_GROUP_DELIMITER"); - if (delimiter == CM_NULLPTR) { - delimiter = "\\"; + // 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; } - std::vector<std::string> folders = - cmSystemTools::tokenize(groupName, delimiter); - sourceGroup = makefile->GetSourceGroup(folders); - if (sourceGroup == CM_NULLPTR) { - makefile->AddSourceGroup(folders); - sourceGroup = makefile->GetSourceGroup(folders); + } + } + // 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"; } } - if (sourceGroup == CM_NULLPTR) { - cmSystemTools::Error( - "Autogen: Could not create or find source group: ", - cmQtAutoGeneratorCommon::Quoted(groupName).c_str()); - return false; + } + // 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); } } } - if (sourceGroup != CM_NULLPTR) { - sourceGroup->AddGroupFile(fileName); + + // 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 { + + // Convert file dependencies std::set to std::vector + std::vector<std::string> autogenDepends(autogenDependFiles.begin(), + autogenDependFiles.end()); + + // Add link library target dependencies to the autogen target + // dependencies + 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)) { + std::string util; + if (this->ConfigsList.size() > 1) { + util += "$<$<CONFIG:"; + util += config; + util += ">:"; + } + util += libTarget->GetName(); + if (this->ConfigsList.size() > 1) { + util += ">"; + } + autogenDepends.push_back(util); + } + } + } + } + + // Create autogen target + cmTarget* autogenTarget = makefile->AddUtilityCommand( + this->AutogenTargetName, cmMakefile::TargetOrigin::Generator, true, + this->DirWork.c_str(), /*byproducts=*/autogenProvides, autogenDepends, + 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); + } } - return true; } -static void AddGeneratedSource(cmMakefile* makefile, - const std::string& filename, - cmQtAutoGeneratorCommon::GeneratorType genType) +void cmQtAutoGeneratorInitializer::SetupCustomTargets() { - cmSourceFile* gFile = makefile->GetOrCreateSource(filename, true); - gFile->SetProperty("GENERATED", "1"); - gFile->SetProperty("SKIP_AUTOGEN", "On"); + cmMakefile* makefile = this->Target->Target->GetMakefile(); - AddToSourceGroup(makefile, filename, genType); -} + // forget the variables added here afterwards again: + cmMakefile::ScopePushPop varScope(makefile); + static_cast<void>(varScope); -static void AcquireScanFiles(cmGeneratorTarget const* target, - std::vector<std::string>& mocUicSources, - std::vector<std::string>& mocUicHeaders, - std::vector<std::string>& mocSkipList, - std::vector<std::string>& uicSkipList) -{ - const bool mocTarget = target->GetPropertyAsBool("AUTOMOC"); - const bool uicTarget = target->GetPropertyAsBool("AUTOUIC"); - - std::vector<cmSourceFile*> srcFiles; - target->GetConfigCommonSourceFiles(srcFiles); - for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin(); - fileIt != srcFiles.end(); ++fileIt) { - cmSourceFile* sf = *fileIt; - const cmSystemTools::FileFormat fileType = - cmSystemTools::GetFileFormat(sf->GetExtension().c_str()); - - if (!(fileType == cmSystemTools::CXX_FILE_FORMAT) && - !(fileType == cmSystemTools::HEADER_FILE_FORMAT)) { - continue; + // 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 (PropertyEnabled(sf, "GENERATED") && - !target->GetPropertyAsBool("__UNDOCUMENTED_AUTOGEN_GENERATED_FILES")) { - // FIXME: Add a policy whose NEW behavior allows generated files. - // The implementation already works. We disable it here to avoid - // changing behavior for existing projects that do not expect it. - continue; + } + 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); } - const std::string absFile = - cmsys::SystemTools::GetRealPath(sf->GetFullPath()); - // Skip flags - const bool skipAll = PropertyEnabled(sf, "SKIP_AUTOGEN"); - const bool mocSkip = skipAll || PropertyEnabled(sf, "SKIP_AUTOMOC"); - const bool uicSkip = skipAll || PropertyEnabled(sf, "SKIP_AUTOUIC"); - // Add file name to skip lists. - // Do this even when the file is not added to the sources/headers lists - // because the file name may be extracted from an other file when - // processing - if (mocSkip) { - mocSkipList.push_back(absFile); + + 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()); } - if (uicSkip) { - uicSkipList.push_back(absFile); + 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); } - if ((mocTarget && !mocSkip) || (uicTarget && !uicSkip)) { - // Add file name to sources or headers list - switch (fileType) { - case cmSystemTools::CXX_FILE_FORMAT: - mocUicSources.push_back(absFile); - break; - case cmSystemTools::HEADER_FILE_FORMAT: - mocUicHeaders.push_back(absFile); - break; - default: - break; + // 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; } } } } -static void MocSetupAutoTarget( - cmGeneratorTarget const* target, const std::string& autogenTargetName, - std::string const& qtMajorVersion, std::string const& config, - std::vector<std::string> const& configs, - std::vector<std::string> const& mocSkipList, - std::map<std::string, std::string>& configMocIncludes, - std::map<std::string, std::string>& configMocDefines) +void cmQtAutoGeneratorInitializer::SetupCustomTargetsMoc() { - cmLocalGenerator* lg = target->GetLocalGenerator(); - cmMakefile* makefile = target->Target->GetMakefile(); + cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); + cmMakefile* makefile = this->Target->Target->GetMakefile(); + AddDefinitionEscaped(makefile, "_moc_skip", this->MocSkip); AddDefinitionEscaped(makefile, "_moc_options", - GetSafeProperty(target, "AUTOMOC_MOC_OPTIONS")); - AddDefinitionEscaped(makefile, "_moc_skip", mocSkipList); + GetSafeProperty(this->Target, "AUTOMOC_MOC_OPTIONS")); AddDefinitionEscaped(makefile, "_moc_relaxed_mode", makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE") ? "TRUE" : "FALSE"); - AddDefinitionEscaped(makefile, "_moc_depend_filters", - GetSafeProperty(target, "AUTOMOC_DEPEND_FILTERS")); - - if (QtVersionGreaterOrEqual( - qtMajorVersion, GetQtMinorVersion(target, qtMajorVersion), 5, 8)) { + 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 { - // Default settings - std::string incs; - std::string compileDefs; - GetCompileDefinitionsAndDirectories(target, config, incs, compileDefs); - AddDefinitionEscaped(makefile, "_moc_incs", incs); - AddDefinitionEscaped(makefile, "_moc_compile_defs", compileDefs); - - // Configuration specific settings - for (std::vector<std::string>::const_iterator li = configs.begin(); - li != configs.end(); ++li) { - std::string configIncs; - std::string configCompileDefs; - GetCompileDefinitionsAndDirectories(target, *li, configIncs, - configCompileDefs); - if (configIncs != incs) { - configMocIncludes[*li] = cmOutputConverter::EscapeForCMake(configIncs); + 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; + } } - if (configCompileDefs != compileDefs) { - configMocDefines[*li] = - cmOutputConverter::EscapeForCMake(configCompileDefs); + { + 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; - const char* mocExec = CM_NULLPTR; - if (qtMajorVersion == "5") { - cmGeneratorTarget* qt5Moc = lg->FindGeneratorTargetToUse("Qt5::moc"); - if (qt5Moc != CM_NULLPTR) { - mocExec = qt5Moc->ImportedGetLocation(""); + + if (this->QtVersionMajor == "5") { + cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::moc"); + if (tgt != nullptr) { + mocExec = SafeString(tgt->ImportedGetLocation("")); } else { - err = "Qt5::moc target not found " + autogenTargetName; + err = "AUTOMOC: Qt5::moc target not found"; } - } else if (qtMajorVersion == "4") { - cmGeneratorTarget* qt4Moc = lg->FindGeneratorTargetToUse("Qt4::moc"); - if (qt4Moc != CM_NULLPTR) { - mocExec = qt4Moc->ImportedGetLocation(""); + } else if (this->QtVersionMajor == "4") { + cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::moc"); + if (tgt != nullptr) { + mocExec = SafeString(tgt->ImportedGetLocation("")); } else { - err = "Qt4::moc target not found " + autogenTargetName; + err = "AUTOMOC: Qt4::moc target not found"; } } else { - err = "The CMAKE_AUTOMOC feature supports only Qt 4 and Qt 5 "; - err += autogenTargetName; + err = "The AUTOMOC feature supports only Qt 4 and Qt 5"; } - // Add definition or error + if (err.empty()) { - AddDefinitionEscaped(makefile, "_qt_moc_executable", - mocExec ? mocExec : ""); + AddDefinitionEscaped(makefile, "_qt_moc_executable", mocExec); } else { + err += " ("; + err += this->Target->GetName(); + err += ")"; cmSystemTools::Error(err.c_str()); } } } -static void UicGetOpts(cmGeneratorTarget const* target, - const std::string& config, std::string& optString) +void cmQtAutoGeneratorInitializer::SetupCustomTargetsUic() { - std::vector<std::string> opts; - target->GetAutoUicOptions(opts, config); - optString = cmJoin(opts, ";"); -} - -static void UicSetupAutoTarget( - cmGeneratorTarget const* target, std::string const& qtMajorVersion, - std::string const& config, std::vector<std::string> const& configs, - std::vector<std::string> const& uicSkipList, - std::map<std::string, std::string>& configUicOptions) -{ - cmLocalGenerator* lg = target->GetLocalGenerator(); - cmMakefile* makefile = target->Target->GetMakefile(); - - AddDefinitionEscaped(makefile, "_uic_skip", uicSkipList); + cmMakefile* makefile = this->Target->Target->GetMakefile(); // Uic search paths { std::vector<std::string> uicSearchPaths; - cmSystemTools::ExpandListArgument( - GetSafeProperty(target, "AUTOUIC_SEARCH_PATHS"), uicSearchPaths); - const std::string srcDir = makefile->GetCurrentSourceDirectory(); - for (std::vector<std::string>::iterator it = uicSearchPaths.begin(); - it != uicSearchPaths.end(); ++it) { - *it = cmSystemTools::CollapseFullPath(*it, srcDir); + { + 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 uicOpts; - UicGetOpts(target, config, uicOpts); + std::string const uicOpts = UicGetOpts(this->ConfigDefault); AddDefinitionEscaped(makefile, "_uic_target_options", uicOpts); // Configuration specific settings - for (std::vector<std::string>::const_iterator li = configs.begin(); - li != configs.end(); ++li) { - std::string configUicOpts; - UicGetOpts(target, *li, configUicOpts); + for (std::string const& cfg : this->ConfigsList) { + std::string const configUicOpts = UicGetOpts(cfg); if (configUicOpts != uicOpts) { - configUicOptions[*li] = - cmOutputConverter::EscapeForCMake(configUicOpts); + this->ConfigUicOptions[cfg] = configUicOpts; } } } - // Uic files options + // .ui files skip and options { std::vector<std::string> uiFileFiles; - std::vector<std::string> uiFileOptions; + std::vector<std::vector<std::string>> uiFileOptions; { - std::set<std::string> skipped; - skipped.insert(uicSkipList.begin(), uicSkipList.end()); - - const std::vector<cmSourceFile*> uiFilesWithOptions = - makefile->GetQtUiFilesWithOptions(); - for (std::vector<cmSourceFile*>::const_iterator fileIt = - uiFilesWithOptions.begin(); - fileIt != uiFilesWithOptions.end(); ++fileIt) { - cmSourceFile* sf = *fileIt; - const std::string absFile = - cmsys::SystemTools::GetRealPath(sf->GetFullPath()); - if (skipped.insert(absFile).second) { - // The file wasn't skipped - uiFileFiles.push_back(absFile); - { - std::string opts = sf->GetProperty("AUTOUIC_OPTIONS"); - cmSystemTools::ReplaceString(opts, ";", - cmQtAutoGeneratorCommon::listSep); - uiFileOptions.push_back(opts); + std::string const uiExt = "ui"; + for (cmSourceFile* sf : makefile->GetSourceFiles()) { + // sf->GetExtension() is only valid after sf->GetFullPath() ... + std::string const& fPath = sf->GetFullPath(); + 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)); + } } } } @@ -508,589 +1217,130 @@ static void UicSetupAutoTarget( AddDefinitionEscaped(makefile, "_qt_uic_options_options", uiFileOptions); } + AddDefinitionEscaped(makefile, "_uic_skip", this->UicSkip); + // Uic executable { std::string err; - const char* uicExec = CM_NULLPTR; - if (qtMajorVersion == "5") { - cmGeneratorTarget* qt5Uic = lg->FindGeneratorTargetToUse("Qt5::uic"); - if (qt5Uic != CM_NULLPTR) { - uicExec = qt5Uic->ImportedGetLocation(""); + 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 (qtMajorVersion == "4") { - cmGeneratorTarget* qt4Uic = lg->FindGeneratorTargetToUse("Qt4::uic"); - if (qt4Uic != CM_NULLPTR) { - uicExec = qt4Uic->ImportedGetLocation(""); + } else if (this->QtVersionMajor == "4") { + cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::uic"); + if (tgt != nullptr) { + uicExec = SafeString(tgt->ImportedGetLocation("")); } else { - err = "Qt4::uic target not found " + target->GetName(); + err = "AUTOUIC: Qt4::uic target not found"; } } else { - err = "The CMAKE_AUTOUIC feature supports only Qt 4 and Qt 5 "; - err += target->GetName(); + err = "The AUTOUIC feature supports only Qt 4 and Qt 5"; } - // Add definition or error + if (err.empty()) { - AddDefinitionEscaped(makefile, "_qt_uic_executable", - uicExec ? uicExec : ""); + AddDefinitionEscaped(makefile, "_qt_uic_executable", uicExec); } else { + err += " ("; + err += this->Target->GetName(); + err += ")"; cmSystemTools::Error(err.c_str()); } } } -static std::string RccGetExecutable(cmGeneratorTarget const* target, - const std::string& qtMajorVersion) +std::vector<std::string> cmQtAutoGeneratorInitializer::AddGeneratedSource( + std::string const& filename, cmQtAutoGen::Generator genType) { - std::string rccExec; - cmLocalGenerator* lg = target->GetLocalGenerator(); - if (qtMajorVersion == "5") { - cmGeneratorTarget* qt5Rcc = lg->FindGeneratorTargetToUse("Qt5::rcc"); - if (qt5Rcc != CM_NULLPTR) { - rccExec = qt5Rcc->ImportedGetLocation(""); - } else { - cmSystemTools::Error("Qt5::rcc target not found ", - target->GetName().c_str()); - } - } else if (qtMajorVersion == "4") { - cmGeneratorTarget* qt4Rcc = lg->FindGeneratorTargetToUse("Qt4::rcc"); - if (qt4Rcc != CM_NULLPTR) { - rccExec = qt4Rcc->ImportedGetLocation(""); - } else { - cmSystemTools::Error("Qt4::rcc target not found ", - target->GetName().c_str()); - } + std::vector<std::string> genFiles; + // Register source file in makefile and source group + if (this->MultiConfig != cmQtAutoGen::FULL) { + genFiles.push_back(filename); } else { - cmSystemTools::Error( - "The CMAKE_AUTORCC feature supports only Qt 4 and Qt 5 ", - target->GetName().c_str()); - } - return rccExec; -} - -static void RccMergeOptions(std::vector<std::string>& opts, - const std::vector<std::string>& fileOpts, - bool isQt5) -{ - static const char* valueOptions[] = { "name", "root", "compress", - "threshold" }; - std::vector<std::string> extraOpts; - for (std::vector<std::string>::const_iterator fit = fileOpts.begin(); - fit != fileOpts.end(); ++fit) { - std::vector<std::string>::iterator existingIt = - std::find(opts.begin(), opts.end(), *fit); - if (existingIt != opts.end()) { - const char* optName = fit->c_str(); - if (*optName == '-') { - ++optName; - if (isQt5 && *optName == '-') { - ++optName; - } - } - // Test if this is a value option and change the existing value - if ((optName != fit->c_str()) && - std::find_if(cmArrayBegin(valueOptions), cmArrayEnd(valueOptions), - cmStrCmp(optName)) != cmArrayEnd(valueOptions)) { - const std::vector<std::string>::iterator existValueIt(existingIt + 1); - const std::vector<std::string>::const_iterator fileValueIt(fit + 1); - if ((existValueIt != opts.end()) && (fileValueIt != fileOpts.end())) { - *existValueIt = *fileValueIt; - ++fit; - } - } - } else { - extraOpts.push_back(*fit); + for (std::string const& cfg : this->ConfigsList) { + genFiles.push_back( + cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg)); } } - opts.insert(opts.end(), extraOpts.begin(), extraOpts.end()); -} - -static void RccSetupAutoTarget(cmGeneratorTarget const* target, - const std::string& qtMajorVersion) -{ - cmMakefile* makefile = target->Target->GetMakefile(); - const bool qtMajorVersion5 = (qtMajorVersion == "5"); - const std::string rccCommand = RccGetExecutable(target, qtMajorVersion); - std::vector<std::string> _rcc_files; - std::vector<std::string> _rcc_inputs; - std::vector<std::string> rccFileFiles; - std::vector<std::string> rccFileOptions; - std::vector<std::string> rccOptionsTarget; - if (const char* opts = target->GetProperty("AUTORCC_OPTIONS")) { - cmSystemTools::ExpandListArgument(opts, rccOptionsTarget); - } - - std::vector<cmSourceFile*> srcFiles; - target->GetConfigCommonSourceFiles(srcFiles); - for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin(); - fileIt != srcFiles.end(); ++fileIt) { - cmSourceFile* sf = *fileIt; - if ((sf->GetExtension() == "qrc") && - !PropertyEnabled(sf, "SKIP_AUTOGEN") && - !PropertyEnabled(sf, "SKIP_AUTORCC")) { - const std::string absFile = - cmsys::SystemTools::GetRealPath(sf->GetFullPath()); - // qrc file - _rcc_files.push_back(absFile); - // qrc file entries - { - std::string entriesList = "{"; - // Read input file list only for non generated .qrc files. - if (!PropertyEnabled(sf, "GENERATED")) { - std::string error; - std::vector<std::string> files; - if (cmQtAutoGeneratorCommon::RccListInputs( - qtMajorVersion, rccCommand, absFile, files, &error)) { - entriesList += cmJoin(files, cmQtAutoGeneratorCommon::listSep); - } else { - cmSystemTools::Error(error.c_str()); - } - } - entriesList += "}"; - _rcc_inputs.push_back(entriesList); - } - // rcc options for this qrc file + { + cmMakefile* makefile = this->Target->Target->GetMakefile(); + for (std::string const& genFile : genFiles) { { - // Merged target and file options - std::vector<std::string> rccOptions(rccOptionsTarget); - if (const char* prop = sf->GetProperty("AUTORCC_OPTIONS")) { - std::vector<std::string> optsVec; - cmSystemTools::ExpandListArgument(prop, optsVec); - RccMergeOptions(rccOptions, optsVec, qtMajorVersion5); - } - // Only store non empty options lists - if (!rccOptions.empty()) { - rccFileFiles.push_back(absFile); - rccFileOptions.push_back( - cmJoin(rccOptions, cmQtAutoGeneratorCommon::listSep)); - } + cmSourceFile* gFile = makefile->GetOrCreateSource(genFile, true); + gFile->SetProperty("GENERATED", "1"); + gFile->SetProperty("SKIP_AUTOGEN", "On"); } + AddToSourceGroup(makefile, genFile, genType); } } - AddDefinitionEscaped(makefile, "_qt_rcc_executable", rccCommand); - AddDefinitionEscaped(makefile, "_rcc_files", _rcc_files); - AddDefinitionEscaped(makefile, "_rcc_inputs", _rcc_inputs); - AddDefinitionEscaped(makefile, "_rcc_options_files", rccFileFiles); - AddDefinitionEscaped(makefile, "_rcc_options_options", rccFileOptions); -} - -void cmQtAutoGeneratorInitializer::InitializeAutogenSources( - cmGeneratorTarget* target) -{ - if (target->GetPropertyAsBool("AUTOMOC")) { - cmMakefile* makefile = target->Target->GetMakefile(); - const std::vector<std::string> suffixes = - GetConfigurationSuffixes(makefile); - // Get build directory - const std::string autogenBuildDir = GetAutogenTargetBuildDir(target); - // Register all compilation files as generated - for (std::vector<std::string>::const_iterator it = suffixes.begin(); - it != suffixes.end(); ++it) { - std::string mcFile = autogenBuildDir + "/mocs_compilation"; - mcFile += *it; - mcFile += ".cpp"; - AddGeneratedSource(makefile, mcFile, cmQtAutoGeneratorCommon::MOC); - } - // Mocs compilation file - if (IsMultiConfig(target->GetGlobalGenerator())) { - target->AddSource(autogenBuildDir + "/mocs_compilation_$<CONFIG>.cpp"); - } else { - target->AddSource(autogenBuildDir + "/mocs_compilation.cpp"); + // 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; } -void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( - cmLocalGenerator* lg, cmGeneratorTarget* target) +std::string cmQtAutoGeneratorInitializer::GetQtMajorVersion( + cmGeneratorTarget const* target) { cmMakefile* makefile = target->Target->GetMakefile(); - - // Create a custom target for running generators at buildtime - const bool mocEnabled = target->GetPropertyAsBool("AUTOMOC"); - const bool uicEnabled = target->GetPropertyAsBool("AUTOUIC"); - const bool rccEnabled = target->GetPropertyAsBool("AUTORCC"); - const bool multiConfig = IsMultiConfig(target->GetGlobalGenerator()); - const std::string autogenTargetName = GetAutogenTargetName(target); - const std::string autogenBuildDir = GetAutogenTargetBuildDir(target); - const std::string workingDirectory = - cmSystemTools::CollapseFullPath("", makefile->GetCurrentBinaryDirectory()); - const std::string qtMajorVersion = GetQtMajorVersion(target); - const std::string rccCommand = RccGetExecutable(target, qtMajorVersion); - const std::vector<std::string> suffixes = GetConfigurationSuffixes(makefile); - std::vector<std::string> autogenDepends; - std::vector<std::string> autogenProvides; - - // Remove build directories on cleanup - makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", - autogenBuildDir.c_str(), false); - - // Remove old settings on cleanup - { - std::string base = GetAutogenTargetFilesDir(target); - for (std::vector<std::string>::const_iterator it = suffixes.begin(); - it != suffixes.end(); ++it) { - std::string fname = base + "/AutogenOldSettings" + *it + ".cmake"; - makefile->AppendProperty("ADDITIONAL_MAKE_CLEAN_FILES", fname.c_str(), - false); - } - } - - // Compose command lines - cmCustomCommandLines commandLines; - { - cmCustomCommandLine currentLine; - currentLine.push_back(cmSystemTools::GetCMakeCommand()); - currentLine.push_back("-E"); - currentLine.push_back("cmake_autogen"); - currentLine.push_back(GetAutogenTargetFilesDir(target)); - currentLine.push_back("$<CONFIGURATION>"); - commandLines.push_back(currentLine); + std::string qtMajor = makefile->GetSafeDefinition("QT_VERSION_MAJOR"); + if (qtMajor.empty()) { + qtMajor = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR"); } - - // Compose target comment - std::string autogenComment; - { - std::vector<std::string> toolNames; - if (mocEnabled) { - toolNames.push_back("MOC"); - } - if (uicEnabled) { - toolNames.push_back("UIC"); - } - if (rccEnabled) { - toolNames.push_back("RCC"); - } - - std::string tools = toolNames[0]; - toolNames.erase(toolNames.begin()); - while (toolNames.size() > 1) { - tools += ", " + toolNames[0]; - toolNames.erase(toolNames.begin()); - } - if (toolNames.size() == 1) { - tools += " and " + toolNames[0]; - } - autogenComment = "Automatic " + tools + " for target " + target->GetName(); - } - - // Add moc compilation to generated files list - if (mocEnabled) { - for (std::vector<std::string>::const_iterator it = suffixes.begin(); - it != suffixes.end(); ++it) { - std::string mcFile = autogenBuildDir + "/mocs_compilation"; - mcFile += *it; - mcFile += ".cpp"; - autogenProvides.push_back(mcFile); - } - } - - // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES - if (mocEnabled || uicEnabled) { - if (multiConfig) { - target->AddIncludeDirectory(autogenBuildDir + "/include_$<CONFIG>", - true); - - } else { - target->AddIncludeDirectory(autogenBuildDir + "/include", true); - } - } - -#if defined(_WIN32) && !defined(__CYGWIN__) - bool usePRE_BUILD = false; - cmGlobalGenerator* gg = lg->GetGlobalGenerator(); - if (gg->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; - } -#endif - - // Initialize autogen target dependencies - if (const char* deps = target->GetProperty("AUTOGEN_TARGET_DEPENDS")) { - cmSystemTools::ExpandListArgument(deps, autogenDepends); - } - // Add link library targets to the autogen dependencies - { - const cmTarget::LinkLibraryVectorType& libVec = - target->Target->GetOriginalLinkLibraries(); - for (cmTarget::LinkLibraryVectorType::const_iterator it = libVec.begin(); - it != libVec.end(); ++it) { - const std::string& libName = it->first; - if (makefile->FindTargetToUse(libName) != CM_NULLPTR) { - autogenDepends.push_back(libName); - } - } - } - { - cmFilePathChecksum fpathCheckSum(makefile); - // Iterate over all source files - std::vector<cmSourceFile*> srcFiles; - target->GetConfigCommonSourceFiles(srcFiles); - for (std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin(); - fileIt != srcFiles.end(); ++fileIt) { - cmSourceFile* sf = *fileIt; - if (!PropertyEnabled(sf, "SKIP_AUTOGEN")) { - std::string const& ext = sf->GetExtension(); - // Add generated file that will be scanned by moc or uic to - // the dependencies - if (mocEnabled || uicEnabled) { - const cmSystemTools::FileFormat fileType = - cmSystemTools::GetFileFormat(ext.c_str()); - if ((fileType == cmSystemTools::CXX_FILE_FORMAT) || - (fileType == cmSystemTools::HEADER_FILE_FORMAT)) { - if (PropertyEnabled(sf, "GENERATED")) { - if ((mocEnabled && !PropertyEnabled(sf, "SKIP_AUTOMOC")) || - (uicEnabled && !PropertyEnabled(sf, "SKIP_AUTOUIC"))) { - autogenDepends.push_back( - cmsys::SystemTools::GetRealPath(sf->GetFullPath())); -#if defined(_WIN32) && !defined(__CYGWIN__) - // Cannot use PRE_BUILD with generated files - usePRE_BUILD = false; -#endif - } - } - } - } - // Process rcc enabled files - if (rccEnabled && (ext == "qrc") && - !PropertyEnabled(sf, "SKIP_AUTORCC")) { - const std::string absFile = - cmsys::SystemTools::GetRealPath(sf->GetFullPath()); - - // Compose rcc output file name - { - std::string rccOutBase = autogenBuildDir + "/"; - rccOutBase += fpathCheckSum.getPart(absFile); - rccOutBase += "/qrc_"; - rccOutBase += - cmsys::SystemTools::GetFilenameWithoutLastExtension(absFile); - - // Register rcc ouput file as generated - for (std::vector<std::string>::const_iterator it = - suffixes.begin(); - it != suffixes.end(); ++it) { - std::string rccOutCfg = rccOutBase; - rccOutCfg += *it; - rccOutCfg += ".cpp"; - AddGeneratedSource(makefile, rccOutCfg, - cmQtAutoGeneratorCommon::RCC); - autogenProvides.push_back(rccOutCfg); - } - // Add rcc output file to origin target sources - if (multiConfig) { - target->AddSource(rccOutBase + "_$<CONFIG>.cpp"); - } else { - target->AddSource(rccOutBase + ".cpp"); - } - } - - if (PropertyEnabled(sf, "GENERATED")) { - // Add generated qrc file to the dependencies - autogenDepends.push_back(absFile); - } else { - // Run cmake again when .qrc file changes - makefile->AddCMakeDependFile(absFile); - - // Add the qrc input files to the dependencies - std::string error; - if (!cmQtAutoGeneratorCommon::RccListInputs( - qtMajorVersion, rccCommand, absFile, autogenDepends, - &error)) { - cmSystemTools::Error(error.c_str()); - } - } -#if defined(_WIN32) && !defined(__CYGWIN__) - // Cannot use PRE_BUILD because the resource files themselves - // may not be sources within the target so VS may not know the - // target needs to re-build at all. - usePRE_BUILD = false; -#endif - } - } - } - } - -#if defined(_WIN32) && !defined(__CYGWIN__) - if (usePRE_BUILD) { - // If the autogen target depends on an other target don't use PRE_BUILD - for (std::vector<std::string>::iterator it = autogenDepends.begin(); - it != autogenDepends.end(); ++it) { - if (makefile->FindTargetToUse(*it) != CM_NULLPTR) { - usePRE_BUILD = false; - break; - } - } - } - if (usePRE_BUILD) { - // 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. - std::vector<std::string> no_output; - cmCustomCommand cc(makefile, no_output, autogenProvides, autogenDepends, - commandLines, autogenComment.c_str(), - workingDirectory.c_str()); - cc.SetEscapeOldStyle(false); - cc.SetEscapeAllowMakeVars(true); - target->Target->AddPreBuildCommand(cc); - } else -#endif - { - cmTarget* autogenTarget = makefile->AddUtilityCommand( - autogenTargetName, true, workingDirectory.c_str(), - /*byproducts=*/autogenProvides, autogenDepends, commandLines, false, - autogenComment.c_str()); - - cmGeneratorTarget* gt = new cmGeneratorTarget(autogenTarget, lg); - lg->AddGeneratorTarget(gt); - - // Set target folder - const char* autogenFolder = - makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER"); - if (!autogenFolder) { - autogenFolder = - makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER"); - } - if (autogenFolder && *autogenFolder) { - autogenTarget->SetProperty("FOLDER", autogenFolder); - } else { - // inherit FOLDER property from target (#13688) - utilCopyTargetProperty(gt->Target, target->Target, "FOLDER"); - } - - target->Target->AddUtility(autogenTargetName); + const char* targetQtVersion = + target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", ""); + if (targetQtVersion != nullptr) { + qtMajor = targetQtVersion; } + return qtMajor; } -void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( - cmGeneratorTarget const* target) +std::string cmQtAutoGeneratorInitializer::GetQtMinorVersion( + cmGeneratorTarget const* target, std::string const& qtVersionMajor) { cmMakefile* makefile = target->Target->GetMakefile(); - - // forget the variables added here afterwards again: - cmMakefile::ScopePushPop varScope(makefile); - static_cast<void>(varScope); - - // Get configurations - std::string config; - const std::vector<std::string> configs(GetConfigurations(makefile, &config)); - - // Configurations settings buffers - std::map<std::string, std::string> configSuffix; - std::map<std::string, std::string> configMocIncludes; - std::map<std::string, std::string> configMocDefines; - std::map<std::string, std::string> configUicOptions; - - // Configuration suffix - if (IsMultiConfig(target->GetGlobalGenerator())) { - for (std::vector<std::string>::const_iterator it = configs.begin(); - it != configs.end(); ++it) { - configSuffix[*it] = "_" + *it; - } + std::string qtMinor; + if (qtVersionMajor == "5") { + qtMinor = makefile->GetSafeDefinition("Qt5Core_VERSION_MINOR"); } - - // Basic setup - { - const bool mocEnabled = target->GetPropertyAsBool("AUTOMOC"); - const bool uicEnabled = target->GetPropertyAsBool("AUTOUIC"); - const bool rccEnabled = target->GetPropertyAsBool("AUTORCC"); - const std::string autogenTargetName = GetAutogenTargetName(target); - const std::string qtMajorVersion = GetQtMajorVersion(target); - - std::vector<std::string> sources; - std::vector<std::string> headers; - - if (mocEnabled || uicEnabled || rccEnabled) { - std::vector<std::string> mocSkipList; - std::vector<std::string> uicSkipList; - AcquireScanFiles(target, sources, headers, mocSkipList, uicSkipList); - if (mocEnabled) { - MocSetupAutoTarget(target, autogenTargetName, qtMajorVersion, config, - configs, mocSkipList, configMocIncludes, - configMocDefines); - } - if (uicEnabled) { - UicSetupAutoTarget(target, qtMajorVersion, config, configs, - uicSkipList, configUicOptions); - } - if (rccEnabled) { - RccSetupAutoTarget(target, qtMajorVersion); - } - } - - AddDefinitionEscaped(makefile, "_autogen_build_dir", - GetAutogenTargetBuildDir(target)); - AddDefinitionEscaped(makefile, "_qt_version_major", qtMajorVersion); - AddDefinitionEscaped(makefile, "_sources", sources); - AddDefinitionEscaped(makefile, "_headers", headers); + if (qtMinor.empty()) { + qtMinor = makefile->GetSafeDefinition("QT_VERSION_MINOR"); } - // Generate info file - std::string infoFile = GetAutogenTargetFilesDir(target); - infoFile += "/AutogenInfo.cmake"; - { - std::string inf = cmSystemTools::GetCMakeRoot(); - inf += "/Modules/AutogenInfo.cmake.in"; - makefile->ConfigureFile(inf.c_str(), infoFile.c_str(), false, true, false); + const char* targetQtVersion = + target->GetLinkInterfaceDependentStringProperty("QT_MINOR_VERSION", ""); + if (targetQtVersion != nullptr) { + qtMinor = targetQtVersion; } + return qtMinor; +} - // Append custom definitions to info file on demand - if (!configSuffix.empty() || !configMocDefines.empty() || - !configMocIncludes.empty() || !configUicOptions.empty()) { - - // Ensure we have write permission in case .in was read-only. - 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(infoFile, perm); - if (!(perm & mode_write)) { - cmSystemTools::SetPermissions(infoFile, perm | mode_write); - } - - // Open and write file - cmsys::ofstream ofs(infoFile.c_str(), std::ios::app); - if (ofs) { - ofs << "# Configuration specific options\n"; - for (std::map<std::string, std::string>::iterator - it = configSuffix.begin(), - end = configSuffix.end(); - it != end; ++it) { - ofs << "set(AM_CONFIG_SUFFIX_" << it->first << " " << it->second - << ")\n"; - } - for (std::map<std::string, std::string>::iterator - it = configMocDefines.begin(), - end = configMocDefines.end(); - it != end; ++it) { - ofs << "set(AM_MOC_DEFINITIONS_" << it->first << " " << it->second - << ")\n"; - } - for (std::map<std::string, std::string>::iterator - it = configMocIncludes.begin(), - end = configMocIncludes.end(); - it != end; ++it) { - ofs << "set(AM_MOC_INCLUDES_" << it->first << " " << it->second - << ")\n"; - } - for (std::map<std::string, std::string>::iterator - it = configUicOptions.begin(), - end = configUicOptions.end(); - it != end; ++it) { - ofs << "set(AM_UIC_TARGET_OPTIONS_" << it->first << " " << it->second - << ")\n"; - } - } else { - // File open error - std::string error = "Internal CMake error when trying to open file: "; - error += cmQtAutoGeneratorCommon::Quoted(infoFile); - error += " for writing."; - cmSystemTools::Error(error.c_str()); - } +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; } |