| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| #include "cmQtAutoGenInitializer.h" |
| |
| #include <cstddef> |
| #include <deque> |
| #include <initializer_list> |
| #include <map> |
| #include <set> |
| #include <sstream> // for basic_ios, istringstream |
| #include <string> |
| #include <unordered_set> |
| #include <utility> |
| #include <vector> |
| |
| #include <cm/algorithm> |
| #include <cm/iterator> |
| #include <cm/memory> |
| #include <cmext/algorithm> |
| #include <cmext/string_view> |
| |
| #include <cm3p/json/value.h> |
| #include <cm3p/json/writer.h> |
| |
| #include "cmsys/SystemInformation.hxx" |
| |
| #include "cmCustomCommand.h" |
| #include "cmCustomCommandLines.h" |
| #include "cmGeneratedFileStream.h" |
| #include "cmGeneratorExpression.h" |
| #include "cmGeneratorTarget.h" |
| #include "cmGlobalGenerator.h" |
| #include "cmLinkItem.h" |
| #include "cmListFileCache.h" |
| #include "cmLocalGenerator.h" |
| #include "cmMakefile.h" |
| #include "cmMessageType.h" |
| #include "cmPolicies.h" |
| #include "cmProperty.h" |
| #include "cmQtAutoGen.h" |
| #include "cmQtAutoGenGlobalInitializer.h" |
| #include "cmSourceFile.h" |
| #include "cmSourceFileLocationKind.h" |
| #include "cmSourceGroup.h" |
| #include "cmState.h" |
| #include "cmStateTypes.h" |
| #include "cmStringAlgorithms.h" |
| #include "cmSystemTools.h" |
| #include "cmTarget.h" |
| #include "cmake.h" |
| |
| namespace { |
| |
| unsigned int GetParallelCPUCount() |
| { |
| static unsigned int count = 0; |
| // Detect only on the first call |
| if (count == 0) { |
| cmsys::SystemInformation info; |
| info.RunCPUCheck(); |
| count = |
| cm::clamp(info.GetNumberOfPhysicalCPU(), 1u, cmQtAutoGen::ParallelMax); |
| } |
| return count; |
| } |
| |
| std::string FileProjectRelativePath(cmMakefile* makefile, |
| std::string const& fileName) |
| { |
| std::string res; |
| { |
| std::string pSource = cmSystemTools::RelativePath( |
| makefile->GetCurrentSourceDirectory(), fileName); |
| std::string pBinary = cmSystemTools::RelativePath( |
| makefile->GetCurrentBinaryDirectory(), fileName); |
| if (pSource.size() < pBinary.size()) { |
| res = std::move(pSource); |
| } else if (pBinary.size() < fileName.size()) { |
| res = std::move(pBinary); |
| } else { |
| res = fileName; |
| } |
| } |
| return res; |
| } |
| |
| /** |
| * Tests if targetDepend is a STATIC_LIBRARY and if any of its |
| * recursive STATIC_LIBRARY dependencies depends on targetOrigin |
| * (STATIC_LIBRARY cycle). |
| */ |
| bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin, |
| cmGeneratorTarget const* targetDepend, |
| std::string const& config) |
| { |
| bool cycle = false; |
| if ((targetOrigin->GetType() == cmStateEnums::STATIC_LIBRARY) && |
| (targetDepend->GetType() == cmStateEnums::STATIC_LIBRARY)) { |
| std::set<cmGeneratorTarget const*> knownLibs; |
| std::deque<cmGeneratorTarget const*> testLibs; |
| |
| // Insert initial static_library dependency |
| knownLibs.insert(targetDepend); |
| testLibs.push_back(targetDepend); |
| |
| while (!testLibs.empty()) { |
| cmGeneratorTarget const* testTarget = testLibs.front(); |
| testLibs.pop_front(); |
| // Check if the test target is the origin target (cycle) |
| if (testTarget == targetOrigin) { |
| cycle = true; |
| break; |
| } |
| // Collect all static_library dependencies from the test target |
| cmLinkImplementationLibraries const* libs = |
| testTarget->GetLinkImplementationLibraries(config); |
| if (libs) { |
| for (cmLinkItem const& item : libs->Libraries) { |
| cmGeneratorTarget const* depTarget = item.Target; |
| if (depTarget && |
| (depTarget->GetType() == cmStateEnums::STATIC_LIBRARY) && |
| knownLibs.insert(depTarget).second) { |
| testLibs.push_back(depTarget); |
| } |
| } |
| } |
| } |
| } |
| return cycle; |
| } |
| |
| /** Sanitizes file search paths. */ |
| class SearchPathSanitizer |
| { |
| public: |
| SearchPathSanitizer(cmMakefile* makefile) |
| : SourcePath_(makefile->GetCurrentSourceDirectory()) |
| { |
| } |
| std::vector<std::string> operator()( |
| std::vector<std::string> const& paths) const; |
| |
| private: |
| std::string SourcePath_; |
| }; |
| |
| std::vector<std::string> SearchPathSanitizer::operator()( |
| std::vector<std::string> const& paths) const |
| { |
| std::vector<std::string> res; |
| res.reserve(paths.size()); |
| for (std::string const& srcPath : paths) { |
| // Collapse relative paths |
| std::string path = |
| cmSystemTools::CollapseFullPath(srcPath, this->SourcePath_); |
| // Remove suffix slashes |
| while (cmHasSuffix(path, '/')) { |
| path.pop_back(); |
| } |
| // Accept only non empty paths |
| if (!path.empty()) { |
| res.emplace_back(std::move(path)); |
| } |
| } |
| return res; |
| } |
| |
| /** @brief Writes a CMake info file. */ |
| class InfoWriter |
| { |
| public: |
| // -- Single value |
| void Set(std::string const& key, std::string const& value) |
| { |
| this->Value_[key] = value; |
| } |
| void SetConfig(std::string const& key, |
| cmQtAutoGenInitializer::ConfigString const& cfgStr); |
| void SetBool(std::string const& key, bool value) |
| { |
| this->Value_[key] = value; |
| } |
| void SetUInt(std::string const& key, unsigned int value) |
| { |
| this->Value_[key] = value; |
| } |
| |
| // -- Array utility |
| template <typename CONT> |
| static bool MakeArray(Json::Value& jval, CONT const& container); |
| |
| template <typename CONT> |
| static void MakeStringArray(Json::Value& jval, CONT const& container); |
| |
| // -- Array value |
| template <typename CONT> |
| void SetArray(std::string const& key, CONT const& container); |
| template <typename CONT> |
| void SetConfigArray( |
| std::string const& key, |
| cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr); |
| |
| // -- Array of arrays |
| template <typename CONT, typename FUNC> |
| void SetArrayArray(std::string const& key, CONT const& container, FUNC func); |
| |
| // -- Save to json file |
| bool Save(std::string const& filename); |
| |
| private: |
| Json::Value Value_; |
| }; |
| |
| void InfoWriter::SetConfig(std::string const& key, |
| cmQtAutoGenInitializer::ConfigString const& cfgStr) |
| { |
| this->Set(key, cfgStr.Default); |
| for (auto const& item : cfgStr.Config) { |
| this->Set(cmStrCat(key, '_', item.first), item.second); |
| } |
| } |
| |
| template <typename CONT> |
| bool InfoWriter::MakeArray(Json::Value& jval, CONT const& container) |
| { |
| jval = Json::arrayValue; |
| std::size_t const listSize = cm::size(container); |
| if (listSize == 0) { |
| return false; |
| } |
| jval.resize(static_cast<unsigned int>(listSize)); |
| return true; |
| } |
| |
| template <typename CONT> |
| void InfoWriter::MakeStringArray(Json::Value& jval, CONT const& container) |
| { |
| if (MakeArray(jval, container)) { |
| Json::ArrayIndex ii = 0; |
| for (std::string const& item : container) { |
| jval[ii++] = item; |
| } |
| } |
| } |
| |
| template <typename CONT> |
| void InfoWriter::SetArray(std::string const& key, CONT const& container) |
| { |
| MakeStringArray(this->Value_[key], container); |
| } |
| |
| template <typename CONT, typename FUNC> |
| void InfoWriter::SetArrayArray(std::string const& key, CONT const& container, |
| FUNC func) |
| { |
| Json::Value& jval = this->Value_[key]; |
| if (MakeArray(jval, container)) { |
| Json::ArrayIndex ii = 0; |
| for (auto const& citem : container) { |
| Json::Value& aval = jval[ii++]; |
| aval = Json::arrayValue; |
| func(aval, citem); |
| } |
| } |
| } |
| |
| template <typename CONT> |
| void InfoWriter::SetConfigArray( |
| std::string const& key, |
| cmQtAutoGenInitializer::ConfigStrings<CONT> const& cfgStr) |
| { |
| this->SetArray(key, cfgStr.Default); |
| for (auto const& item : cfgStr.Config) { |
| this->SetArray(cmStrCat(key, '_', item.first), item.second); |
| } |
| } |
| |
| bool InfoWriter::Save(std::string const& filename) |
| { |
| cmGeneratedFileStream fileStream; |
| fileStream.SetCopyIfDifferent(true); |
| fileStream.Open(filename, false, true); |
| if (!fileStream) { |
| return false; |
| } |
| |
| Json::StyledStreamWriter jsonWriter; |
| try { |
| jsonWriter.write(fileStream, this->Value_); |
| } catch (...) { |
| return false; |
| } |
| |
| return fileStream.Close(); |
| } |
| |
| void AddAutogenExecutableToDependencies( |
| cmQtAutoGenInitializer::GenVarsT const& genVars, |
| std::vector<std::string>& dependencies) |
| { |
| if (genVars.ExecutableTarget != nullptr) { |
| dependencies.push_back(genVars.ExecutableTarget->Target->GetName()); |
| } else if (!genVars.Executable.empty()) { |
| dependencies.push_back(genVars.Executable); |
| } |
| } |
| |
| } // End of unnamed namespace |
| |
| cmQtAutoGenInitializer::cmQtAutoGenInitializer( |
| cmQtAutoGenGlobalInitializer* globalInitializer, |
| cmGeneratorTarget* genTarget, IntegerVersion const& qtVersion, |
| bool mocEnabled, bool uicEnabled, bool rccEnabled, bool globalAutogenTarget, |
| bool globalAutoRccTarget) |
| : GlobalInitializer(globalInitializer) |
| , GenTarget(genTarget) |
| , GlobalGen(genTarget->GetGlobalGenerator()) |
| , LocalGen(genTarget->GetLocalGenerator()) |
| , Makefile(genTarget->Makefile) |
| , PathCheckSum(genTarget->Makefile) |
| , QtVersion(qtVersion) |
| { |
| this->AutogenTarget.GlobalTarget = globalAutogenTarget; |
| this->Moc.Enabled = mocEnabled; |
| this->Uic.Enabled = uicEnabled; |
| this->Rcc.Enabled = rccEnabled; |
| this->Rcc.GlobalTarget = globalAutoRccTarget; |
| } |
| |
| bool cmQtAutoGenInitializer::InitCustomTargets() |
| { |
| // Configurations |
| this->MultiConfig = this->GlobalGen->IsMultiConfig(); |
| this->ConfigDefault = this->Makefile->GetDefaultConfiguration(); |
| this->ConfigsList = |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); |
| |
| // Verbosity |
| { |
| std::string def = |
| this->Makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE"); |
| if (!def.empty()) { |
| unsigned long iVerb = 0; |
| if (cmStrToULong(def, &iVerb)) { |
| // Numeric verbosity |
| this->Verbosity = static_cast<unsigned int>(iVerb); |
| } else { |
| // Non numeric verbosity |
| if (cmIsOn(def)) { |
| this->Verbosity = 1; |
| } |
| } |
| } |
| } |
| |
| // Targets FOLDER |
| { |
| cmProp folder = |
| this->Makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER"); |
| if (!folder) { |
| folder = this->Makefile->GetState()->GetGlobalProperty( |
| "AUTOGEN_TARGETS_FOLDER"); |
| } |
| // Inherit FOLDER property from target (#13688) |
| if (!folder) { |
| folder = this->GenTarget->GetProperty("FOLDER"); |
| } |
| if (folder) { |
| this->TargetsFolder = *folder; |
| } |
| } |
| |
| // Check status of policy CMP0071 regarding handling of GENERATED files |
| switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0071)) { |
| case cmPolicies::WARN: |
| // Ignore GENERATED files but warn |
| this->CMP0071Warn = true; |
| CM_FALLTHROUGH; |
| case cmPolicies::OLD: |
| // Ignore GENERATED files |
| break; |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: |
| case cmPolicies::NEW: |
| // Process GENERATED files |
| this->CMP0071Accept = true; |
| break; |
| } |
| |
| // Check status of policy CMP0100 regarding handling of .hh headers |
| switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0100)) { |
| case cmPolicies::WARN: |
| // Ignore but .hh files but warn |
| this->CMP0100Warn = true; |
| CM_FALLTHROUGH; |
| case cmPolicies::OLD: |
| // Ignore .hh files |
| break; |
| case cmPolicies::REQUIRED_IF_USED: |
| case cmPolicies::REQUIRED_ALWAYS: |
| case cmPolicies::NEW: |
| // Process .hh file |
| this->CMP0100Accept = true; |
| break; |
| } |
| |
| // Common directories |
| { |
| // Collapsed current binary directory |
| std::string const cbd = cmSystemTools::CollapseFullPath( |
| std::string(), this->Makefile->GetCurrentBinaryDirectory()); |
| |
| // Info directory |
| this->Dir.Info = cmStrCat(cbd, "/CMakeFiles/", this->GenTarget->GetName(), |
| "_autogen.dir"); |
| cmSystemTools::ConvertToUnixSlashes(this->Dir.Info); |
| |
| // Build directory |
| this->Dir.Build = this->GenTarget->GetSafeProperty("AUTOGEN_BUILD_DIR"); |
| if (this->Dir.Build.empty()) { |
| this->Dir.Build = |
| cmStrCat(cbd, '/', this->GenTarget->GetName(), "_autogen"); |
| } |
| cmSystemTools::ConvertToUnixSlashes(this->Dir.Build); |
| // Cleanup build directory |
| this->AddCleanFile(this->Dir.Build); |
| |
| // Working directory |
| this->Dir.Work = cbd; |
| cmSystemTools::ConvertToUnixSlashes(this->Dir.Work); |
| |
| // Include directory |
| this->ConfigFileNamesAndGenex(this->Dir.Include, this->Dir.IncludeGenExp, |
| cmStrCat(this->Dir.Build, "/include"), ""); |
| } |
| |
| // Moc, Uic and _autogen target settings |
| if (this->MocOrUicEnabled()) { |
| // Init moc specific settings |
| if (this->Moc.Enabled && !this->InitMoc()) { |
| return false; |
| } |
| |
| // Init uic specific settings |
| if (this->Uic.Enabled && !this->InitUic()) { |
| return false; |
| } |
| |
| // Autogen target name |
| this->AutogenTarget.Name = |
| cmStrCat(this->GenTarget->GetName(), "_autogen"); |
| |
| // Autogen target parallel processing |
| { |
| std::string const& prop = |
| this->GenTarget->GetSafeProperty("AUTOGEN_PARALLEL"); |
| if (prop.empty() || (prop == "AUTO")) { |
| // Autodetect number of CPUs |
| this->AutogenTarget.Parallel = GetParallelCPUCount(); |
| } else { |
| this->AutogenTarget.Parallel = 1; |
| } |
| } |
| |
| // Autogen target info and settings files |
| { |
| // Info file |
| this->AutogenTarget.InfoFile = |
| cmStrCat(this->Dir.Info, "/AutogenInfo.json"); |
| |
| // Used settings file |
| this->ConfigFileNames(this->AutogenTarget.SettingsFile, |
| cmStrCat(this->Dir.Info, "/AutogenUsed"), ".txt"); |
| this->ConfigFileClean(this->AutogenTarget.SettingsFile); |
| |
| // Parse cache file |
| this->ConfigFileNames(this->AutogenTarget.ParseCacheFile, |
| cmStrCat(this->Dir.Info, "/ParseCache"), ".txt"); |
| this->ConfigFileClean(this->AutogenTarget.ParseCacheFile); |
| } |
| |
| // Autogen target: Compute user defined dependencies |
| { |
| this->AutogenTarget.DependOrigin = |
| this->GenTarget->GetPropertyAsBool("AUTOGEN_ORIGIN_DEPENDS"); |
| |
| std::string const& deps = |
| this->GenTarget->GetSafeProperty("AUTOGEN_TARGET_DEPENDS"); |
| if (!deps.empty()) { |
| for (std::string const& depName : cmExpandedList(deps)) { |
| // Allow target and file dependencies |
| auto* depTarget = this->Makefile->FindTargetToUse(depName); |
| if (depTarget) { |
| this->AutogenTarget.DependTargets.insert(depTarget); |
| } else { |
| this->AutogenTarget.DependFiles.insert(depName); |
| } |
| } |
| } |
| } |
| |
| if (this->Moc.Enabled) { |
| // Path prefix |
| if (cmIsOn(this->GenTarget->GetProperty("AUTOMOC_PATH_PREFIX"))) { |
| this->Moc.PathPrefix = true; |
| } |
| |
| // CMAKE_AUTOMOC_RELAXED_MODE |
| if (this->Makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE")) { |
| this->Moc.RelaxedMode = true; |
| this->Makefile->IssueMessage( |
| MessageType::AUTHOR_WARNING, |
| cmStrCat("AUTOMOC: CMAKE_AUTOMOC_RELAXED_MODE is " |
| "deprecated an will be removed in the future. Consider " |
| "disabling it and converting the target ", |
| this->GenTarget->GetName(), " to regular mode.")); |
| } |
| |
| // Options |
| cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MOC_OPTIONS"), |
| this->Moc.Options); |
| // Filters |
| cmExpandList(this->GenTarget->GetSafeProperty("AUTOMOC_MACRO_NAMES"), |
| this->Moc.MacroNames); |
| { |
| auto filterList = cmExpandedList( |
| this->GenTarget->GetSafeProperty("AUTOMOC_DEPEND_FILTERS")); |
| if ((filterList.size() % 2) != 0) { |
| cmSystemTools::Error( |
| cmStrCat("AutoMoc: AUTOMOC_DEPEND_FILTERS predefs size ", |
| filterList.size(), " is not a multiple of 2.")); |
| return false; |
| } |
| this->Moc.DependFilters.reserve(1 + (filterList.size() / 2)); |
| this->Moc.DependFilters.emplace_back( |
| "Q_PLUGIN_METADATA", |
| "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" |
| "[^\\)]*FILE[ \t]*\"([^\"]+)\""); |
| for (std::size_t ii = 0; ii != filterList.size(); ii += 2) { |
| this->Moc.DependFilters.emplace_back(filterList[ii], |
| filterList[ii + 1]); |
| } |
| } |
| } |
| } |
| |
| // Init rcc specific settings |
| if (this->Rcc.Enabled && !this->InitRcc()) { |
| return false; |
| } |
| |
| // Add autogen include directory to the origin target INCLUDE_DIRECTORIES |
| if (this->MocOrUicEnabled() || (this->Rcc.Enabled && this->MultiConfig)) { |
| this->GenTarget->AddIncludeDirectory(this->Dir.IncludeGenExp, true); |
| } |
| |
| // Scan files |
| if (!this->InitScanFiles()) { |
| return false; |
| } |
| |
| // Create autogen target |
| if (this->MocOrUicEnabled() && !this->InitAutogenTarget()) { |
| return false; |
| } |
| |
| // Create rcc targets |
| if (this->Rcc.Enabled && !this->InitRccTargets()) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool cmQtAutoGenInitializer::InitMoc() |
| { |
| // Mocs compilation file |
| if (this->GlobalGen->IsXcode()) { |
| // XXX(xcode-per-cfg-src): Drop this Xcode-specific code path |
| // when the Xcode generator supports per-config sources. |
| this->Moc.CompilationFile.Default = |
| cmStrCat(this->Dir.Build, "/mocs_compilation.cpp"); |
| this->Moc.CompilationFileGenex = this->Moc.CompilationFile.Default; |
| } else { |
| this->ConfigFileNamesAndGenex( |
| this->Moc.CompilationFile, this->Moc.CompilationFileGenex, |
| cmStrCat(this->Dir.Build, "/mocs_compilation"_s), ".cpp"_s); |
| } |
| |
| // Moc predefs |
| if (this->GenTarget->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") && |
| (this->QtVersion >= IntegerVersion(5, 8))) { |
| // Command |
| this->Makefile->GetDefExpandList("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND", |
| this->Moc.PredefsCmd); |
| // Header |
| if (!this->Moc.PredefsCmd.empty()) { |
| this->ConfigFileNames(this->Moc.PredefsFile, |
| cmStrCat(this->Dir.Build, "/moc_predefs"), ".h"); |
| } |
| } |
| |
| // Moc includes |
| { |
| SearchPathSanitizer sanitizer(this->Makefile); |
| auto getDirs = |
| [this, &sanitizer](std::string const& cfg) -> std::vector<std::string> { |
| // Get the include dirs for this target, without stripping the implicit |
| // include dirs off, see issue #13667. |
| std::vector<std::string> dirs; |
| bool const appendImplicit = (this->QtVersion.Major >= 5); |
| this->LocalGen->GetIncludeDirectoriesImplicit( |
| dirs, this->GenTarget, "CXX", cfg, false, appendImplicit); |
| return sanitizer(dirs); |
| }; |
| |
| // Default configuration include directories |
| this->Moc.Includes.Default = getDirs(this->ConfigDefault); |
| // Other configuration settings |
| if (this->MultiConfig) { |
| for (std::string const& cfg : this->ConfigsList) { |
| std::vector<std::string> dirs = getDirs(cfg); |
| if (dirs == this->Moc.Includes.Default) { |
| continue; |
| } |
| this->Moc.Includes.Config[cfg] = std::move(dirs); |
| } |
| } |
| } |
| |
| // Moc compile definitions |
| { |
| auto getDefs = [this](std::string const& cfg) -> std::set<std::string> { |
| std::set<std::string> defines; |
| this->LocalGen->GetTargetDefines(this->GenTarget, cfg, "CXX", defines); |
| #ifdef _WIN32 |
| if (this->Moc.PredefsCmd.empty()) { |
| // Add WIN32 definition if we don't have a moc_predefs.h |
| defines.insert("WIN32"); |
| } |
| #endif |
| return defines; |
| }; |
| |
| // Default configuration defines |
| this->Moc.Defines.Default = getDefs(this->ConfigDefault); |
| // Other configuration defines |
| if (this->MultiConfig) { |
| for (std::string const& cfg : this->ConfigsList) { |
| std::set<std::string> defines = getDefs(cfg); |
| if (defines == this->Moc.Defines.Default) { |
| continue; |
| } |
| this->Moc.Defines.Config[cfg] = std::move(defines); |
| } |
| } |
| } |
| |
| // Moc executable |
| { |
| if (!this->GetQtExecutable(this->Moc, "moc", false)) { |
| return false; |
| } |
| // Let the _autogen target depend on the moc executable |
| if (this->Moc.ExecutableTarget) { |
| this->AutogenTarget.DependTargets.insert( |
| this->Moc.ExecutableTarget->Target); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool cmQtAutoGenInitializer::InitUic() |
| { |
| // Uic search paths |
| { |
| std::string const& usp = |
| this->GenTarget->GetSafeProperty("AUTOUIC_SEARCH_PATHS"); |
| if (!usp.empty()) { |
| this->Uic.SearchPaths = |
| SearchPathSanitizer(this->Makefile)(cmExpandedList(usp)); |
| } |
| } |
| // Uic target options |
| { |
| auto getOpts = [this](std::string const& cfg) -> std::vector<std::string> { |
| std::vector<std::string> opts; |
| this->GenTarget->GetAutoUicOptions(opts, cfg); |
| return opts; |
| }; |
| |
| // Default options |
| this->Uic.Options.Default = getOpts(this->ConfigDefault); |
| // Configuration specific options |
| if (this->MultiConfig) { |
| for (std::string const& cfg : this->ConfigsList) { |
| std::vector<std::string> options = getOpts(cfg); |
| if (options == this->Uic.Options.Default) { |
| continue; |
| } |
| this->Uic.Options.Config[cfg] = std::move(options); |
| } |
| } |
| } |
| |
| // Uic executable |
| { |
| if (!this->GetQtExecutable(this->Uic, "uic", true)) { |
| return false; |
| } |
| // Let the _autogen target depend on the uic executable |
| if (this->Uic.ExecutableTarget) { |
| this->AutogenTarget.DependTargets.insert( |
| this->Uic.ExecutableTarget->Target); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool cmQtAutoGenInitializer::InitRcc() |
| { |
| // Rcc executable |
| { |
| if (!this->GetQtExecutable(this->Rcc, "rcc", false)) { |
| return false; |
| } |
| // Evaluate test output on demand |
| CompilerFeatures& features = *this->Rcc.ExecutableFeatures; |
| if (!features.Evaluated) { |
| // Look for list options |
| if (this->QtVersion.Major == 5 || this->QtVersion.Major == 6) { |
| if (features.HelpOutput.find("--list") != std::string::npos) { |
| features.ListOptions.emplace_back("--list"); |
| } else if (features.HelpOutput.find("-list") != std::string::npos) { |
| features.ListOptions.emplace_back("-list"); |
| } |
| } |
| // Evaluation finished |
| features.Evaluated = true; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool cmQtAutoGenInitializer::InitScanFiles() |
| { |
| cmake const* cm = this->Makefile->GetCMakeInstance(); |
| auto const& kw = this->GlobalInitializer->kw(); |
| |
| auto makeMUFile = [this, &kw](cmSourceFile* sf, std::string const& fullPath, |
| std::vector<size_t> const& configs, |
| bool muIt) -> MUFileHandle { |
| MUFileHandle muf = cm::make_unique<MUFile>(); |
| muf->FullPath = fullPath; |
| muf->SF = sf; |
| if (!configs.empty() && configs.size() != this->ConfigsList.size()) { |
| muf->Configs = configs; |
| } |
| muf->Generated = sf->GetIsGenerated(); |
| bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN); |
| muf->SkipMoc = this->Moc.Enabled && |
| (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOMOC)); |
| muf->SkipUic = this->Uic.Enabled && |
| (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC)); |
| if (muIt) { |
| muf->MocIt = this->Moc.Enabled && !muf->SkipMoc; |
| muf->UicIt = this->Uic.Enabled && !muf->SkipUic; |
| } |
| return muf; |
| }; |
| |
| auto addMUHeader = [this](MUFileHandle&& muf, cm::string_view extension) { |
| cmSourceFile* sf = muf->SF; |
| const bool muIt = (muf->MocIt || muf->UicIt); |
| if (this->CMP0100Accept || (extension != "hh")) { |
| // Accept |
| if (muIt && muf->Generated) { |
| this->AutogenTarget.FilesGenerated.emplace_back(muf.get()); |
| } |
| this->AutogenTarget.Headers.emplace(sf, std::move(muf)); |
| } else if (muIt && this->CMP0100Warn) { |
| // Store file for warning message |
| this->AutogenTarget.CMP0100HeadersWarn.push_back(sf); |
| } |
| }; |
| |
| auto addMUSource = [this](MUFileHandle&& muf) { |
| if ((muf->MocIt || muf->UicIt) && muf->Generated) { |
| this->AutogenTarget.FilesGenerated.emplace_back(muf.get()); |
| } |
| this->AutogenTarget.Sources.emplace(muf->SF, std::move(muf)); |
| }; |
| |
| // Scan through target files |
| { |
| // Scan through target files |
| for (cmGeneratorTarget::AllConfigSource const& acs : |
| this->GenTarget->GetAllConfigSources()) { |
| std::string const& fullPath = acs.Source->GetFullPath(); |
| std::string const& extLower = |
| cmSystemTools::LowerCase(acs.Source->GetExtension()); |
| |
| // Register files that will be scanned by moc or uic |
| if (this->MocOrUicEnabled()) { |
| if (cm->IsAHeaderExtension(extLower)) { |
| addMUHeader(makeMUFile(acs.Source, fullPath, acs.Configs, true), |
| extLower); |
| } else if (cm->IsACLikeSourceExtension(extLower)) { |
| addMUSource(makeMUFile(acs.Source, fullPath, acs.Configs, true)); |
| } |
| } |
| |
| // Register rcc enabled files |
| if (this->Rcc.Enabled) { |
| if ((extLower == kw.qrc) && |
| !acs.Source->GetPropertyAsBool(kw.SKIP_AUTOGEN) && |
| !acs.Source->GetPropertyAsBool(kw.SKIP_AUTORCC)) { |
| // Register qrc file |
| Qrc qrc; |
| qrc.QrcFile = fullPath; |
| qrc.QrcName = |
| cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile); |
| qrc.Generated = acs.Source->GetIsGenerated(); |
| // RCC options |
| { |
| std::string const& opts = |
| acs.Source->GetSafeProperty(kw.AUTORCC_OPTIONS); |
| if (!opts.empty()) { |
| cmExpandList(opts, qrc.Options); |
| } |
| } |
| this->Rcc.Qrcs.push_back(std::move(qrc)); |
| } |
| } |
| } |
| } |
| // cmGeneratorTarget::GetAllConfigSources computes the target's |
| // sources meta data cache. Clear it so that OBJECT library targets that |
| // are AUTOGEN initialized after this target get their added |
| // mocs_compilation.cpp source acknowledged by this target. |
| this->GenTarget->ClearSourcesCache(); |
| |
| // For source files find additional headers and private headers |
| if (this->MocOrUicEnabled()) { |
| // Header search suffixes and extensions |
| static std::initializer_list<cm::string_view> const suffixes{ "", "_p" }; |
| auto const& exts = cm->GetHeaderExtensions(); |
| // Scan through sources |
| for (auto const& pair : this->AutogenTarget.Sources) { |
| MUFile const& muf = *pair.second; |
| if (muf.MocIt || muf.UicIt) { |
| // Search for the default header file and a private header |
| std::string const& srcFullPath = muf.SF->ResolveFullPath(); |
| std::string basePath = cmStrCat( |
| cmQtAutoGen::SubDirPrefix(srcFullPath), |
| cmSystemTools::GetFilenameWithoutLastExtension(srcFullPath)); |
| for (auto const& suffix : suffixes) { |
| std::string const suffixedPath = cmStrCat(basePath, suffix); |
| for (auto const& ext : exts) { |
| std::string fullPath = cmStrCat(suffixedPath, '.', ext); |
| |
| auto constexpr locationKind = cmSourceFileLocationKind::Known; |
| cmSourceFile* sf = |
| this->Makefile->GetSource(fullPath, locationKind); |
| if (sf) { |
| // Check if we know about this header already |
| if (cm::contains(this->AutogenTarget.Headers, sf)) { |
| continue; |
| } |
| // We only accept not-GENERATED files that do exist. |
| if (!sf->GetIsGenerated() && |
| !cmSystemTools::FileExists(fullPath)) { |
| continue; |
| } |
| } else if (cmSystemTools::FileExists(fullPath)) { |
| // Create a new source file for the existing file |
| sf = this->Makefile->CreateSource(fullPath, false, locationKind); |
| } |
| |
| if (sf) { |
| auto eMuf = makeMUFile(sf, fullPath, muf.Configs, true); |
| // Only process moc/uic when the parent is processed as well |
| if (!muf.MocIt) { |
| eMuf->MocIt = false; |
| } |
| if (!muf.UicIt) { |
| eMuf->UicIt = false; |
| } |
| addMUHeader(std::move(eMuf), ext); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| // Scan through all source files in the makefile to extract moc and uic |
| // parameters. Historically we support non target source file parameters. |
| // The reason is that their file names might be discovered from source files |
| // at generation time. |
| if (this->MocOrUicEnabled()) { |
| for (const auto& sf : this->Makefile->GetSourceFiles()) { |
| // sf->GetExtension() is only valid after sf->ResolveFullPath() ... |
| // Since we're iterating over source files that might be not in the |
| // target we need to check for path errors (not existing files). |
| std::string pathError; |
| std::string const& fullPath = sf->ResolveFullPath(&pathError); |
| if (!pathError.empty() || fullPath.empty()) { |
| continue; |
| } |
| std::string const& extLower = |
| cmSystemTools::LowerCase(sf->GetExtension()); |
| |
| if (cm->IsAHeaderExtension(extLower)) { |
| if (!cm::contains(this->AutogenTarget.Headers, sf.get())) { |
| auto muf = makeMUFile(sf.get(), fullPath, {}, false); |
| if (muf->SkipMoc || muf->SkipUic) { |
| addMUHeader(std::move(muf), extLower); |
| } |
| } |
| } else if (cm->IsACLikeSourceExtension(extLower)) { |
| if (!cm::contains(this->AutogenTarget.Sources, sf.get())) { |
| auto muf = makeMUFile(sf.get(), fullPath, {}, false); |
| if (muf->SkipMoc || muf->SkipUic) { |
| addMUSource(std::move(muf)); |
| } |
| } |
| } else if (this->Uic.Enabled && (extLower == kw.ui)) { |
| // .ui file |
| bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN); |
| bool const skipUic = |
| (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC)); |
| if (!skipUic) { |
| // Check if the .ui file has uic options |
| std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS); |
| if (uicOpts.empty()) { |
| this->Uic.UiFilesNoOptions.emplace_back(fullPath); |
| } else { |
| this->Uic.UiFilesWithOptions.emplace_back(fullPath, |
| cmExpandedList(uicOpts)); |
| } |
| |
| auto uiHeaderRelativePath = cmSystemTools::RelativePath( |
| this->LocalGen->GetCurrentSourceDirectory(), |
| cmSystemTools::GetFilenamePath(fullPath)); |
| |
| // Avoid creating a path containing adjacent slashes |
| if (!uiHeaderRelativePath.empty() && |
| uiHeaderRelativePath.back() != '/') { |
| uiHeaderRelativePath += '/'; |
| } |
| |
| auto uiHeaderFilePath = cmStrCat( |
| '/', uiHeaderRelativePath, "ui_"_s, |
| cmSystemTools::GetFilenameWithoutLastExtension(fullPath), ".h"_s); |
| |
| ConfigString uiHeader; |
| std::string uiHeaderGenex; |
| this->ConfigFileNamesAndGenex( |
| uiHeader, uiHeaderGenex, cmStrCat(this->Dir.Build, "/include"_s), |
| uiHeaderFilePath); |
| |
| this->Uic.UiHeaders.emplace_back( |
| std::make_pair(uiHeader, uiHeaderGenex)); |
| } else { |
| // Register skipped .ui file |
| this->Uic.SkipUi.insert(fullPath); |
| } |
| } |
| } |
| } |
| |
| // Process GENERATED sources and headers |
| if (this->MocOrUicEnabled() && !this->AutogenTarget.FilesGenerated.empty()) { |
| if (this->CMP0071Accept) { |
| // Let the autogen target depend on the GENERATED files |
| for (MUFile* muf : this->AutogenTarget.FilesGenerated) { |
| this->AutogenTarget.DependFiles.insert(muf->FullPath); |
| } |
| } else if (this->CMP0071Warn) { |
| cm::string_view property; |
| if (this->Moc.Enabled && this->Uic.Enabled) { |
| property = "SKIP_AUTOGEN"; |
| } else if (this->Moc.Enabled) { |
| property = "SKIP_AUTOMOC"; |
| } else if (this->Uic.Enabled) { |
| property = "SKIP_AUTOUIC"; |
| } |
| std::string files; |
| for (MUFile* muf : this->AutogenTarget.FilesGenerated) { |
| files += cmStrCat(" ", Quoted(muf->FullPath), '\n'); |
| } |
| this->Makefile->IssueMessage( |
| MessageType::AUTHOR_WARNING, |
| cmStrCat( |
| cmPolicies::GetPolicyWarning(cmPolicies::CMP0071), '\n', |
| "For compatibility, CMake is excluding the GENERATED source " |
| "file(s):\n", |
| files, "from processing by ", |
| cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false), |
| ". If any of the files should be processed, set CMP0071 to NEW. " |
| "If any of the files should not be processed, " |
| "explicitly exclude them by setting the source file property ", |
| property, ":\n set_property(SOURCE file.h PROPERTY ", property, |
| " ON)\n")); |
| } |
| } |
| |
| // Generate CMP0100 warning |
| if (this->MocOrUicEnabled() && |
| !this->AutogenTarget.CMP0100HeadersWarn.empty()) { |
| cm::string_view property; |
| if (this->Moc.Enabled && this->Uic.Enabled) { |
| property = "SKIP_AUTOGEN"; |
| } else if (this->Moc.Enabled) { |
| property = "SKIP_AUTOMOC"; |
| } else if (this->Uic.Enabled) { |
| property = "SKIP_AUTOUIC"; |
| } |
| std::string files; |
| for (cmSourceFile* sf : this->AutogenTarget.CMP0100HeadersWarn) { |
| files += cmStrCat(" ", Quoted(sf->GetFullPath()), '\n'); |
| } |
| this->Makefile->IssueMessage( |
| MessageType::AUTHOR_WARNING, |
| cmStrCat( |
| cmPolicies::GetPolicyWarning(cmPolicies::CMP0100), '\n', |
| "For compatibility, CMake is excluding the header file(s):\n", files, |
| "from processing by ", |
| cmQtAutoGen::Tools(this->Moc.Enabled, this->Uic.Enabled, false), |
| ". If any of the files should be processed, set CMP0100 to NEW. " |
| "If any of the files should not be processed, " |
| "explicitly exclude them by setting the source file property ", |
| property, ":\n set_property(SOURCE file.hh PROPERTY ", property, |
| " ON)\n")); |
| } |
| |
| // Process qrc files |
| if (!this->Rcc.Qrcs.empty()) { |
| const bool modernQt = (this->QtVersion.Major >= 5); |
| // Target rcc options |
| std::vector<std::string> optionsTarget = |
| cmExpandedList(this->GenTarget->GetSafeProperty(kw.AUTORCC_OPTIONS)); |
| |
| // Check if file name is unique |
| for (Qrc& qrc : this->Rcc.Qrcs) { |
| qrc.Unique = true; |
| for (Qrc const& qrc2 : this->Rcc.Qrcs) { |
| if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) { |
| qrc.Unique = false; |
| break; |
| } |
| } |
| } |
| // Path checksum and file names |
| for (Qrc& qrc : this->Rcc.Qrcs) { |
| // Path checksum |
| qrc.QrcPathChecksum = this->PathCheckSum.getPart(qrc.QrcFile); |
| // Output file name |
| qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum, |
| "/qrc_", qrc.QrcName, ".cpp"); |
| std::string const base = cmStrCat(this->Dir.Info, "/AutoRcc_", |
| qrc.QrcName, '_', qrc.QrcPathChecksum); |
| qrc.LockFile = cmStrCat(base, "_Lock.lock"); |
| qrc.InfoFile = cmStrCat(base, "_Info.json"); |
| this->ConfigFileNames(qrc.SettingsFile, cmStrCat(base, "_Used"), ".txt"); |
| } |
| // rcc options |
| for (Qrc& qrc : this->Rcc.Qrcs) { |
| // Target options |
| std::vector<std::string> opts = optionsTarget; |
| // Merge computed "-name XYZ" option |
| { |
| std::string name = qrc.QrcName; |
| // Replace '-' with '_'. The former is not valid for symbol names. |
| std::replace(name.begin(), name.end(), '-', '_'); |
| if (!qrc.Unique) { |
| name += cmStrCat('_', qrc.QrcPathChecksum); |
| } |
| std::vector<std::string> nameOpts; |
| nameOpts.emplace_back("-name"); |
| nameOpts.emplace_back(std::move(name)); |
| RccMergeOptions(opts, nameOpts, modernQt); |
| } |
| // Merge file option |
| RccMergeOptions(opts, qrc.Options, modernQt); |
| qrc.Options = std::move(opts); |
| } |
| // rcc resources |
| for (Qrc& qrc : this->Rcc.Qrcs) { |
| if (!qrc.Generated) { |
| std::string error; |
| RccLister const lister(this->Rcc.Executable, |
| this->Rcc.ExecutableFeatures->ListOptions); |
| if (!lister.list(qrc.QrcFile, qrc.Resources, error)) { |
| cmSystemTools::Error(error); |
| return false; |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| bool cmQtAutoGenInitializer::InitAutogenTarget() |
| { |
| // Register info file as generated by CMake |
| this->Makefile->AddCMakeOutputFile(this->AutogenTarget.InfoFile); |
| |
| // Files provided by the autogen target |
| std::vector<std::string> autogenByproducts; |
| if (this->Moc.Enabled) { |
| this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true); |
| autogenByproducts.push_back(this->Moc.CompilationFileGenex); |
| } |
| |
| if (this->Uic.Enabled) { |
| for (const auto& file : this->Uic.UiHeaders) { |
| this->AddGeneratedSource(file.first, this->Uic); |
| autogenByproducts.push_back(file.second); |
| } |
| } |
| |
| // Compose target comment |
| std::string autogenComment; |
| { |
| std::string tools; |
| if (this->Moc.Enabled) { |
| tools += "MOC"; |
| } |
| if (this->Uic.Enabled) { |
| if (!tools.empty()) { |
| tools += " and "; |
| } |
| tools += "UIC"; |
| } |
| autogenComment = cmStrCat("Automatic ", tools, " for target ", |
| this->GenTarget->GetName()); |
| } |
| |
| // Compose command lines |
| // FIXME: Take advantage of our per-config mocs_compilation_$<CONFIG>.cpp |
| // instead of fiddling with the include directories |
| std::vector<std::string> configs; |
| this->GlobalGen->GetQtAutoGenConfigs(configs); |
| bool stdPipesUTF8 = true; |
| cmCustomCommandLines commandLines; |
| for (auto const& config : configs) { |
| commandLines.push_back(cmMakeCommandLine( |
| { cmSystemTools::GetCMakeCommand(), "-E", "cmake_autogen", |
| this->AutogenTarget.InfoFile, config })); |
| } |
| |
| // Use PRE_BUILD on demand |
| bool usePRE_BUILD = false; |
| if (this->GlobalGen->GetName().find("Visual Studio") != std::string::npos) { |
| // Under VS use a PRE_BUILD event instead of a separate target to |
| // reduce the number of targets loaded into the IDE. |
| // This also works around a VS 11 bug that may skip updating the target: |
| // https://connect.microsoft.com/VisualStudio/feedback/details/769495 |
| usePRE_BUILD = true; |
| } |
| // Disable PRE_BUILD in some cases |
| if (usePRE_BUILD) { |
| // Cannot use PRE_BUILD with file depends |
| if (!this->AutogenTarget.DependFiles.empty()) { |
| usePRE_BUILD = false; |
| } |
| // Cannot use PRE_BUILD when a global autogen target is in place |
| if (this->AutogenTarget.GlobalTarget) { |
| usePRE_BUILD = false; |
| } |
| } |
| // Create the autogen target/command |
| if (usePRE_BUILD) { |
| // Add additional autogen target dependencies to origin target |
| for (cmTarget* depTarget : this->AutogenTarget.DependTargets) { |
| this->GenTarget->Target->AddUtility(depTarget->GetName(), false, |
| this->Makefile); |
| } |
| |
| if (!this->Uic.UiFilesNoOptions.empty() || |
| !this->Uic.UiFilesWithOptions.empty()) { |
| // Add a generated timestamp file |
| ConfigString timestampFile; |
| std::string timestampFileGenex; |
| ConfigFileNamesAndGenex(timestampFile, timestampFileGenex, |
| cmStrCat(this->Dir.Build, "/autouic"_s), |
| ".stamp"_s); |
| this->AddGeneratedSource(timestampFile, this->Uic); |
| |
| // Add a step in the pre-build command to touch the timestamp file |
| commandLines.push_back( |
| cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", "touch", |
| timestampFileGenex })); |
| |
| // UIC needs to be re-run if any of the known UI files change or the |
| // executable itself has been updated |
| auto uicDependencies = this->Uic.UiFilesNoOptions; |
| for (auto const& uiFile : this->Uic.UiFilesWithOptions) { |
| uicDependencies.push_back(uiFile.first); |
| } |
| AddAutogenExecutableToDependencies(this->Uic, uicDependencies); |
| |
| // Add a rule file to cause the target to build if a dependency has |
| // changed, which will trigger the pre-build command to run autogen |
| 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. |
| // |
| // PRE_BUILD does not support file dependencies! |
| const std::vector<std::string> no_output; |
| const std::vector<std::string> no_deps; |
| cmCustomCommand cc(no_output, autogenByproducts, no_deps, commandLines, |
| this->Makefile->GetBacktrace(), autogenComment.c_str(), |
| this->Dir.Work.c_str(), stdPipesUTF8); |
| cc.SetEscapeOldStyle(false); |
| cc.SetEscapeAllowMakeVars(true); |
| this->GenTarget->Target->AddPreBuildCommand(std::move(cc)); |
| } else { |
| |
| // Add link library target dependencies to the autogen target |
| // dependencies |
| if (this->AutogenTarget.DependOrigin) { |
| // add_dependencies/addUtility do not support generator expressions. |
| // We depend only on the libraries found in all configs therefore. |
| std::map<cmGeneratorTarget const*, std::size_t> commonTargets; |
| for (std::string const& config : this->ConfigsList) { |
| cmLinkImplementationLibraries const* libs = |
| this->GenTarget->GetLinkImplementationLibraries(config); |
| if (libs) { |
| for (cmLinkItem const& item : libs->Libraries) { |
| cmGeneratorTarget const* libTarget = item.Target; |
| if (libTarget && |
| !StaticLibraryCycle(this->GenTarget, libTarget, config)) { |
| // Increment target config count |
| commonTargets[libTarget]++; |
| } |
| } |
| } |
| } |
| for (auto const& item : commonTargets) { |
| if (item.second == this->ConfigsList.size()) { |
| this->AutogenTarget.DependTargets.insert(item.first->Target); |
| } |
| } |
| } |
| |
| std::vector<std::string> dependencies( |
| this->AutogenTarget.DependFiles.begin(), |
| this->AutogenTarget.DependFiles.end()); |
| |
| const bool useNinjaDepfile = this->QtVersion >= IntegerVersion(5, 15) && |
| this->GlobalGen->GetName().find("Ninja") != std::string::npos; |
| if (useNinjaDepfile) { |
| // Create a custom command that generates a timestamp file and |
| // has a depfile assigned. The depfile is created by JobDepFilesMergeT. |
| // |
| // Also create an additional '_autogen_timestamp_deps' that the custom |
| // command will depend on. It will have no sources or commands to |
| // execute, but it will have dependencies that would originally be |
| // assigned to the pre-Qt 5.15 'autogen' target. These dependencies will |
| // serve as a list of order-only dependencies for the custom command, |
| // without forcing the custom command to re-execute. |
| // |
| // The dependency tree would then look like |
| // '_autogen_timestamp_deps (order-only)' <- '/timestamp' file <- |
| // '_autogen' target. |
| const auto timestampTargetName = |
| cmStrCat(this->GenTarget->GetName(), "_autogen_timestamp_deps"); |
| std::vector<std::string> timestampTargetProvides; |
| cmCustomCommandLines timestampTargetCommandLines; |
| |
| // Add additional autogen target dependencies to |
| // '_autogen_timestamp_deps'. |
| for (const cmTarget* t : this->AutogenTarget.DependTargets) { |
| dependencies.push_back(t->GetName()); |
| } |
| |
| cmTarget* timestampTarget = this->LocalGen->AddUtilityCommand( |
| timestampTargetName, true, this->Dir.Work.c_str(), |
| /*byproducts=*/timestampTargetProvides, |
| /*depends=*/dependencies, timestampTargetCommandLines, cmPolicies::NEW, |
| false, nullptr); |
| this->LocalGen->AddGeneratorTarget( |
| cm::make_unique<cmGeneratorTarget>(timestampTarget, this->LocalGen)); |
| |
| // Set FOLDER property on the timestamp target, so it appears in the |
| // appropriate folder in an IDE or in the file api. |
| if (!this->TargetsFolder.empty()) { |
| timestampTarget->SetProperty("FOLDER", this->TargetsFolder); |
| } |
| |
| // Make '/timestamp' file depend on '_autogen_timestamp_deps' and on the |
| // moc and uic executables (whichever are enabled). |
| dependencies.clear(); |
| dependencies.push_back(timestampTargetName); |
| |
| AddAutogenExecutableToDependencies(this->Moc, dependencies); |
| AddAutogenExecutableToDependencies(this->Uic, dependencies); |
| |
| // Create the custom command that outputs the timestamp file. |
| const char timestampFileName[] = "timestamp"; |
| const std::string outputFile = |
| cmStrCat(this->Dir.Build, "/", timestampFileName); |
| this->AutogenTarget.DepFile = cmStrCat(this->Dir.Build, "/deps"); |
| this->AutogenTarget.DepFileRuleName = |
| cmStrCat(this->GenTarget->GetName(), "_autogen/", timestampFileName); |
| commandLines.push_back(cmMakeCommandLine( |
| { cmSystemTools::GetCMakeCommand(), "-E", "touch", outputFile })); |
| |
| this->AddGeneratedSource(outputFile, this->Moc); |
| const std::string no_main_dependency; |
| this->LocalGen->AddCustomCommandToOutput( |
| outputFile, dependencies, no_main_dependency, commandLines, |
| autogenComment.c_str(), this->Dir.Work.c_str(), |
| /*cmp0116=*/cmPolicies::NEW, /*replace=*/false, |
| /*escapeOldStyle=*/false, |
| /*uses_terminal=*/false, |
| /*command_expand_lists=*/false, this->AutogenTarget.DepFile, "", |
| stdPipesUTF8); |
| |
| // Alter variables for the autogen target which now merely wraps the |
| // custom command |
| dependencies.clear(); |
| dependencies.push_back(outputFile); |
| commandLines.clear(); |
| autogenComment.clear(); |
| } |
| |
| // Create autogen target |
| cmTarget* autogenTarget = this->LocalGen->AddUtilityCommand( |
| this->AutogenTarget.Name, true, this->Dir.Work.c_str(), |
| /*byproducts=*/autogenByproducts, |
| /*depends=*/dependencies, commandLines, cmPolicies::NEW, false, |
| autogenComment.c_str()); |
| // Create autogen generator target |
| this->LocalGen->AddGeneratorTarget( |
| cm::make_unique<cmGeneratorTarget>(autogenTarget, this->LocalGen)); |
| |
| // Forward origin utilities to autogen target |
| if (this->AutogenTarget.DependOrigin) { |
| for (BT<std::pair<std::string, bool>> const& depName : |
| this->GenTarget->GetUtilities()) { |
| autogenTarget->AddUtility(depName.Value.first, false, this->Makefile); |
| } |
| } |
| if (!useNinjaDepfile) { |
| // Add additional autogen target dependencies to autogen target |
| for (cmTarget* depTarget : this->AutogenTarget.DependTargets) { |
| autogenTarget->AddUtility(depTarget->GetName(), false, this->Makefile); |
| } |
| } |
| |
| // Set FOLDER property in autogen target |
| if (!this->TargetsFolder.empty()) { |
| autogenTarget->SetProperty("FOLDER", this->TargetsFolder); |
| } |
| |
| // Add autogen target to the origin target dependencies |
| this->GenTarget->Target->AddUtility(this->AutogenTarget.Name, false, |
| this->Makefile); |
| |
| // Add autogen target to the global autogen target dependencies |
| if (this->AutogenTarget.GlobalTarget) { |
| this->GlobalInitializer->AddToGlobalAutoGen(this->LocalGen, |
| this->AutogenTarget.Name); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool cmQtAutoGenInitializer::InitRccTargets() |
| { |
| for (Qrc const& qrc : this->Rcc.Qrcs) { |
| // Register info file as generated by CMake |
| this->Makefile->AddCMakeOutputFile(qrc.InfoFile); |
| // Register file at target |
| { |
| cmSourceFile* sf = this->AddGeneratedSource(qrc.OutputFile, this->Rcc); |
| sf->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "On"); |
| } |
| |
| std::vector<std::string> ccOutput; |
| ccOutput.push_back(qrc.OutputFile); |
| |
| std::vector<std::string> ccDepends; |
| // Add the .qrc and info file to the custom command dependencies |
| ccDepends.push_back(qrc.QrcFile); |
| ccDepends.push_back(qrc.InfoFile); |
| |
| bool stdPipesUTF8 = true; |
| cmCustomCommandLines commandLines; |
| if (this->MultiConfig) { |
| // Build for all configurations |
| for (std::string const& config : this->ConfigsList) { |
| commandLines.push_back( |
| cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", |
| "cmake_autorcc", qrc.InfoFile, config })); |
| } |
| } else { |
| commandLines.push_back( |
| cmMakeCommandLine({ cmSystemTools::GetCMakeCommand(), "-E", |
| "cmake_autorcc", qrc.InfoFile, "$<CONFIG>" })); |
| } |
| std::string ccComment = |
| cmStrCat("Automatic RCC for ", |
| FileProjectRelativePath(this->Makefile, qrc.QrcFile)); |
| |
| if (qrc.Generated || this->Rcc.GlobalTarget) { |
| // Create custom rcc target |
| std::string ccName; |
| { |
| ccName = cmStrCat(this->GenTarget->GetName(), "_arcc_", qrc.QrcName); |
| if (!qrc.Unique) { |
| ccName += cmStrCat('_', qrc.QrcPathChecksum); |
| } |
| |
| cmTarget* autoRccTarget = this->LocalGen->AddUtilityCommand( |
| ccName, true, this->Dir.Work.c_str(), ccOutput, ccDepends, |
| commandLines, cmPolicies::NEW, false, ccComment.c_str(), false, |
| false, "", stdPipesUTF8); |
| |
| // Create autogen generator target |
| this->LocalGen->AddGeneratorTarget( |
| cm::make_unique<cmGeneratorTarget>(autoRccTarget, this->LocalGen)); |
| |
| // Set FOLDER property in autogen target |
| if (!this->TargetsFolder.empty()) { |
| autoRccTarget->SetProperty("FOLDER", this->TargetsFolder); |
| } |
| if (!this->Rcc.ExecutableTargetName.empty()) { |
| autoRccTarget->AddUtility(this->Rcc.ExecutableTargetName, false, |
| this->Makefile); |
| } |
| } |
| // Add autogen target to the origin target dependencies |
| this->GenTarget->Target->AddUtility(ccName, false, this->Makefile); |
| |
| // Add autogen target to the global autogen target dependencies |
| if (this->Rcc.GlobalTarget) { |
| this->GlobalInitializer->AddToGlobalAutoRcc(this->LocalGen, ccName); |
| } |
| } else { |
| // Create custom rcc command |
| { |
| std::vector<std::string> ccByproducts; |
| |
| // Add the resource files to the dependencies |
| for (std::string const& fileName : qrc.Resources) { |
| // Add resource file to the custom command dependencies |
| ccDepends.push_back(fileName); |
| } |
| if (!this->Rcc.ExecutableTargetName.empty()) { |
| ccDepends.push_back(this->Rcc.ExecutableTargetName); |
| } |
| std::string no_main_dependency; |
| cmImplicitDependsList no_implicit_depends; |
| this->LocalGen->AddCustomCommandToOutput( |
| ccOutput, ccByproducts, ccDepends, no_main_dependency, |
| no_implicit_depends, commandLines, ccComment.c_str(), |
| this->Dir.Work.c_str(), cmPolicies::NEW, false, true, false, false, |
| "", "", stdPipesUTF8); |
| } |
| // Reconfigure when .qrc file changes |
| this->Makefile->AddCMakeDependFile(qrc.QrcFile); |
| } |
| } |
| |
| return true; |
| } |
| |
| bool cmQtAutoGenInitializer::SetupCustomTargets() |
| { |
| // Create info directory on demand |
| if (!cmSystemTools::MakeDirectory(this->Dir.Info)) { |
| cmSystemTools::Error(cmStrCat("AutoGen: Could not create directory: ", |
| Quoted(this->Dir.Info))); |
| return false; |
| } |
| |
| // Generate autogen target info file |
| if (this->MocOrUicEnabled()) { |
| // Write autogen target info files |
| if (!this->SetupWriteAutogenInfo()) { |
| return false; |
| } |
| } |
| |
| // Write AUTORCC info files |
| return !this->Rcc.Enabled || this->SetupWriteRccInfo(); |
| } |
| |
| bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() |
| { |
| // Utility lambdas |
| auto MfDef = [this](std::string const& key) { |
| return this->Makefile->GetSafeDefinition(key); |
| }; |
| |
| // Filtered headers and sources |
| std::set<std::string> moc_skip; |
| std::set<std::string> uic_skip; |
| std::vector<MUFile const*> headers; |
| std::vector<MUFile const*> sources; |
| |
| // Filter headers |
| { |
| headers.reserve(this->AutogenTarget.Headers.size()); |
| for (auto const& pair : this->AutogenTarget.Headers) { |
| MUFile const* const muf = pair.second.get(); |
| if (muf->SkipMoc) { |
| moc_skip.insert(muf->FullPath); |
| } |
| if (muf->SkipUic) { |
| uic_skip.insert(muf->FullPath); |
| } |
| if (muf->Generated && !this->CMP0071Accept) { |
| continue; |
| } |
| if (muf->MocIt || muf->UicIt) { |
| headers.emplace_back(muf); |
| } |
| } |
| std::sort(headers.begin(), headers.end(), |
| [](MUFile const* a, MUFile const* b) { |
| return (a->FullPath < b->FullPath); |
| }); |
| } |
| |
| // Filter sources |
| { |
| sources.reserve(this->AutogenTarget.Sources.size()); |
| for (auto const& pair : this->AutogenTarget.Sources) { |
| MUFile const* const muf = pair.second.get(); |
| if (muf->Generated && !this->CMP0071Accept) { |
| continue; |
| } |
| if (muf->SkipMoc) { |
| moc_skip.insert(muf->FullPath); |
| } |
| if (muf->SkipUic) { |
| uic_skip.insert(muf->FullPath); |
| } |
| if (muf->MocIt || muf->UicIt) { |
| sources.emplace_back(muf); |
| } |
| } |
| std::sort(sources.begin(), sources.end(), |
| [](MUFile const* a, MUFile const* b) { |
| return (a->FullPath < b->FullPath); |
| }); |
| } |
| |
| // Info writer |
| InfoWriter info; |
| |
| // General |
| info.SetBool("MULTI_CONFIG", this->MultiConfig); |
| info.SetUInt("PARALLEL", this->AutogenTarget.Parallel); |
| info.SetUInt("VERBOSITY", this->Verbosity); |
| |
| // Directories |
| info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR")); |
| info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR")); |
| info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR")); |
| info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR")); |
| info.Set("BUILD_DIR", this->Dir.Build); |
| info.SetConfig("INCLUDE_DIR", this->Dir.Include); |
| |
| info.SetUInt("QT_VERSION_MAJOR", this->QtVersion.Major); |
| info.SetUInt("QT_VERSION_MINOR", this->QtVersion.Minor); |
| info.Set("QT_MOC_EXECUTABLE", this->Moc.Executable); |
| info.Set("QT_UIC_EXECUTABLE", this->Uic.Executable); |
| |
| info.Set("CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand()); |
| info.SetConfig("SETTINGS_FILE", this->AutogenTarget.SettingsFile); |
| info.SetConfig("PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile); |
| info.Set("DEP_FILE", this->AutogenTarget.DepFile); |
| info.Set("DEP_FILE_RULE_NAME", this->AutogenTarget.DepFileRuleName); |
| info.SetArray("CMAKE_LIST_FILES", this->Makefile->GetListFiles()); |
| info.SetArray("HEADER_EXTENSIONS", |
| this->Makefile->GetCMakeInstance()->GetHeaderExtensions()); |
| auto cfgArray = [this](std::vector<size_t> const& configs) -> Json::Value { |
| Json::Value value; |
| if (!configs.empty()) { |
| value = Json::arrayValue; |
| for (size_t ci : configs) { |
| value.append(this->ConfigsList[ci]); |
| } |
| } |
| return value; |
| }; |
| info.SetArrayArray("HEADERS", headers, |
| [this, &cfgArray](Json::Value& jval, MUFile const* muf) { |
| jval.resize(4u); |
| jval[0u] = muf->FullPath; |
| jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', |
| muf->UicIt ? 'U' : 'u'); |
| jval[2u] = this->GetMocBuildPath(*muf); |
| jval[3u] = cfgArray(muf->Configs); |
| }); |
| info.SetArrayArray( |
| "SOURCES", sources, [&cfgArray](Json::Value& jval, MUFile const* muf) { |
| jval.resize(3u); |
| jval[0u] = muf->FullPath; |
| jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u'); |
| jval[2u] = cfgArray(muf->Configs); |
| }); |
| |
| // Write moc settings |
| if (this->Moc.Enabled) { |
| info.SetArray("MOC_SKIP", moc_skip); |
| info.SetConfigArray("MOC_DEFINITIONS", this->Moc.Defines); |
| info.SetConfigArray("MOC_INCLUDES", this->Moc.Includes); |
| info.SetArray("MOC_OPTIONS", this->Moc.Options); |
| info.SetBool("MOC_RELAXED_MODE", this->Moc.RelaxedMode); |
| info.SetBool("MOC_PATH_PREFIX", this->Moc.PathPrefix); |
| info.SetArray("MOC_MACRO_NAMES", this->Moc.MacroNames); |
| info.SetArrayArray( |
| "MOC_DEPEND_FILTERS", this->Moc.DependFilters, |
| [](Json::Value& jval, std::pair<std::string, std::string> const& pair) { |
| jval.resize(2u); |
| jval[0u] = pair.first; |
| jval[1u] = pair.second; |
| }); |
| info.SetConfig("MOC_COMPILATION_FILE", this->Moc.CompilationFile); |
| info.SetArray("MOC_PREDEFS_CMD", this->Moc.PredefsCmd); |
| info.SetConfig("MOC_PREDEFS_FILE", this->Moc.PredefsFile); |
| } |
| |
| // Write uic settings |
| if (this->Uic.Enabled) { |
| // Add skipped .ui files |
| uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end()); |
| |
| info.SetArray("UIC_SKIP", uic_skip); |
| info.SetArrayArray("UIC_UI_FILES", this->Uic.UiFilesWithOptions, |
| [](Json::Value& jval, UicT::UiFileT const& uiFile) { |
| jval.resize(2u); |
| jval[0u] = uiFile.first; |
| InfoWriter::MakeStringArray(jval[1u], uiFile.second); |
| }); |
| info.SetConfigArray("UIC_OPTIONS", this->Uic.Options); |
| info.SetArray("UIC_SEARCH_PATHS", this->Uic.SearchPaths); |
| } |
| |
| info.Save(this->AutogenTarget.InfoFile); |
| |
| return true; |
| } |
| |
| bool cmQtAutoGenInitializer::SetupWriteRccInfo() |
| { |
| for (Qrc const& qrc : this->Rcc.Qrcs) { |
| // Utility lambdas |
| auto MfDef = [this](std::string const& key) { |
| return this->Makefile->GetSafeDefinition(key); |
| }; |
| |
| InfoWriter info; |
| |
| // General |
| info.SetBool("MULTI_CONFIG", this->MultiConfig); |
| info.SetUInt("VERBOSITY", this->Verbosity); |
| |
| // Files |
| info.Set("LOCK_FILE", qrc.LockFile); |
| info.SetConfig("SETTINGS_FILE", qrc.SettingsFile); |
| |
| // Directories |
| info.Set("CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR")); |
| info.Set("CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR")); |
| info.Set("CMAKE_CURRENT_SOURCE_DIR", MfDef("CMAKE_CURRENT_SOURCE_DIR")); |
| info.Set("CMAKE_CURRENT_BINARY_DIR", MfDef("CMAKE_CURRENT_BINARY_DIR")); |
| info.Set("BUILD_DIR", this->Dir.Build); |
| info.SetConfig("INCLUDE_DIR", this->Dir.Include); |
| |
| // rcc executable |
| info.Set("RCC_EXECUTABLE", this->Rcc.Executable); |
| info.SetArray("RCC_LIST_OPTIONS", |
| this->Rcc.ExecutableFeatures->ListOptions); |
| |
| // qrc file |
| info.Set("SOURCE", qrc.QrcFile); |
| info.Set("OUTPUT_CHECKSUM", qrc.QrcPathChecksum); |
| info.Set("OUTPUT_NAME", cmSystemTools::GetFilenameName(qrc.OutputFile)); |
| info.SetArray("OPTIONS", qrc.Options); |
| info.SetArray("INPUTS", qrc.Resources); |
| |
| info.Save(qrc.InfoFile); |
| } |
| |
| return true; |
| } |
| |
| cmSourceFile* cmQtAutoGenInitializer::RegisterGeneratedSource( |
| std::string const& filename) |
| { |
| cmSourceFile* gFile = this->Makefile->GetOrCreateSource(filename, true); |
| gFile->MarkAsGenerated(); |
| gFile->SetProperty("SKIP_AUTOGEN", "1"); |
| return gFile; |
| } |
| |
| cmSourceFile* cmQtAutoGenInitializer::AddGeneratedSource( |
| std::string const& filename, GenVarsT const& genVars, bool prepend) |
| { |
| // Register source at makefile |
| cmSourceFile* gFile = this->RegisterGeneratedSource(filename); |
| // Add source file to target |
| this->GenTarget->AddSource(filename, prepend); |
| |
| // Add source file to source group |
| this->AddToSourceGroup(filename, genVars.GenNameUpper); |
| |
| return gFile; |
| } |
| |
| void cmQtAutoGenInitializer::AddGeneratedSource(ConfigString const& filename, |
| GenVarsT const& genVars, |
| bool prepend) |
| { |
| // XXX(xcode-per-cfg-src): Drop the Xcode-specific part of the condition |
| // when the Xcode generator supports per-config sources. |
| if (!this->MultiConfig || this->GlobalGen->IsXcode()) { |
| this->AddGeneratedSource(filename.Default, genVars, prepend); |
| return; |
| } |
| for (auto const& cfg : this->ConfigsList) { |
| std::string const& filenameCfg = filename.Config.at(cfg); |
| // Register source at makefile |
| this->RegisterGeneratedSource(filenameCfg); |
| // Add source file to target for this configuration. |
| this->GenTarget->AddSource( |
| cmStrCat("$<$<CONFIG:"_s, cfg, ">:"_s, filenameCfg, ">"_s), prepend); |
| // Add source file to source group |
| this->AddToSourceGroup(filenameCfg, genVars.GenNameUpper); |
| } |
| } |
| |
| void cmQtAutoGenInitializer::AddToSourceGroup(std::string const& fileName, |
| cm::string_view genNameUpper) |
| { |
| cmSourceGroup* sourceGroup = nullptr; |
| // Acquire source group |
| { |
| std::string property; |
| std::string groupName; |
| { |
| // Prefer generator specific source group name |
| std::initializer_list<std::string> const props{ |
| cmStrCat(genNameUpper, "_SOURCE_GROUP"), "AUTOGEN_SOURCE_GROUP" |
| }; |
| for (std::string const& prop : props) { |
| cmProp propName = this->Makefile->GetState()->GetGlobalProperty(prop); |
| if (cmNonempty(propName)) { |
| groupName = *propName; |
| property = prop; |
| break; |
| } |
| } |
| } |
| // Generate a source group on demand |
| if (!groupName.empty()) { |
| sourceGroup = this->Makefile->GetOrCreateSourceGroup(groupName); |
| if (!sourceGroup) { |
| cmSystemTools::Error( |
| cmStrCat(genNameUpper, " error in ", property, |
| ": Could not find or create the source group ", |
| cmQtAutoGen::Quoted(groupName))); |
| } |
| } |
| } |
| if (sourceGroup) { |
| sourceGroup->AddGroupFile(fileName); |
| } |
| } |
| |
| void cmQtAutoGenInitializer::AddCleanFile(std::string const& fileName) |
| { |
| this->GenTarget->Target->AppendProperty("ADDITIONAL_CLEAN_FILES", fileName, |
| false); |
| } |
| |
| void cmQtAutoGenInitializer::ConfigFileNames(ConfigString& configString, |
| cm::string_view prefix, |
| cm::string_view suffix) |
| { |
| configString.Default = cmStrCat(prefix, suffix); |
| if (this->MultiConfig) { |
| for (auto const& cfg : this->ConfigsList) { |
| configString.Config[cfg] = cmStrCat(prefix, '_', cfg, suffix); |
| } |
| } |
| } |
| |
| void cmQtAutoGenInitializer::ConfigFileNamesAndGenex( |
| ConfigString& configString, std::string& genex, cm::string_view const prefix, |
| cm::string_view const suffix) |
| { |
| this->ConfigFileNames(configString, prefix, suffix); |
| if (this->MultiConfig) { |
| genex = cmStrCat(prefix, "_$<CONFIG>"_s, suffix); |
| } else { |
| genex = configString.Default; |
| } |
| } |
| |
| void cmQtAutoGenInitializer::ConfigFileClean(ConfigString& configString) |
| { |
| this->AddCleanFile(configString.Default); |
| if (this->MultiConfig) { |
| for (auto const& pair : configString.Config) { |
| this->AddCleanFile(pair.second); |
| } |
| } |
| } |
| |
| static cmQtAutoGen::IntegerVersion parseMocVersion(std::string str) |
| { |
| cmQtAutoGen::IntegerVersion result; |
| |
| static const std::string prelude = "moc "; |
| size_t pos = str.find(prelude); |
| if (pos == std::string::npos) { |
| return result; |
| } |
| |
| str.erase(0, prelude.size() + pos); |
| std::istringstream iss(str); |
| std::string major; |
| std::string minor; |
| if (!std::getline(iss, major, '.') || !std::getline(iss, minor, '.')) { |
| return result; |
| } |
| |
| result.Major = static_cast<unsigned int>(std::stoi(major)); |
| result.Minor = static_cast<unsigned int>(std::stoi(minor)); |
| return result; |
| } |
| |
| static cmQtAutoGen::IntegerVersion GetMocVersion( |
| const std::string& mocExecutablePath) |
| { |
| std::string capturedStdOut; |
| int exitCode; |
| if (!cmSystemTools::RunSingleCommand({ mocExecutablePath, "--version" }, |
| &capturedStdOut, nullptr, &exitCode, |
| nullptr, cmSystemTools::OUTPUT_NONE)) { |
| return {}; |
| } |
| |
| if (exitCode != 0) { |
| return {}; |
| } |
| |
| return parseMocVersion(capturedStdOut); |
| } |
| |
| static std::string FindMocExecutableFromMocTarget(cmMakefile* makefile, |
| unsigned int qtMajorVersion) |
| { |
| std::string result; |
| const std::string mocTargetName = |
| "Qt" + std::to_string(qtMajorVersion) + "::moc"; |
| cmTarget* mocTarget = makefile->FindTargetToUse(mocTargetName); |
| if (mocTarget) { |
| result = mocTarget->GetSafeProperty("IMPORTED_LOCATION"); |
| } |
| return result; |
| } |
| |
| std::pair<cmQtAutoGen::IntegerVersion, unsigned int> |
| cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target, |
| std::string mocExecutable) |
| { |
| // Converts a char ptr to an unsigned int value |
| auto toUInt = [](const char* const input) -> unsigned int { |
| unsigned long tmp = 0; |
| if (input && cmStrToULong(input, &tmp)) { |
| return static_cast<unsigned int>(tmp); |
| } |
| return 0u; |
| }; |
| auto toUInt2 = [](cmProp input) -> unsigned int { |
| unsigned long tmp = 0; |
| if (input && cmStrToULong(*input, &tmp)) { |
| return static_cast<unsigned int>(tmp); |
| } |
| return 0u; |
| }; |
| |
| // Initialize return value to a default |
| std::pair<IntegerVersion, unsigned int> res( |
| IntegerVersion(), |
| toUInt(target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", |
| ""))); |
| |
| // Acquire known Qt versions |
| std::vector<cmQtAutoGen::IntegerVersion> knownQtVersions; |
| { |
| // Qt version variable prefixes |
| static std::initializer_list< |
| std::pair<cm::string_view, cm::string_view>> const keys{ |
| { "Qt6Core_VERSION_MAJOR", "Qt6Core_VERSION_MINOR" }, |
| { "Qt5Core_VERSION_MAJOR", "Qt5Core_VERSION_MINOR" }, |
| { "QT_VERSION_MAJOR", "QT_VERSION_MINOR" }, |
| }; |
| |
| knownQtVersions.reserve(keys.size() * 2); |
| |
| // Adds a version to the result (nullptr safe) |
| auto addVersion = [&knownQtVersions, &toUInt2](cmProp major, |
| cmProp minor) { |
| cmQtAutoGen::IntegerVersion ver(toUInt2(major), toUInt2(minor)); |
| if (ver.Major != 0) { |
| knownQtVersions.emplace_back(ver); |
| } |
| }; |
| |
| // Read versions from variables |
| for (auto const& keyPair : keys) { |
| addVersion(target->Makefile->GetDefinition(std::string(keyPair.first)), |
| target->Makefile->GetDefinition(std::string(keyPair.second))); |
| } |
| |
| // Read versions from directory properties |
| for (auto const& keyPair : keys) { |
| addVersion(target->Makefile->GetProperty(std::string(keyPair.first)), |
| target->Makefile->GetProperty(std::string(keyPair.second))); |
| } |
| } |
| |
| // Evaluate known Qt versions |
| if (!knownQtVersions.empty()) { |
| if (res.second == 0) { |
| // No specific version was requested by the target: |
| // Use highest known Qt version. |
| res.first = knownQtVersions.at(0); |
| } else { |
| // Pick a version from the known versions: |
| for (auto it : knownQtVersions) { |
| if (it.Major == res.second) { |
| res.first = it; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (res.first.Major == 0) { |
| // We could not get the version number from variables or directory |
| // properties. This might happen if the find_package call for Qt is wrapped |
| // in a function. Try to find the moc executable path from the available |
| // targets and call "moc --version" to get the Qt version. |
| if (mocExecutable.empty()) { |
| mocExecutable = |
| FindMocExecutableFromMocTarget(target->Makefile, res.second); |
| } |
| if (!mocExecutable.empty()) { |
| res.first = GetMocVersion(mocExecutable); |
| } |
| } |
| |
| return res; |
| } |
| |
| std::string cmQtAutoGenInitializer::GetMocBuildPath(MUFile const& muf) |
| { |
| std::string res; |
| if (!muf.MocIt) { |
| return res; |
| } |
| |
| std::string basePath = |
| cmStrCat(this->PathCheckSum.getPart(muf.FullPath), "/moc_", |
| FileNameWithoutLastExtension(muf.FullPath)); |
| |
| res = cmStrCat(basePath, ".cpp"); |
| if (this->Moc.EmittedBuildPaths.emplace(res).second) { |
| return res; |
| } |
| |
| // File name already emitted. |
| // Try appending the header suffix to the base path. |
| basePath = cmStrCat(basePath, '_', muf.SF->GetExtension()); |
| res = cmStrCat(basePath, ".cpp"); |
| if (this->Moc.EmittedBuildPaths.emplace(res).second) { |
| return res; |
| } |
| |
| // File name with header extension already emitted. |
| // Try adding a number to the base path. |
| constexpr std::size_t number_begin = 2; |
| constexpr std::size_t number_end = 256; |
| for (std::size_t ii = number_begin; ii != number_end; ++ii) { |
| res = cmStrCat(basePath, '_', ii, ".cpp"); |
| if (this->Moc.EmittedBuildPaths.emplace(res).second) { |
| return res; |
| } |
| } |
| |
| // Output file name conflict (unlikely, but still...) |
| cmSystemTools::Error( |
| cmStrCat("moc output file name conflict for ", muf.FullPath)); |
| |
| return res; |
| } |
| |
| bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars, |
| const std::string& executable, |
| bool ignoreMissingTarget) const |
| { |
| auto print_err = [this, &genVars](std::string const& err) { |
| cmSystemTools::Error(cmStrCat(genVars.GenNameUpper, " for target ", |
| this->GenTarget->GetName(), ": ", err)); |
| }; |
| |
| // Custom executable |
| { |
| std::string const prop = cmStrCat(genVars.GenNameUpper, "_EXECUTABLE"); |
| std::string const& val = this->GenTarget->Target->GetSafeProperty(prop); |
| if (!val.empty()) { |
| // Evaluate generator expression |
| { |
| cmListFileBacktrace lfbt = this->Makefile->GetBacktrace(); |
| cmGeneratorExpression ge(lfbt); |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(val); |
| genVars.Executable = cge->Evaluate(this->LocalGen, ""); |
| } |
| if (genVars.Executable.empty() && !ignoreMissingTarget) { |
| print_err(prop + " evaluates to an empty value"); |
| return false; |
| } |
| |
| // Create empty compiler features. |
| genVars.ExecutableFeatures = |
| std::make_shared<cmQtAutoGen::CompilerFeatures>(); |
| return true; |
| } |
| } |
| |
| // Find executable target |
| { |
| // Find executable target name |
| cm::string_view prefix; |
| if (this->QtVersion.Major == 4) { |
| prefix = "Qt4::"; |
| } else if (this->QtVersion.Major == 5) { |
| prefix = "Qt5::"; |
| } else if (this->QtVersion.Major == 6) { |
| prefix = "Qt6::"; |
| } |
| std::string const targetName = cmStrCat(prefix, executable); |
| |
| // Find target |
| cmGeneratorTarget* genTarget = |
| this->LocalGen->FindGeneratorTargetToUse(targetName); |
| if (genTarget) { |
| genVars.ExecutableTargetName = targetName; |
| genVars.ExecutableTarget = genTarget; |
| if (genTarget->IsImported()) { |
| genVars.Executable = genTarget->ImportedGetLocation(""); |
| } else { |
| genVars.Executable = genTarget->GetLocation(""); |
| } |
| } else { |
| if (ignoreMissingTarget) { |
| // Create empty compiler features. |
| genVars.ExecutableFeatures = |
| std::make_shared<cmQtAutoGen::CompilerFeatures>(); |
| return true; |
| } |
| print_err(cmStrCat("Could not find ", executable, " executable target ", |
| targetName)); |
| return false; |
| } |
| } |
| |
| // Get executable features |
| { |
| std::string err; |
| genVars.ExecutableFeatures = this->GlobalInitializer->GetCompilerFeatures( |
| executable, genVars.Executable, err); |
| if (!genVars.ExecutableFeatures) { |
| print_err(err); |
| return false; |
| } |
| } |
| |
| return true; |
| } |