From d867e058924d348ee5ec5bba867965e8f1f276e2 Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Fri, 20 Sep 2019 22:39:13 +0200 Subject: Autogen: Use JSON instead of CMake script for info files We used to store information for the _autogen target in a CMake script file AutogenInfo.cmake, which was imported by a temporary cmake instance in the _autogen target. This introduced the overhead of creating a temporary cmake instance and inherited the limitations of the CMake language which only supports lists. This patch introduces JSON files to pass information to AUTORCC and autogen_ targets. JSON files are more flexible for passing data, e.g. they support nested lists. The patch has the side effects that - AutogenInfo.cmake is renamed to AutogenInfo.json - AutogenOldSettings.txt is renamed to AutogenUsed.txt - RCCInfo.cmake is renamed to AutoRcc___Info.json - RCC.lock is renamed to AutoRcc___Lock.lock - RCCSettings.txt is renamed to AutoRcc___Used.txt --- Source/cmQtAutoGen.cxx | 11 +- Source/cmQtAutoGen.h | 5 +- Source/cmQtAutoGenInitializer.cxx | 792 ++++++++++++++++++++------------------ Source/cmQtAutoGenInitializer.h | 114 +++--- Source/cmQtAutoGenerator.cxx | 260 +++++++++++-- Source/cmQtAutoGenerator.h | 49 ++- Source/cmQtAutoMocUic.cxx | 513 ++++++++++++------------ Source/cmQtAutoMocUic.h | 45 +-- Source/cmQtAutoRcc.cxx | 116 ++---- Source/cmQtAutoRcc.h | 7 +- 10 files changed, 1051 insertions(+), 861 deletions(-) diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index 57c8825..0f0e864 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx @@ -72,7 +72,6 @@ void MergeOptions(std::vector& baseOpts, // - Class definitions unsigned int const cmQtAutoGen::ParallelMax = 64; -std::string const cmQtAutoGen::ListSep = "<<>>"; cm::string_view cmQtAutoGen::GeneratorName(GenT genType) { @@ -162,6 +161,16 @@ std::string cmQtAutoGen::QuotedCommand(std::vector const& command) return res; } +std::string cmQtAutoGen::FileNameWithoutLastExtension(cm::string_view filename) +{ + auto slashPos = filename.rfind('/'); + if (slashPos != cm::string_view::npos) { + filename.remove_prefix(slashPos + 1); + } + auto dotPos = filename.rfind('.'); + return std::string(filename.substr(0, dotPos)); +} + std::string cmQtAutoGen::ParentDir(cm::string_view filename) { auto slashPos = filename.rfind('/'); diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index c8e4203..d070b79 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -62,8 +62,6 @@ public: RCC // AUTORCC }; - /// @brief Nested lists separator - static std::string const ListSep; /// @brief Maximum number of parallel threads/processes in a generator static unsigned int const ParallelMax; @@ -81,6 +79,9 @@ public: static std::string QuotedCommand(std::vector const& command); + /// @brief Returns the file name without path and extension (thread safe) + static std::string FileNameWithoutLastExtension(cm::string_view filename); + /// @brief Returns the parent directory of the file (thread safe) static std::string ParentDir(cm::string_view filename); diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 0329ac7..0d56fe1 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -8,7 +8,7 @@ #include "cmAlgorithms.h" #include "cmCustomCommand.h" #include "cmCustomCommandLines.h" -#include "cmFilePathChecksum.h" +#include "cmGeneratedFileStream.h" #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" @@ -17,7 +17,6 @@ #include "cmLocalGenerator.h" #include "cmMakefile.h" #include "cmMessageType.h" -#include "cmOutputConverter.h" #include "cmPolicies.h" #include "cmSourceFile.h" #include "cmSourceFileLocationKind.h" @@ -27,6 +26,8 @@ #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmTarget.h" +#include "cm_jsoncpp_value.h" +#include "cm_jsoncpp_writer.h" #include "cmake.h" #include "cmsys/SystemInformation.hxx" @@ -34,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +43,7 @@ #include #include +#include #include namespace { @@ -157,99 +160,134 @@ std::vector SearchPathSanitizer::operator()( } return res; } -} // End of unnamed namespace -cmQtAutoGenInitializer::InfoWriter::InfoWriter(std::string const& filename) +/** @brief Writes a CMake info file. */ +class InfoWriter { - Ofs_.SetCopyIfDifferent(true); - Ofs_.Open(filename, false, true); -} +public: + // -- Single value + void Set(std::string const& key, std::string const& value) + { + Value_[key] = value; + } + void SetConfig(std::string const& key, + cmQtAutoGenInitializer::ConfigString const& cfgStr); + void SetBool(std::string const& key, bool value) { Value_[key] = value; } + void SetUInt(std::string const& key, unsigned int value) + { + Value_[key] = value; + } + + // -- Array utility + template + static bool MakeArray(Json::Value& jval, CONT const& container); + + template + static void MakeStringArray(Json::Value& jval, CONT const& container); + + // -- Array value + template + void SetArray(std::string const& key, CONT const& container); + template + void SetConfigArray( + std::string const& key, + cmQtAutoGenInitializer::ConfigStrings const& cfgStr); -template -std::string cmQtAutoGenInitializer::InfoWriter::ListJoin(IT it_begin, - IT it_end) + // -- Array of arrays + template + 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) { - std::string res; - for (IT it = it_begin; it != it_end; ++it) { - if (it != it_begin) { - res += ';'; - } - for (const char* c = it->c_str(); *c; ++c) { - if (*c == '"') { - // Escape the double quote to avoid ending the argument. - res += "\\\""; - } else if (*c == '$') { - // Escape the dollar to avoid expanding variables. - res += "\\$"; - } else if (*c == '\\') { - // Escape the backslash to avoid other escapes. - res += "\\\\"; - } else if (*c == ';') { - // Escape the semicolon to avoid list expansion. - res += "\\;"; - } else { - // Other characters will be parsed correctly. - res += *c; - } - } + Set(key, cfgStr.Default); + for (auto const& item : cfgStr.Config) { + Set(cmStrCat(key, '_', item.first), item.second); } - return res; } -inline std::string cmQtAutoGenInitializer::InfoWriter::ConfigKey( - cm::string_view key, std::string const& config) +template +bool InfoWriter::MakeArray(Json::Value& jval, CONT const& container) { - return cmStrCat(key, "_", config); + jval = Json::arrayValue; + std::size_t const listSize = cm::size(container); + if (listSize == 0) { + return false; + } + jval.resize(static_cast(listSize)); + return true; } -void cmQtAutoGenInitializer::InfoWriter::Write(cm::string_view key, - std::string const& value) +template +void InfoWriter::MakeStringArray(Json::Value& jval, CONT const& container) { - Ofs_ << "set(" << key << " " << cmOutputConverter::EscapeForCMake(value) - << ")\n"; -}; - -void cmQtAutoGenInitializer::InfoWriter::WriteUInt(cm::string_view key, - unsigned int value) -{ - Ofs_ << "set(" << key << " " << value << ")\n"; -}; + if (MakeArray(jval, container)) { + Json::ArrayIndex ii = 0; + for (std::string const& item : container) { + jval[ii++] = item; + } + } +} -template -void cmQtAutoGenInitializer::InfoWriter::WriteStrings(cm::string_view key, - C const& container) +template +void InfoWriter::SetArray(std::string const& key, CONT const& container) { - Ofs_ << "set(" << key << " \"" - << ListJoin(container.begin(), container.end()) << "\")\n"; + MakeStringArray(Value_[key], container); } -void cmQtAutoGenInitializer::InfoWriter::WriteConfig( - cm::string_view key, std::map const& map) +template +void InfoWriter::SetArrayArray(std::string const& key, CONT const& container, + FUNC func) { - for (auto const& item : map) { - Write(ConfigKey(key, item.first), item.second); + Json::Value& jval = 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 -void cmQtAutoGenInitializer::InfoWriter::WriteConfigStrings( - cm::string_view key, std::map const& map) +template +void InfoWriter::SetConfigArray( + std::string const& key, + cmQtAutoGenInitializer::ConfigStrings const& cfgStr) { - for (auto const& item : map) { - WriteStrings(ConfigKey(key, item.first), item.second); + SetArray(key, cfgStr.Default); + for (auto const& item : cfgStr.Config) { + SetArray(cmStrCat(key, '_', item.first), item.second); } } -void cmQtAutoGenInitializer::InfoWriter::WriteNestedLists( - cm::string_view key, std::vector> const& lists) +bool InfoWriter::Save(std::string const& filename) { - std::vector seplist; - seplist.reserve(lists.size()); - for (std::vector const& list : lists) { - seplist.push_back(cmStrCat("{", ListJoin(list.begin(), list.end()), "}")); + cmGeneratedFileStream fileStream; + fileStream.SetCopyIfDifferent(true); + fileStream.Open(filename, false, true); + if (!fileStream) { + return false; } - Write(key, cmJoin(seplist, cmQtAutoGen::ListSep)); -}; + + Json::StyledStreamWriter jsonWriter; + try { + jsonWriter.write(fileStream, Value_); + } catch (...) { + return false; + } + + return fileStream.Close(); +} + +} // End of unnamed namespace cmQtAutoGenInitializer::cmQtAutoGenInitializer( cmQtAutoGenGlobalInitializer* globalInitializer, @@ -261,6 +299,7 @@ cmQtAutoGenInitializer::cmQtAutoGenInitializer( , GlobalGen(genTarget->GetGlobalGenerator()) , LocalGen(genTarget->GetLocalGenerator()) , Makefile(genTarget->Makefile) + , PathCheckSum(genTarget->Makefile) , QtVersion(qtVersion) { AutogenTarget.GlobalTarget = globalAutogenTarget; @@ -280,12 +319,20 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } // Verbosity - this->Verbosity = this->Makefile->GetSafeDefinition("CMAKE_AUTOGEN_VERBOSE"); - if (!this->Verbosity.empty()) { - unsigned long iVerb = 0; - if (!cmStrToULong(this->Verbosity, &iVerb)) { - // Non numeric verbosity - this->Verbosity = cmIsOn(this->Verbosity) ? "1" : "0"; + { + 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(iVerb); + } else { + // Non numeric verbosity + if (cmIsOn(def)) { + this->Verbosity = 1; + } + } } } @@ -352,15 +399,9 @@ bool cmQtAutoGenInitializer::InitCustomTargets() cmSystemTools::ConvertToUnixSlashes(this->Dir.Work); // Include directory - this->Dir.Include = cmStrCat(this->Dir.Build, "/include"); - // Per config include directories - if (this->MultiConfig) { - for (std::string const& cfg : this->ConfigsList) { - std::string& dir = this->Dir.ConfigInclude[cfg]; - dir = cmStrCat(this->Dir.Build, "/include_", cfg); - } - } - this->Dir.IncludeGenExp = this->Dir.Include; + ConfigFileNames(this->Dir.Include, cmStrCat(this->Dir.Build, "/include"), + ""); + this->Dir.IncludeGenExp = this->Dir.Include.Default; if (this->MultiConfig) { this->Dir.IncludeGenExp += "_$"; } @@ -383,36 +424,31 @@ bool cmQtAutoGenInitializer::InitCustomTargets() cmStrCat(this->GenTarget->GetName(), "_autogen"); // Autogen target parallel processing - this->AutogenTarget.Parallel = - this->GenTarget->GetSafeProperty("AUTOGEN_PARALLEL"); - if (this->AutogenTarget.Parallel.empty() || - (this->AutogenTarget.Parallel == "AUTO")) { - // Autodetect number of CPUs - this->AutogenTarget.Parallel = std::to_string(GetParallelCPUCount()); + { + std::string 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.cmake"); + cmStrCat(this->Dir.Info, "/AutogenInfo.json"); - this->AutogenTarget.SettingsFile = - cmStrCat(this->Dir.Info, "/AutogenOldSettings.txt"); - - if (this->MultiConfig) { - for (std::string const& cfg : this->ConfigsList) { - std::string& filename = this->AutogenTarget.ConfigSettingsFile[cfg]; - filename = - AppendFilenameSuffix(this->AutogenTarget.SettingsFile, "_" + cfg); - this->AddCleanFile(filename); - } - } else { - this->AddCleanFile(this->AutogenTarget.SettingsFile); - } + // Used settings file + ConfigFileNames(this->AutogenTarget.SettingsFile, + cmStrCat(this->Dir.Info, "/AutogenUsed"), ".txt"); + ConfigFileClean(this->AutogenTarget.SettingsFile); - this->AutogenTarget.ParseCacheFile = - cmStrCat(this->Dir.Info, "/ParseCache.txt"); - this->AddCleanFile(this->AutogenTarget.ParseCacheFile); + // Parse cache file + ConfigFileNames(this->AutogenTarget.ParseCacheFile, + cmStrCat(this->Dir.Info, "/ParseCache"), ".txt"); + ConfigFileClean(this->AutogenTarget.ParseCacheFile); } // Autogen target: Compute user defined dependencies @@ -435,9 +471,15 @@ bool cmQtAutoGenInitializer::InitCustomTargets() } } - // CMAKE_AUTOMOC_RELAXED_MODE deprecation warning if (this->Moc.Enabled) { + // Path prefix + if (cmIsOn(this->GenTarget->GetSafeProperty("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 " @@ -445,6 +487,32 @@ bool cmQtAutoGenInitializer::InitCustomTargets() "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]); + } + } } } @@ -479,20 +547,27 @@ bool cmQtAutoGenInitializer::InitCustomTargets() bool cmQtAutoGenInitializer::InitMoc() { // Mocs compilation file - this->Moc.MocsCompilation = + this->Moc.CompilationFile = cmStrCat(this->Dir.Build, "/mocs_compilation.cpp"); - // Moc predefs command + // Moc predefs if (this->GenTarget->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") && (this->QtVersion >= IntegerVersion(5, 8))) { - this->Moc.PredefsCmd = this->Makefile->GetSafeDefinition( - "CMAKE_CXX_COMPILER_PREDEFINES_COMMAND"); + // Command + cmExpandList(this->Makefile->GetSafeDefinition( + "CMAKE_CXX_COMPILER_PREDEFINES_COMMAND"), + this->Moc.PredefsCmd); + // Header + if (!this->Moc.PredefsCmd.empty()) { + ConfigFileNames(this->Moc.PredefsFile, + cmStrCat(this->Dir.Build, "/moc_predefs"), ".h"); + } } // Moc includes { SearchPathSanitizer sanitizer(this->Makefile); - auto GetIncludeDirs = + auto getDirs = [this, &sanitizer](std::string const& cfg) -> std::vector { // Get the include dirs for this target, without stripping the implicit // include dirs off, see issue #13667. @@ -504,22 +579,22 @@ bool cmQtAutoGenInitializer::InitMoc() }; // Default configuration include directories - this->Moc.Includes = GetIncludeDirs(this->ConfigDefault); + this->Moc.Includes.Default = getDirs(this->ConfigDefault); // Other configuration settings if (this->MultiConfig) { for (std::string const& cfg : this->ConfigsList) { - std::vector dirs = GetIncludeDirs(cfg); - if (dirs != this->Moc.Includes) { - this->Moc.ConfigIncludes[cfg] = std::move(dirs); + std::vector dirs = getDirs(cfg); + if (dirs == this->Moc.Includes.Default) { + continue; } + this->Moc.Includes.Config[cfg] = std::move(dirs); } } } // Moc compile definitions { - auto GetCompileDefinitions = - [this](std::string const& cfg) -> std::set { + auto getDefs = [this](std::string const& cfg) -> std::set { std::set defines; this->LocalGen->GetTargetDefines(this->GenTarget, cfg, "CXX", defines); #ifdef _WIN32 @@ -532,14 +607,15 @@ bool cmQtAutoGenInitializer::InitMoc() }; // Default configuration defines - this->Moc.Defines = GetCompileDefinitions(this->ConfigDefault); + this->Moc.Defines.Default = getDefs(this->ConfigDefault); // Other configuration defines if (this->MultiConfig) { for (std::string const& cfg : this->ConfigsList) { - std::set defines = GetCompileDefinitions(cfg); - if (defines != this->Moc.Defines) { - this->Moc.ConfigDefines[cfg] = std::move(defines); + std::set defines = getDefs(cfg); + if (defines == this->Moc.Defines.Default) { + continue; } + this->Moc.Defines.Config[cfg] = std::move(defines); } } } @@ -572,23 +648,22 @@ bool cmQtAutoGenInitializer::InitUic() } // Uic target options { - auto UicGetOpts = - [this](std::string const& cfg) -> std::vector { + auto getOpts = [this](std::string const& cfg) -> std::vector { std::vector opts; this->GenTarget->GetAutoUicOptions(opts, cfg); return opts; }; - // Default settings - this->Uic.Options = UicGetOpts(this->ConfigDefault); - - // Configuration specific settings + // Default options + this->Uic.Options.Default = getOpts(this->ConfigDefault); + // Configuration specific options if (this->MultiConfig) { for (std::string const& cfg : this->ConfigsList) { - std::vector options = UicGetOpts(cfg); - if (options != this->Uic.Options) { - this->Uic.ConfigOptions[cfg] = std::move(options); + std::vector options = getOpts(cfg); + if (options == this->Uic.Options.Default) { + continue; } + this->Uic.Options.Config[cfg] = std::move(options); } } } @@ -822,8 +897,7 @@ bool cmQtAutoGenInitializer::InitScanFiles() // Check if the .ui file has uic options std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS); if (!uicOpts.empty()) { - this->Uic.FileFiles.push_back(fullPath); - this->Uic.FileOptions.push_back(cmExpandedList(uicOpts)); + this->Uic.UiFiles.emplace_back(fullPath, cmExpandedList(uicOpts)); } } else { // Register skipped .ui file @@ -887,31 +961,17 @@ bool cmQtAutoGenInitializer::InitScanFiles() } } // Path checksum and file names - { - cmFilePathChecksum const fpathCheckSum(this->Makefile); - for (Qrc& qrc : this->Rcc.Qrcs) { - // Path checksum - qrc.QrcPathChecksum = fpathCheckSum.getPart(qrc.QrcFile); - // Output file name - qrc.OutputFile = cmStrCat(this->Dir.Build, '/', qrc.QrcPathChecksum, - "/qrc_", qrc.QrcName, ".cpp"); - { - cm::string_view const baseSuffix = qrc.Unique - ? cm::string_view() - : cm::string_view(qrc.QrcPathChecksum); - std::string const base = - cmStrCat(this->Dir.Info, "/RCC", qrc.QrcName, baseSuffix); - qrc.LockFile = cmStrCat(base, ".lock"); - qrc.InfoFile = cmStrCat(base, "Info.cmake"); - qrc.SettingsFile = cmStrCat(base, "Settings.txt"); - if (this->MultiConfig) { - for (std::string const& cfg : this->ConfigsList) { - qrc.ConfigSettingsFile[cfg] = - AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg); - } - } - } - } + 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"); + ConfigFileNames(qrc.SettingsFile, cmStrCat(base, "_Used"), ".txt"); } // rcc options for (Qrc& qrc : this->Rcc.Qrcs) { @@ -959,8 +1019,8 @@ bool cmQtAutoGenInitializer::InitAutogenTarget() // Files provided by the autogen target std::vector autogenProvides; if (this->Moc.Enabled) { - this->AddGeneratedSource(this->Moc.MocsCompilation, this->Moc, true); - autogenProvides.push_back(this->Moc.MocsCompilation); + this->AddGeneratedSource(this->Moc.CompilationFile, this->Moc, true); + autogenProvides.push_back(this->Moc.CompilationFile); } // Compose target comment @@ -1214,229 +1274,185 @@ bool cmQtAutoGenInitializer::SetupCustomTargets() bool cmQtAutoGenInitializer::SetupWriteAutogenInfo() { - InfoWriter ofs(this->AutogenTarget.InfoFile); - if (ofs) { - // Utility lambdas - auto MfDef = [this](const char* key) { - return this->Makefile->GetSafeDefinition(key); - }; + // Utility lambdas + auto MfDef = [this](std::string const& key) { + return this->Makefile->GetSafeDefinition(key); + }; - // Write common settings - ofs.Write("# Meta\n"); - ofs.Write("AM_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE"); - ofs.Write("AM_PARALLEL", this->AutogenTarget.Parallel); - ofs.Write("AM_VERBOSITY", this->Verbosity); - - ofs.Write("# Directories\n"); - ofs.Write("AM_CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR")); - ofs.Write("AM_CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR")); - ofs.Write("AM_CMAKE_CURRENT_SOURCE_DIR", - MfDef("CMAKE_CURRENT_SOURCE_DIR")); - ofs.Write("AM_CMAKE_CURRENT_BINARY_DIR", - MfDef("CMAKE_CURRENT_BINARY_DIR")); - ofs.Write("AM_BUILD_DIR", this->Dir.Build); - ofs.Write("AM_INCLUDE_DIR", this->Dir.Include); - ofs.WriteConfig("AM_INCLUDE_DIR", this->Dir.ConfigInclude); - - std::vector headers; - std::vector headersFlags; - std::vector headersBuildPaths; - std::vector sources; - std::vector sourcesFlags; - std::set moc_skip; - std::set uic_skip; - - // Filter headers - { - auto headerCount = this->AutogenTarget.Headers.size(); - headers.reserve(headerCount); - headersFlags.reserve(headerCount); + // Filtered headers and sources + std::set moc_skip; + std::set uic_skip; + std::vector headers; + std::vector sources; - std::vector sortedHeaders; - { - sortedHeaders.reserve(headerCount); - for (auto const& pair : this->AutogenTarget.Headers) { - sortedHeaders.emplace_back(pair.second.get()); - } - std::sort(sortedHeaders.begin(), sortedHeaders.end(), - [](MUFile const* a, MUFile const* b) { - return (a->FullPath < b->FullPath); - }); + // Filter headers + { + headers.reserve(this->AutogenTarget.Headers.size()); + for (auto const& pair : this->AutogenTarget.Headers) { + MUFile const* const muf = pair.second.get(); + if (muf->Generated && !this->CMP0071Accept) { + continue; } - - for (MUFile const* const muf : sortedHeaders) { - 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) { - headers.emplace_back(muf->FullPath); - headersFlags.emplace_back( - cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u')); - } + if (muf->SkipMoc) { + moc_skip.insert(muf->FullPath); } - } - // Header build paths - { - cmFilePathChecksum const fpathCheckSum(this->Makefile); - std::unordered_set emitted; - for (std::string const& hdr : headers) { - std::string const basePath = - cmStrCat(fpathCheckSum.getPart(hdr), "/moc_", - cmSystemTools::GetFilenameWithoutLastExtension(hdr)); - std::string suffix; - for (int ii = 0; ii != 1024; ++ii) { - std::string path = cmStrCat(basePath, suffix, ".cpp"); - if (emitted.emplace(path).second) { - headersBuildPaths.emplace_back(std::move(path)); - break; - } - suffix = cmStrCat('_', ii + 1); - } + if (muf->SkipUic) { + uic_skip.insert(muf->FullPath); + } + 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 - { - auto sourcesCount = this->AutogenTarget.Sources.size(); - sources.reserve(sourcesCount); - sourcesFlags.reserve(sourcesCount); - - std::vector sorted; - sorted.reserve(sourcesCount); - for (auto const& pair : this->AutogenTarget.Sources) { - sorted.emplace_back(pair.second.get()); + // 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; } - std::sort(sorted.begin(), sorted.end(), - [](MUFile const* a, MUFile const* b) { - return (a->FullPath < b->FullPath); - }); - - for (MUFile const* const muf : sorted) { - 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->FullPath); - sourcesFlags.emplace_back( - cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u')); - } + 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); + }); + } - ofs.Write("# Qt\n"); - ofs.WriteUInt("AM_QT_VERSION_MAJOR", this->QtVersion.Major); - ofs.Write("AM_QT_MOC_EXECUTABLE", this->Moc.Executable); - ofs.Write("AM_QT_UIC_EXECUTABLE", this->Uic.Executable); - - ofs.Write("# Files\n"); - ofs.Write("AM_CMAKE_EXECUTABLE", cmSystemTools::GetCMakeCommand()); - ofs.Write("AM_SETTINGS_FILE", this->AutogenTarget.SettingsFile); - ofs.WriteConfig("AM_SETTINGS_FILE", - this->AutogenTarget.ConfigSettingsFile); - ofs.Write("AM_PARSE_CACHE_FILE", this->AutogenTarget.ParseCacheFile); - ofs.WriteStrings("AM_HEADERS", headers); - ofs.WriteStrings("AM_HEADERS_FLAGS", headersFlags); - ofs.WriteStrings("AM_HEADERS_BUILD_PATHS", headersBuildPaths); - ofs.WriteStrings("AM_SOURCES", sources); - ofs.WriteStrings("AM_SOURCES_FLAGS", sourcesFlags); - - // Write moc settings - if (this->Moc.Enabled) { - ofs.Write("# MOC settings\n"); - ofs.WriteStrings("AM_MOC_SKIP", moc_skip); - ofs.WriteStrings("AM_MOC_DEFINITIONS", this->Moc.Defines); - ofs.WriteConfigStrings("AM_MOC_DEFINITIONS", this->Moc.ConfigDefines); - ofs.WriteStrings("AM_MOC_INCLUDES", this->Moc.Includes); - ofs.WriteConfigStrings("AM_MOC_INCLUDES", this->Moc.ConfigIncludes); - ofs.Write("AM_MOC_OPTIONS", - this->GenTarget->GetSafeProperty("AUTOMOC_MOC_OPTIONS")); - ofs.Write("AM_MOC_RELAXED_MODE", MfDef("CMAKE_AUTOMOC_RELAXED_MODE")); - ofs.Write("AM_MOC_PATH_PREFIX", - this->GenTarget->GetSafeProperty("AUTOMOC_PATH_PREFIX")); - ofs.Write("AM_MOC_MACRO_NAMES", - this->GenTarget->GetSafeProperty("AUTOMOC_MACRO_NAMES")); - ofs.Write("AM_MOC_DEPEND_FILTERS", - this->GenTarget->GetSafeProperty("AUTOMOC_DEPEND_FILTERS")); - ofs.Write("AM_MOC_PREDEFS_CMD", this->Moc.PredefsCmd); - } - - // Write uic settings - if (this->Uic.Enabled) { - // Add skipped .ui files - uic_skip.insert(this->Uic.SkipUi.begin(), this->Uic.SkipUi.end()); + // 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.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.SetArray("HEADER_EXTENSIONS", + this->Makefile->GetCMakeInstance()->GetHeaderExtensions()); + info.SetArrayArray( + "HEADERS", headers, [this](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] = this->GetMocBuildPath(*muf); + }); + info.SetArrayArray( + "SOURCES", sources, [](Json::Value& jval, MUFile const* muf) { + jval.resize(2u); + jval[0u] = muf->FullPath; + jval[1u] = cmStrCat(muf->MocIt ? 'M' : 'm', muf->UicIt ? 'U' : 'u'); + }); + + // 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 const& pair) { + jval.resize(2u); + jval[0u] = pair.first; + jval[1u] = pair.second; + }); + info.Set("MOC_COMPILATION_FILE", this->Moc.CompilationFile); + info.SetArray("MOC_PREDEFS_CMD", this->Moc.PredefsCmd); + info.SetConfig("MOC_PREDEFS_FILE", this->Moc.PredefsFile); + } - ofs.Write("# UIC settings\n"); - ofs.WriteStrings("AM_UIC_SKIP", uic_skip); - ofs.WriteStrings("AM_UIC_TARGET_OPTIONS", this->Uic.Options); - ofs.WriteConfigStrings("AM_UIC_TARGET_OPTIONS", this->Uic.ConfigOptions); - ofs.WriteStrings("AM_UIC_OPTIONS_FILES", this->Uic.FileFiles); - ofs.WriteNestedLists("AM_UIC_OPTIONS_OPTIONS", this->Uic.FileOptions); - ofs.WriteStrings("AM_UIC_SEARCH_PATHS", this->Uic.SearchPaths); - } - } else { - cmSystemTools::Error(cmStrCat("AutoGen: Could not write file ", - this->AutogenTarget.InfoFile)); - return false; + // 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.UiFiles, + [](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) { - InfoWriter ofs(qrc.InfoFile); - if (ofs) { - // Utility lambdas - auto MfDef = [this](const char* key) { - return this->Makefile->GetSafeDefinition(key); - }; + // Utility lambdas + auto MfDef = [this](std::string const& key) { + return this->Makefile->GetSafeDefinition(key); + }; - // Write - ofs.Write("# Configurations\n"); - ofs.Write("ARCC_MULTI_CONFIG", this->MultiConfig ? "TRUE" : "FALSE"); - ofs.Write("ARCC_VERBOSITY", this->Verbosity); - ofs.Write("# Settings file\n"); - ofs.Write("ARCC_SETTINGS_FILE", qrc.SettingsFile); - ofs.WriteConfig("ARCC_SETTINGS_FILE", qrc.ConfigSettingsFile); - - ofs.Write("# Directories\n"); - ofs.Write("ARCC_CMAKE_SOURCE_DIR", MfDef("CMAKE_SOURCE_DIR")); - ofs.Write("ARCC_CMAKE_BINARY_DIR", MfDef("CMAKE_BINARY_DIR")); - ofs.Write("ARCC_BUILD_DIR", this->Dir.Build); - ofs.Write("ARCC_INCLUDE_DIR", this->Dir.Include); - ofs.WriteConfig("ARCC_INCLUDE_DIR", this->Dir.ConfigInclude); - - ofs.Write("# Rcc executable\n"); - ofs.Write("ARCC_RCC_EXECUTABLE", this->Rcc.Executable); - ofs.WriteStrings("ARCC_RCC_LIST_OPTIONS", - this->Rcc.ExecutableFeatures->ListOptions); - - ofs.Write("# Rcc job\n"); - ofs.Write("ARCC_LOCK_FILE", qrc.LockFile); - ofs.Write("ARCC_SOURCE", qrc.QrcFile); - ofs.Write("ARCC_OUTPUT_CHECKSUM", qrc.QrcPathChecksum); - ofs.Write("ARCC_OUTPUT_NAME", - cmSystemTools::GetFilenameName(qrc.OutputFile)); - ofs.WriteStrings("ARCC_OPTIONS", qrc.Options); - ofs.WriteStrings("ARCC_INPUTS", qrc.Resources); - } else { - cmSystemTools::Error( - cmStrCat("AutoRcc: Could not write file ", qrc.InfoFile)); - return false; - } + 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; @@ -1509,6 +1525,28 @@ void cmQtAutoGenInitializer::AddCleanFile(std::string const& fileName) fileName.c_str(), 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::ConfigFileClean(ConfigString& configString) +{ + this->AddCleanFile(configString.Default); + if (this->MultiConfig) { + for (auto const& pair : configString.Config) { + this->AddCleanFile(pair.second); + } + } +} + std::pair cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target) { @@ -1581,6 +1619,30 @@ cmQtAutoGenInitializer::GetQtVersion(cmGeneratorTarget const* target) return res; } +std::string cmQtAutoGenInitializer::GetMocBuildPath(MUFile const& muf) +{ + std::string res; + if (!muf.MocIt) { + return res; + } + { + std::string const basePath = + cmStrCat(this->PathCheckSum.getPart(muf.FullPath), "/moc_", + FileNameWithoutLastExtension(muf.FullPath)); + std::string suffix; + constexpr std::size_t num_tries_max = 256; + for (std::size_t ii = 0; ii != num_tries_max; ++ii) { + res = cmStrCat(basePath, suffix, ".cpp"); + if (this->Moc.EmittedBuildPaths.emplace(res).second) { + break; + } + // Compute new suffix + suffix = cmStrCat('_', ii + 1); + } + } + return res; +} + bool cmQtAutoGenInitializer::GetQtExecutable(GenVarsT& genVars, const std::string& executable, bool ignoreMissingTarget) const diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h index 2411936..0580ddb 100644 --- a/Source/cmQtAutoGenInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -4,17 +4,16 @@ #define cmQtAutoGenInitializer_h #include "cmConfigure.h" // IWYU pragma: keep -#include "cmGeneratedFileStream.h" +#include "cmFilePathChecksum.h" #include "cmQtAutoGen.h" #include -#include #include -#include #include #include #include +#include #include #include @@ -32,6 +31,23 @@ class cmTarget; class cmQtAutoGenInitializer : public cmQtAutoGen { public: + /** String value with per configuration variants. */ + class ConfigString + { + public: + std::string Default; + std::unordered_map Config; + }; + + /** String values with per configuration variants. */ + template + class ConfigStrings + { + public: + C Default; + std::unordered_map Config; + }; + /** rcc job. */ class Qrc { @@ -41,8 +57,7 @@ public: std::string QrcName; std::string QrcPathChecksum; std::string InfoFile; - std::string SettingsFile; - std::map ConfigSettingsFile; + ConfigString SettingsFile; std::string OutputFile; bool Generated = false; bool Unique = false; @@ -63,7 +78,7 @@ public: }; using MUFileHandle = std::unique_ptr; - /** Abstract moc/uic/rcc generator variables base class */ + /** Abstract moc/uic/rcc generator variables base class. */ struct GenVarsT { bool Enabled = false; @@ -81,39 +96,6 @@ public: , GenNameUpper(cmQtAutoGen::GeneratorNameUpper(gen)){}; }; - /** Writes a CMake info file. */ - class InfoWriter - { - public: - /** Open the given file. */ - InfoWriter(std::string const& filename); - - /** @return True if the file is open. */ - explicit operator bool() const { return static_cast(Ofs_); } - - void Write(cm::string_view text) { Ofs_ << text; } - void Write(cm::string_view, std::string const& value); - void WriteUInt(cm::string_view, unsigned int value); - - template - void WriteStrings(cm::string_view, C const& container); - void WriteConfig(cm::string_view, - std::map const& map); - template - void WriteConfigStrings(cm::string_view, - std::map const& map); - void WriteNestedLists(cm::string_view, - std::vector> const& lists); - - private: - template - static std::string ListJoin(IT it_begin, IT it_end); - static std::string ConfigKey(cm::string_view, std::string const& config); - - private: - cmGeneratedFileStream Ofs_; - }; - public: /** @return The detected Qt version and the required Qt major version. */ static std::pair GetQtVersion( @@ -153,6 +135,12 @@ private: cm::string_view genNameUpper); void AddCleanFile(std::string const& fileName); + void ConfigFileNames(ConfigString& configString, cm::string_view prefix, + cm::string_view suffix); + void ConfigFileClean(ConfigString& configString); + + std::string GetMocBuildPath(MUFile const& muf); + bool GetQtExecutable(GenVarsT& genVars, const std::string& executable, bool ignoreMissingTarget) const; @@ -162,16 +150,17 @@ private: cmGlobalGenerator* GlobalGen = nullptr; cmLocalGenerator* LocalGen = nullptr; cmMakefile* Makefile = nullptr; + cmFilePathChecksum const PathCheckSum; // -- Configuration IntegerVersion QtVersion; + unsigned int Verbosity = 0; bool MultiConfig = false; + bool CMP0071Accept = false; + bool CMP0071Warn = false; std::string ConfigDefault; std::vector ConfigsList; - std::string Verbosity; std::string TargetsFolder; - bool CMP0071Accept = false; - bool CMP0071Warn = false; /** Common directories. */ struct @@ -179,8 +168,7 @@ private: std::string Info; std::string Build; std::string Work; - std::string Include; - std::map ConfigInclude; + ConfigString Include; std::string IncludeGenExp; } Dir; @@ -190,12 +178,11 @@ private: std::string Name; bool GlobalTarget = false; // Settings - std::string Parallel; + unsigned int Parallel = 1; // Configuration files std::string InfoFile; - std::string SettingsFile; - std::string ParseCacheFile; - std::map ConfigSettingsFile; + ConfigString SettingsFile; + ConfigString ParseCacheFile; // Dependencies bool DependOrigin = false; std::set DependFiles; @@ -212,26 +199,37 @@ private: MocT() : GenVarsT(GenT::MOC){}; - std::string PredefsCmd; - std::vector Includes; - std::map> ConfigIncludes; - std::set Defines; - std::map> ConfigDefines; - std::string MocsCompilation; + bool RelaxedMode = false; + bool PathPrefix = false; + std::string CompilationFile; + // Compiler implicit pre defines + std::vector PredefsCmd; + ConfigString PredefsFile; + // Defines + ConfigStrings> Defines; + // Includes + ConfigStrings> Includes; + // Options + std::vector Options; + // Filters + std::vector MacroNames; + std::vector> DependFilters; + // Utility + std::unordered_set EmittedBuildPaths; } Moc; /** uic variables. */ struct UicT : public GenVarsT { + using UiFileT = std::pair>; + UicT() : GenVarsT(GenT::UIC){}; std::set SkipUi; + std::vector UiFiles; + ConfigStrings> Options; std::vector SearchPaths; - std::vector Options; - std::map> ConfigOptions; - std::vector FileFiles; - std::vector> FileOptions; } Uic; /** rcc variables. */ diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx index eb829fa..a9d4fee 100644 --- a/Source/cmQtAutoGenerator.cxx +++ b/Source/cmQtAutoGenerator.cxx @@ -2,19 +2,11 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmQtAutoGenerator.h" -#include - -#include "cmsys/FStream.hxx" - -#include "cmGlobalGenerator.h" -#include "cmMakefile.h" #include "cmQtAutoGen.h" -#include "cmState.h" -#include "cmStateDirectory.h" -#include "cmStateSnapshot.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmake.h" +#include "cm_jsoncpp_reader.h" +#include "cmsys/FStream.hxx" cmQtAutoGenerator::Logger::Logger() { @@ -44,13 +36,10 @@ cmQtAutoGenerator::Logger::Logger() cmQtAutoGenerator::Logger::~Logger() = default; -void cmQtAutoGenerator::Logger::RaiseVerbosity(std::string const& value) +void cmQtAutoGenerator::Logger::RaiseVerbosity(unsigned int value) { - unsigned long verbosity = 0; - if (cmStrToULong(value, &verbosity)) { - if (this->Verbosity_ < verbosity) { - this->Verbosity_ = static_cast(verbosity); - } + if (this->Verbosity_ < value) { + this->Verbosity_ = value; } } @@ -214,7 +203,10 @@ bool cmQtAutoGenerator::FileDiffers(std::string const& filename, return differs; } -cmQtAutoGenerator::cmQtAutoGenerator() = default; +cmQtAutoGenerator::cmQtAutoGenerator(GenT genType) + : GenType_(genType) +{ +} cmQtAutoGenerator::~cmQtAutoGenerator() = default; @@ -223,7 +215,7 @@ bool cmQtAutoGenerator::Run(std::string const& infoFile, { // Info settings InfoFile_ = infoFile; - cmSystemTools::ConvertToUnixSlashes(InfoFile_); + cmSystemTools::CollapseFullPath(InfoFile_); if (!InfoFileTime_.Load(InfoFile_)) { cmSystemTools::Stderr(cmStrCat("AutoGen: The info file ", Quoted(InfoFile_), " is not readable\n")); @@ -232,29 +224,221 @@ bool cmQtAutoGenerator::Run(std::string const& infoFile, InfoDir_ = cmSystemTools::GetFilenamePath(infoFile); InfoConfig_ = config; - bool success = false; + // Read info file { - cmake cm(cmake::RoleScript, cmState::Unknown); - cm.SetHomeOutputDirectory(InfoDir()); - cm.SetHomeDirectory(InfoDir()); - cm.GetCurrentSnapshot().SetDefaultDefinitions(); - cmGlobalGenerator gg(&cm); - - cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); - snapshot.GetDirectory().SetCurrentBinary(InfoDir()); - snapshot.GetDirectory().SetCurrentSource(InfoDir()); - - auto makefile = cm::make_unique(&gg, snapshot); - // The OLD/WARN behavior for policy CMP0053 caused a speed regression. - // https://gitlab.kitware.com/cmake/cmake/issues/17570 - makefile->SetPolicyVersion("3.9", std::string()); - gg.SetCurrentMakefile(makefile.get()); - success = this->Init(makefile.get()); + cmsys::ifstream ifs(InfoFile_.c_str(), (std::ios::in | std::ios::binary)); + if (!ifs) { + Log().Error(GenType_, + cmStrCat("Could not to open info file ", Quoted(InfoFile_))); + return false; + } + try { + ifs >> Info_; + } catch (...) { + Log().Error(GenType_, + cmStrCat("Could not read info file ", Quoted(InfoFile_))); + return false; + } } - if (success) { - success = this->Process(); + // Info: setup logger + { + unsigned int value = 0; + if (!InfoUInt("VERBOSITY", value, false)) { + return false; + } + Logger_.RaiseVerbosity(value); } - return success; + // Info: setup project directories + if (!InfoString("CMAKE_SOURCE_DIR", ProjectDirs_.Source, true) || + !InfoString("CMAKE_BINARY_DIR", ProjectDirs_.Binary, true) || + !InfoString("CMAKE_CURRENT_SOURCE_DIR", ProjectDirs_.CurrentSource, + true) || + !InfoString("CMAKE_CURRENT_BINARY_DIR", ProjectDirs_.CurrentBinary, + true)) { + return false; + } + + if (!this->InitFromInfo()) { + return false; + } + // Clear info + Info_ = Json::nullValue; + + return this->Process(); +} + +bool cmQtAutoGenerator::LogInfoError(GenT genType, + cm::string_view message) const +{ + this->Log().Error( + genType, + cmStrCat("Info error in info file\n", Quoted(InfoFile()), ":\n", message)); + return false; +} + +bool cmQtAutoGenerator::LogInfoError(cm::string_view message) const +{ + return LogInfoError(GenType_, message); +} + +bool cmQtAutoGenerator::JsonGetArray(std::vector& list, + Json::Value const& jval) +{ + Json::ArrayIndex const arraySize = jval.size(); + if (arraySize == 0) { + return false; + } + + bool picked = false; + list.reserve(list.size() + arraySize); + for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { + Json::Value const& ival = jval[ii]; + if (ival.isString()) { + list.emplace_back(ival.asString()); + picked = true; + } + } + return picked; +} + +bool cmQtAutoGenerator::JsonGetArray(std::unordered_set& list, + Json::Value const& jval) +{ + Json::ArrayIndex const arraySize = jval.size(); + if (arraySize == 0) { + return false; + } + + bool picked = false; + list.reserve(list.size() + arraySize); + for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { + Json::Value const& ival = jval[ii]; + if (ival.isString()) { + list.emplace(ival.asString()); + picked = true; + } + } + return picked; +} + +std::string cmQtAutoGenerator::InfoConfigKey(std::string const& key) const +{ + return cmStrCat(key, '_', InfoConfig()); +} + +bool cmQtAutoGenerator::InfoString(std::string const& key, std::string& value, + bool required) const +{ + Json::Value const& jval = Info()[key]; + if (!jval.isString()) { + if (!jval.isNull() || required) { + return LogInfoError(cmStrCat(key, " is not a string.")); + } + } else { + value = jval.asString(); + if (value.empty() && required) { + return LogInfoError(cmStrCat(key, " is empty.")); + } + } + return true; +} + +bool cmQtAutoGenerator::InfoStringConfig(std::string const& key, + std::string& value, + + bool required) const +{ + { // Try config + std::string const configKey = InfoConfigKey(key); + Json::Value const& jval = Info_[configKey]; + if (!jval.isNull()) { + if (!jval.isString()) { + return LogInfoError(cmStrCat(configKey, " is not a string.")); + } + value = jval.asString(); + if (required && value.empty()) { + return LogInfoError(cmStrCat(configKey, " is empty.")); + } + return true; + } + } + // Try plain + return InfoString(key, value, required); +} + +bool cmQtAutoGenerator::InfoBool(std::string const& key, bool& value, + bool required) const +{ + Json::Value const& jval = Info()[key]; + if (jval.isBool()) { + value = jval.asBool(); + } else { + if (!jval.isNull() || required) { + return LogInfoError(cmStrCat(key, " is not a boolean.")); + } + } + return true; +} + +bool cmQtAutoGenerator::InfoUInt(std::string const& key, unsigned int& value, + bool required) const +{ + Json::Value const& jval = Info()[key]; + if (jval.isUInt()) { + value = jval.asUInt(); + } else { + if (!jval.isNull() || required) { + return LogInfoError(cmStrCat(key, " is not an unsigned integer.")); + } + } + return true; +} + +bool cmQtAutoGenerator::InfoArray(std::string const& key, + std::vector& list, + bool required) const +{ + Json::Value const& jval = Info()[key]; + if (!jval.isArray()) { + if (!jval.isNull() || required) { + return LogInfoError(cmStrCat(key, " is not an array.")); + } + } + return JsonGetArray(list, jval) || !required; +} + +bool cmQtAutoGenerator::InfoArray(std::string const& key, + std::unordered_set& list, + bool required) const +{ + Json::Value const& jval = Info()[key]; + if (!jval.isArray()) { + if (!jval.isNull() || required) { + return LogInfoError(cmStrCat(key, " is not an array.")); + } + } + return JsonGetArray(list, jval) || !required; +} + +bool cmQtAutoGenerator::InfoArrayConfig(std::string const& key, + std::vector& list, + bool required) const +{ + { // Try config + std::string const configKey = InfoConfigKey(key); + Json::Value const& jval = Info()[configKey]; + if (!jval.isNull()) { + if (!jval.isArray()) { + return LogInfoError(cmStrCat(configKey, " is not an array string.")); + } + if (!JsonGetArray(list, jval) && required) { + return LogInfoError(cmStrCat(configKey, " is empty.")); + } + return true; + } + } + // Try plain + return InfoArray(key, list, required); } std::string cmQtAutoGenerator::SettingsFind(std::string const& content, diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h index f60acb0..7f83fc6 100644 --- a/Source/cmQtAutoGenerator.h +++ b/Source/cmQtAutoGenerator.h @@ -7,15 +7,15 @@ #include "cmFileTime.h" #include "cmQtAutoGen.h" +#include "cm_jsoncpp_value.h" #include #include #include +#include #include -class cmMakefile; - /** \class cmQtAutoGenerator * \brief Base class for QtAutoGen generators */ @@ -34,7 +34,7 @@ public: // -- Verbosity unsigned int Verbosity() const { return this->Verbosity_; } void SetVerbosity(unsigned int value) { this->Verbosity_ = value; } - void RaiseVerbosity(std::string const& value); + void RaiseVerbosity(unsigned int value); bool Verbose() const { return (this->Verbosity_ != 0); } void SetVerbose(bool value) { this->Verbosity_ = value ? 1 : 0; } // -- Color output @@ -80,7 +80,7 @@ public: public: // -- Constructors - cmQtAutoGenerator(); + cmQtAutoGenerator(GenT genType); virtual ~cmQtAutoGenerator(); cmQtAutoGenerator(cmQtAutoGenerator const&) = delete; @@ -91,10 +91,39 @@ public: // -- InfoFile std::string const& InfoFile() const { return InfoFile_; } + Json::Value const& Info() const { return Info_; } cmFileTime const& InfoFileTime() const { return InfoFileTime_; } std::string const& InfoDir() const { return InfoDir_; } std::string const& InfoConfig() const { return InfoConfig_; } + bool LogInfoError(GenT genType, cm::string_view message) const; + bool LogInfoError(cm::string_view message) const; + + /** Returns true if strings were appended to the list. */ + static bool JsonGetArray(std::vector& list, + Json::Value const& jval); + /** Returns true if strings were found in the JSON array. */ + static bool JsonGetArray(std::unordered_set& list, + Json::Value const& jval); + + std::string InfoConfigKey(std::string const& key) const; + + /** Returns false if the JSON value isn't a string. */ + bool InfoString(std::string const& key, std::string& value, + bool required) const; + bool InfoStringConfig(std::string const& key, std::string& value, + bool required) const; + bool InfoBool(std::string const& key, bool& value, bool required) const; + bool InfoUInt(std::string const& key, unsigned int& value, + bool required) const; + /** Returns false if the JSON value isn't an array. */ + bool InfoArray(std::string const& key, std::vector& list, + bool required) const; + bool InfoArray(std::string const& key, std::unordered_set& list, + bool required) const; + bool InfoArrayConfig(std::string const& key, std::vector& list, + bool required) const; + // -- Directories ProjectDirsT const& ProjectDirs() const { return ProjectDirs_; } @@ -104,16 +133,22 @@ public: protected: // -- Abstract processing interface - virtual bool Init(cmMakefile* makefile) = 0; + virtual bool InitFromInfo() = 0; virtual bool Process() = 0; - ProjectDirsT& ProjectDirsRef() { return ProjectDirs_; } + // - Utility classes + Logger const& Log() const { return Logger_; } private: - // -- Info settings + // -- Generator type + GenT GenType_; + // -- Logging + Logger Logger_; + // -- Info file std::string InfoFile_; cmFileTime InfoFileTime_; std::string InfoDir_; std::string InfoConfig_; + Json::Value Info_; // -- Directories ProjectDirsT ProjectDirs_; }; diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 5cd1ba1..5f3cd5f 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -11,19 +11,22 @@ #include "cmAlgorithms.h" #include "cmCryptoHash.h" #include "cmGeneratedFileStream.h" -#include "cmMakefile.h" #include "cmQtAutoGen.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" -#include "cmake.h" +#include "cm_jsoncpp_value.h" #include "cmsys/FStream.hxx" #if defined(__APPLE__) # include #endif -static constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_" -static constexpr std::size_t UiUnderscoreLength = 3; // Length of "ui_" +namespace { + +constexpr std::size_t MocUnderscoreLength = 4; // Length of "moc_" +constexpr std::size_t UiUnderscoreLength = 3; // Length of "ui_" + +} // End of unnamed namespace cmQtAutoMocUic::IncludeKeyT::IncludeKeyT(std::string const& key, std::size_t basePrefixLength) @@ -1464,10 +1467,10 @@ void cmQtAutoMocUic::JobCompileUicT::Process() std::vector cmd; cmd.push_back(UicConst().Executable); { - std::vector allOpts = UicConst().TargetOptions; - auto optionIt = UicConst().Options.find(sourceFile); - if (optionIt != UicConst().Options.end()) { - UicMergeOptions(allOpts, optionIt->second, + std::vector allOpts = UicConst().Options; + auto optionIt = UicConst().UiFiles.find(sourceFile); + if (optionIt != UicConst().UiFiles.end()) { + UicMergeOptions(allOpts, optionIt->second.Options, (BaseConst().QtVersionMajor == 5)); } cmAppend(cmd, allOpts); @@ -1548,338 +1551,310 @@ void cmQtAutoMocUic::JobFinishT::Process() Gen()->AbortSuccess(); } -cmQtAutoMocUic::cmQtAutoMocUic() = default; +cmQtAutoMocUic::cmQtAutoMocUic() + : cmQtAutoGenerator(GenT::GEN) +{ +} cmQtAutoMocUic::~cmQtAutoMocUic() = default; -bool cmQtAutoMocUic::Init(cmMakefile* makefile) +bool cmQtAutoMocUic::InitFromInfo() { - // Utility lambdas - auto InfoGet = [makefile](cm::string_view key) { - return makefile->GetSafeDefinition(std::string(key)); - }; - auto InfoGetBool = [makefile](cm::string_view key) { - return makefile->IsOn(std::string(key)); - }; - auto InfoGetList = - [makefile](cm::string_view key) -> std::vector { - return cmExpandedList(makefile->GetSafeDefinition(std::string(key))); - }; - auto InfoGetLists = - [makefile](cm::string_view key) -> std::vector> { - std::vector> lists; - { - std::string const value = makefile->GetSafeDefinition(std::string(key)); - std::string::size_type pos = 0; - while (pos < value.size()) { - std::string::size_type next = value.find(ListSep, pos); - std::string::size_type length = - (next != std::string::npos) ? next - pos : value.size() - pos; - // Remove enclosing braces - if (length >= 2) { - std::string::const_iterator itBeg = value.begin() + (pos + 1); - std::string::const_iterator itEnd = itBeg + (length - 2); - lists.emplace_back(cmExpandedList(std::string(itBeg, itEnd))); - } - pos += length; - pos += ListSep.size(); - } - } - return lists; - }; - auto InfoGetConfig = [makefile, this](cm::string_view key) -> std::string { - if (const char* valueConf = - makefile->GetDefinition(cmStrCat(key, '_', InfoConfig()))) { - return std::string(valueConf); - } - return makefile->GetSafeDefinition(std::string(key)); - }; - auto InfoGetConfigList = - [&InfoGetConfig](cm::string_view key) -> std::vector { - return cmExpandedList(InfoGetConfig(key)); - }; - auto LogInfoError = [this](cm::string_view msg) -> bool { - this->Log().Error(GenT::GEN, - cmStrCat("In ", Quoted(this->InfoFile()), ":\n", msg)); + // -- Required settings + if (!InfoBool("MULTI_CONFIG", BaseConst_.MultiConfig, true) || + !InfoUInt("QT_VERSION_MAJOR", BaseConst_.QtVersionMajor, true) || + !InfoUInt("PARALLEL", BaseConst_.ThreadCount, false) || + !InfoString("BUILD_DIR", BaseConst_.AutogenBuildDir, true) || + !InfoStringConfig("INCLUDE_DIR", BaseConst_.AutogenIncludeDir, true) || + !InfoString("CMAKE_EXECUTABLE", BaseConst_.CMakeExecutable, true) || + !InfoStringConfig("PARSE_CACHE_FILE", BaseConst_.ParseCacheFile, true) || + !InfoStringConfig("SETTINGS_FILE", SettingsFile_, true) || + !InfoArray("HEADER_EXTENSIONS", BaseConst_.HeaderExtensions, true) || + !InfoString("QT_MOC_EXECUTABLE", MocConst_.Executable, false) || + !InfoString("QT_UIC_EXECUTABLE", UicConst_.Executable, false)) { return false; - }; - auto MatchSizes = [&LogInfoError](cm::string_view keyA, cm::string_view keyB, - std::size_t sizeA, - std::size_t sizeB) -> bool { - if (sizeA == sizeB) { - return true; - } - return LogInfoError(cmStrCat("Lists sizes mismatch ", keyA, '(', sizeA, - ") ", keyB, '(', sizeB, ')')); - }; - - // -- Read info file - if (!makefile->ReadListFile(InfoFile())) { - return LogInfoError("File processing failed"); - } - - // -- Meta - Logger_.RaiseVerbosity(InfoGet("AM_VERBOSITY")); - BaseConst_.MultiConfig = InfoGetBool("AM_MULTI_CONFIG"); - { - unsigned long num = 1; - if (cmStrToULong(InfoGet("AM_PARALLEL"), &num)) { - num = std::max(num, 1); - num = std::min(num, ParallelMax); - } - WorkerPool_.SetThreadCount(static_cast(num)); } - BaseConst_.HeaderExtensions = - makefile->GetCMakeInstance()->GetHeaderExtensions(); - // - Files and directories - ProjectDirsRef().Source = InfoGet("AM_CMAKE_SOURCE_DIR"); - ProjectDirsRef().Binary = InfoGet("AM_CMAKE_BINARY_DIR"); - ProjectDirsRef().CurrentSource = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); - ProjectDirsRef().CurrentBinary = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); - BaseConst_.AutogenBuildDir = InfoGet("AM_BUILD_DIR"); - if (BaseConst_.AutogenBuildDir.empty()) { - return LogInfoError("Autogen build directory missing."); - } - BaseConst_.AutogenIncludeDir = InfoGetConfig("AM_INCLUDE_DIR"); - if (BaseConst_.AutogenIncludeDir.empty()) { - return LogInfoError("Autogen include directory missing."); - } - BaseConst_.CMakeExecutable = InfoGetConfig("AM_CMAKE_EXECUTABLE"); - if (BaseConst_.CMakeExecutable.empty()) { - return LogInfoError("CMake executable file name missing."); - } + // -- Checks if (!BaseConst_.CMakeExecutableTime.Load(BaseConst_.CMakeExecutable)) { return LogInfoError(cmStrCat("The CMake executable ", MessagePath(BaseConst_.CMakeExecutable), " does not exist.")); } - BaseConst_.ParseCacheFile = InfoGetConfig("AM_PARSE_CACHE_FILE"); - if (BaseConst_.ParseCacheFile.empty()) { - return LogInfoError("Parse cache file name missing."); - } - // - Settings file - SettingsFile_ = InfoGetConfig("AM_SETTINGS_FILE"); - if (SettingsFile_.empty()) { - return LogInfoError("Settings file name missing."); - } + // -- Evaluate values + BaseConst_.ThreadCount = std::min(BaseConst_.ThreadCount, ParallelMax); + WorkerPool_.SetThreadCount(BaseConst_.ThreadCount); - // - Qt environment - { - unsigned long qtv = BaseConst_.QtVersionMajor; - if (cmStrToULong(InfoGet("AM_QT_VERSION_MAJOR"), &qtv)) { - BaseConst_.QtVersionMajor = static_cast(qtv); - } - } - - // - Moc - MocConst_.Executable = InfoGet("AM_QT_MOC_EXECUTABLE"); - if (!MocConst().Executable.empty()) { + // -- Moc + if (!MocConst_.Executable.empty()) { + // -- Moc is enabled MocConst_.Enabled = true; - // Load the executable file time - if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) { - return LogInfoError(cmStrCat("The moc executable ", - MessagePath(MocConst_.Executable), - " does not exist.")); - } - for (std::string& sfl : InfoGetList("AM_MOC_SKIP")) { - MocConst_.SkipList.insert(std::move(sfl)); - } - MocConst_.Definitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); - MocConst_.IncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); - MocConst_.OptionsExtra = InfoGetList("AM_MOC_OPTIONS"); - MocConst_.RelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); - MocConst_.PathPrefix = InfoGetBool("AM_MOC_PATH_PREFIX"); + // -- Temporary buffers + struct + { + std::vector MacroNames; + std::vector DependFilters; + } tmp; + + // -- Required settings + if (!InfoBool("MOC_RELAXED_MODE", MocConst_.RelaxedMode, false) || + !InfoBool("MOC_PATH_PREFIX", MocConst_.PathPrefix, true) || + !InfoArray("MOC_SKIP", MocConst_.SkipList, false) || + !InfoArrayConfig("MOC_DEFINITIONS", MocConst_.Definitions, false) || + !InfoArrayConfig("MOC_INCLUDES", MocConst_.IncludePaths, false) || + !InfoArray("MOC_OPTIONS", MocConst_.OptionsExtra, false) || + !InfoStringConfig("MOC_COMPILATION_FILE", MocConst_.CompFileAbs, + true) || + !InfoArray("MOC_PREDEFS_CMD", MocConst_.PredefsCmd, false) || + !InfoStringConfig("MOC_PREDEFS_FILE", MocConst_.PredefsFileAbs, + !MocConst_.PredefsCmd.empty()) || + !InfoArray("MOC_MACRO_NAMES", tmp.MacroNames, true) || + !InfoArray("MOC_DEPEND_FILTERS", tmp.DependFilters, false)) { + return false; + } - for (std::string const& item : InfoGetList("AM_MOC_MACRO_NAMES")) { + // -- Evaluate settings + for (std::string const& item : tmp.MacroNames) { MocConst_.MacroFilters.emplace_back( item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); } + // Dependency filters { - auto addFilter = [this, &LogInfoError](std::string const& key, - std::string const& exp) -> bool { - auto filterErr = [&LogInfoError, &key, - &exp](cm::string_view err) -> bool { - return LogInfoError(cmStrCat("AUTOMOC_DEPEND_FILTERS: ", err, '\n', - " Key: ", Quoted(key), '\n', - " Exp: ", Quoted(exp), '\n')); + Json::Value const& val = Info()["MOC_DEPEND_FILTERS"]; + if (!val.isArray()) { + return LogInfoError("MOC_DEPEND_FILTERS JSON value is not an array."); + } + Json::ArrayIndex const arraySize = val.size(); + for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { + // Test entry closure + auto testEntry = [this, ii](bool test, + cm::string_view message) -> bool { + if (!test) { + this->LogInfoError( + cmStrCat("MOC_DEPEND_FILTERS filter ", ii, ": ", message)); + } + return !test; }; - if (key.empty()) { - return filterErr("Key is empty"); + + Json::Value const& pairVal = val[ii]; + + if (testEntry(pairVal.isArray(), "JSON value is not an array.") || + testEntry(pairVal.size() == 2, "JSON array size invalid.")) { + return false; } - if (exp.empty()) { - return filterErr("Regular expression is empty"); + + Json::Value const& keyVal = pairVal[0u]; + Json::Value const& expVal = pairVal[1u]; + if (testEntry(keyVal.isString(), + "JSON value for keyword is not a string.") || + testEntry(expVal.isString(), + "JSON value for regular expression is not a string.")) { + return false; } - this->MocConst_.DependFilters.emplace_back(key, exp); - if (!this->MocConst_.DependFilters.back().Exp.is_valid()) { - return filterErr("Regular expression compiling failed"); + + std::string const key = keyVal.asString(); + std::string const exp = expVal.asString(); + if (testEntry(!key.empty(), "Keyword is empty.") || + testEntry(!exp.empty(), "Regular expression is empty.")) { + return false; } - return true; - }; - // Insert default filter for Q_PLUGIN_METADATA - if (BaseConst().QtVersionMajor != 4) { - if (!addFilter("Q_PLUGIN_METADATA", - "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" - "[^\\)]*FILE[ \t]*\"([^\"]+)\"")) { + this->MocConst_.DependFilters.emplace_back(key, exp); + if (testEntry( + this->MocConst_.DependFilters.back().Exp.is_valid(), + cmStrCat("Regular expression compilation failed.\nKeyword: ", + Quoted(key), "\nExpression: ", Quoted(exp)))) { return false; } } - // Insert user defined dependency filters - std::vector flts = InfoGetList("AM_MOC_DEPEND_FILTERS"); - if ((flts.size() % 2) != 0) { - return LogInfoError( - "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); + } + // Check if moc executable exists (by reading the file time) + if (!MocConst_.ExecutableTime.Load(MocConst_.Executable)) { + return LogInfoError(cmStrCat("The moc executable ", + MessagePath(MocConst_.Executable), + " does not exist.")); + } + } + + // -- Uic + if (!UicConst_.Executable.empty()) { + // Uic is enabled + UicConst_.Enabled = true; + + // -- Required settings + if (!InfoArray("UIC_SKIP", UicConst_.SkipList, false) || + !InfoArray("UIC_SEARCH_PATHS", UicConst_.SearchPaths, false) || + !InfoArrayConfig("UIC_OPTIONS", UicConst_.Options, false)) { + return false; + } + // .ui files + { + Json::Value const& val = Info()["UIC_UI_FILES"]; + if (!val.isArray()) { + return LogInfoError("UIC_UI_FILES JSON value is not an array."); } - for (auto itC = flts.begin(), itE = flts.end(); itC != itE; itC += 2) { - if (!addFilter(*itC, *(itC + 1))) { + Json::ArrayIndex const arraySize = val.size(); + for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { + // Test entry closure + auto testEntry = [this, ii](bool test, + cm::string_view message) -> bool { + if (!test) { + this->LogInfoError( + cmStrCat("UIC_UI_FILES entry ", ii, ": ", message)); + } + return !test; + }; + + Json::Value const& entry = val[ii]; + if (testEntry(entry.isArray(), "JSON value is not an array.") || + testEntry(entry.size() == 2, "JSON array size invalid.")) { + return false; + } + + Json::Value const& entryName = entry[0u]; + Json::Value const& entryOptions = entry[1u]; + if (testEntry(entryName.isString(), + "JSON value for name is not a string.") || + testEntry(entryOptions.isArray(), + "JSON value for options is not an array.")) { return false; } + + auto& uiFile = UicConst_.UiFiles[entryName.asString()]; + JsonGetArray(uiFile.Options, entryOptions); } } - MocConst_.PredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); - } - // - Uic - UicConst_.Executable = InfoGet("AM_QT_UIC_EXECUTABLE"); - if (!UicConst().Executable.empty()) { - UicConst_.Enabled = true; - // Load the executable file time + // -- Evaluate settings + // Check if uic executable exists (by reading the file time) if (!UicConst_.ExecutableTime.Load(UicConst_.Executable)) { return LogInfoError(cmStrCat("The uic executable ", MessagePath(UicConst_.Executable), " does not exist.")); } - for (std::string& sfl : InfoGetList("AM_UIC_SKIP")) { - UicConst_.SkipList.insert(std::move(sfl)); - } - UicConst_.SearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); - UicConst_.TargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); - { - cm::string_view const keyFiles = "AM_UIC_OPTIONS_FILES"; - cm::string_view const keyOpts = "AM_UIC_OPTIONS_OPTIONS"; - auto sources = InfoGetList(keyFiles); - auto options = InfoGetLists(keyOpts); - if (!MatchSizes(keyFiles, keyOpts, sources.size(), options.size())) { - return false; - } - auto fitEnd = sources.cend(); - auto fit = sources.begin(); - auto oit = options.begin(); - while (fit != fitEnd) { - UicConst_.Options[*fit] = std::move(*oit); - ++fit; - ++oit; - } - } } - // Headers + // -- Headers { - // Get file lists - cm::string_view const keyFiles = "AM_HEADERS"; - cm::string_view const keyFlags = "AM_HEADERS_FLAGS"; - std::vector files = InfoGetList(keyFiles); - std::vector flags = InfoGetList(keyFlags); - std::vector builds; - if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) { - return false; - } - if (MocConst().Enabled) { - cm::string_view const keyPaths = "AM_HEADERS_BUILD_PATHS"; - builds = InfoGetList(keyPaths); - if (!MatchSizes(keyFiles, keyPaths, files.size(), builds.size())) { + Json::Value const& val = Info()["HEADERS"]; + if (!val.isArray()) { + return LogInfoError("HEADERS JSON value is not an array."); + } + Json::ArrayIndex const arraySize = val.size(); + for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { + // Test entry closure + auto testEntry = [this, ii](bool test, cm::string_view message) -> bool { + if (!test) { + this->LogInfoError(cmStrCat("HEADERS entry ", ii, ": ", message)); + } + return !test; + }; + + Json::Value const& entry = val[ii]; + if (testEntry(entry.isArray(), "JSON value is not an array.") || + testEntry(entry.size() == 3, "JSON array size invalid.")) { + return false; + } + + Json::Value const& entryName = entry[0u]; + Json::Value const& entryFlags = entry[1u]; + Json::Value const& entryBuild = entry[2u]; + if (testEntry(entryName.isString(), + "JSON value for name is not a string.") || + testEntry(entryFlags.isString(), + "JSON value for flags is not a string.") || + testEntry(entryBuild.isString(), + "JSON value for build path is not a string.")) { return false; } - } - // Process file lists - for (std::size_t ii = 0; ii != files.size(); ++ii) { - std::string& fileName(files[ii]); - std::string const& fileFlags(flags[ii]); - if (fileFlags.size() != 2) { - LogInfoError(cmStrCat("Invalid flags string size ", fileFlags.size(), - "in ", keyFlags)); + std::string name = entryName.asString(); + std::string flags = entryFlags.asString(); + std::string build = entryBuild.asString(); + if (testEntry(flags.size() == 2, "Invalid flags string size")) { return false; } + cmFileTime fileTime; - if (!fileTime.Load(fileName)) { - LogInfoError(cmStrCat("The header file ", this->MessagePath(fileName), + if (!fileTime.Load(name)) { + LogInfoError(cmStrCat("The header file ", this->MessagePath(name), " does not exist.")); return false; } - SourceFileHandleT sourceHandle = std::make_shared(fileName); + SourceFileHandleT sourceHandle = std::make_shared(name); sourceHandle->FileTime = fileTime; sourceHandle->IsHeader = true; - sourceHandle->Moc = (fileFlags[0] == 'M'); - sourceHandle->Uic = (fileFlags[1] == 'U'); - + sourceHandle->Moc = (flags[0] == 'M'); + sourceHandle->Uic = (flags[1] == 'U'); if (sourceHandle->Moc && MocConst().Enabled) { - sourceHandle->BuildPath = std::move(builds[ii]); - if (sourceHandle->BuildPath.empty()) { - return LogInfoError("Header file build path is empty"); + if (build.empty()) { + return LogInfoError( + cmStrCat("Header file ", ii, " build path is empty")); } + sourceHandle->BuildPath = std::move(build); } - BaseEval().Headers.emplace(std::move(fileName), std::move(sourceHandle)); + BaseEval().Headers.emplace(std::move(name), std::move(sourceHandle)); } } - // Sources + // -- Sources { - cm::string_view const keyFiles = "AM_SOURCES"; - cm::string_view const keyFlags = "AM_SOURCES_FLAGS"; - std::vector files = InfoGetList(keyFiles); - std::vector flags = InfoGetList(keyFlags); - if (!MatchSizes(keyFiles, keyFlags, files.size(), flags.size())) { - return false; - } + Json::Value const& val = Info()["SOURCES"]; + if (!val.isArray()) { + return LogInfoError("SOURCES JSON value is not an array."); + } + Json::ArrayIndex const arraySize = val.size(); + for (Json::ArrayIndex ii = 0; ii != arraySize; ++ii) { + // Test entry closure + auto testEntry = [this, ii](bool test, cm::string_view message) -> bool { + if (!test) { + this->LogInfoError(cmStrCat("SOURCES entry ", ii, ": ", message)); + } + return !test; + }; + + Json::Value const& entry = val[ii]; + if (testEntry(entry.isArray(), "JSON value is not an array.") || + testEntry(entry.size() == 2, "JSON array size invalid.")) { + return false; + } + + Json::Value const& entryName = entry[0u]; + Json::Value const& entryFlags = entry[1u]; + if (testEntry(entryName.isString(), + "JSON value for name is not a string.") || + testEntry(entryFlags.isString(), + "JSON value for flags is not a string.")) { + return false; + } - // Process file lists - for (std::size_t ii = 0; ii != files.size(); ++ii) { - std::string& fileName(files[ii]); - std::string const& fileFlags(flags[ii]); - if (fileFlags.size() != 2) { - LogInfoError(cmStrCat("Invalid flags string size ", fileFlags.size(), - "in ", keyFlags)); + std::string name = entryName.asString(); + std::string flags = entryFlags.asString(); + if (testEntry(flags.size() == 2, "Invalid flags string size")) { return false; } + cmFileTime fileTime; - if (!fileTime.Load(fileName)) { - LogInfoError(cmStrCat("The source file ", this->MessagePath(fileName), + if (!fileTime.Load(name)) { + LogInfoError(cmStrCat("The source file ", this->MessagePath(name), " does not exist.")); return false; } - SourceFileHandleT sourceHandle = std::make_shared(fileName); + SourceFileHandleT sourceHandle = std::make_shared(name); sourceHandle->FileTime = fileTime; sourceHandle->IsHeader = false; - sourceHandle->Moc = (fileFlags[0] == 'M'); - sourceHandle->Uic = (fileFlags[1] == 'U'); - BaseEval().Sources.emplace(std::move(fileName), std::move(sourceHandle)); + sourceHandle->Moc = (flags[0] == 'M'); + sourceHandle->Uic = (flags[1] == 'U'); + BaseEval().Sources.emplace(std::move(name), std::move(sourceHandle)); } } - // Init derived information - // ------------------------ - + // -- Init derived information // Moc variables if (MocConst().Enabled) { - // Mocs compilation file - MocConst_.CompFileAbs = AbsoluteBuildPath("mocs_compilation.cpp"); - - // Moc predefs file - if (!MocConst_.PredefsCmd.empty()) { - std::string pathRel; - if (BaseConst_.MultiConfig) { - pathRel = cmStrCat("moc_predefs_", InfoConfig(), ".h"); - } else { - pathRel = "moc_predefs.h"; - } - MocConst_.PredefsFileAbs = AbsoluteBuildPath(pathRel); - } - // Compose moc includes list { // Compute framework paths @@ -2031,11 +2006,11 @@ void cmQtAutoMocUic::SettingsFileRead() if (UicConst().Enabled) { cryptoHash.Initialize(); cha(UicConst().Executable); - std::for_each(UicConst().TargetOptions.begin(), - UicConst().TargetOptions.end(), cha); - for (const auto& item : UicConst().Options) { + std::for_each(UicConst().Options.begin(), UicConst().Options.end(), cha); + for (const auto& item : UicConst().UiFiles) { cha(item.first); - std::for_each(item.second.begin(), item.second.end(), cha); + auto const& opts = item.second.Options; + std::for_each(opts.begin(), opts.end(), cha); } SettingsStringUic_ = cryptoHash.FinalizeHex(); } @@ -2080,7 +2055,7 @@ bool cmQtAutoMocUic::SettingsFileWrite() if (Log().Verbose()) { Log().Info( GenT::GEN, - cmStrCat("Writing settings file ", MessagePath(SettingsFile_))); + cmStrCat("Writing the settings file ", MessagePath(SettingsFile_))); } // Compose settings file content std::string content; diff --git a/Source/cmQtAutoMocUic.h b/Source/cmQtAutoMocUic.h index f37d923..7101b8f 100644 --- a/Source/cmQtAutoMocUic.h +++ b/Source/cmQtAutoMocUic.h @@ -25,8 +25,6 @@ #include #include -class cmMakefile; - /** \class cmQtAutoMocUic * \brief AUTOMOC and AUTOUIC generator */ @@ -42,17 +40,21 @@ public: public: // -- Types + /** Include string with sub parts. */ + struct IncludeKeyT + { + IncludeKeyT(std::string const& key, std::size_t basePrefixLength); + + std::string Key; // Full include string + std::string Dir; // Include directory + std::string Base; // Base part of the include file name + }; + /** Search key plus regular expression pair. */ struct KeyExpT { KeyExpT() = default; - KeyExpT(const char* key, const char* exp) - : Key(key) - , Exp(exp) - { - } - KeyExpT(std::string key, std::string const& exp) : Key(std::move(key)) , Exp(exp) @@ -63,16 +65,6 @@ public: cmsys::RegularExpression Exp; }; - /** Include string with sub parts. */ - struct IncludeKeyT - { - IncludeKeyT(std::string const& key, std::size_t basePrefixLength); - - std::string Key; // Full include string - std::string Dir; // Include directory - std::string Base; // Base part of the include file name - }; - /** Source file parsing cache. */ class ParseCacheT { @@ -169,6 +161,7 @@ public: // - Config bool MultiConfig = false; unsigned int QtVersionMajor = 4; + unsigned int ThreadCount = 0; // - Directories std::string AutogenBuildDir; std::string AutogenIncludeDir; @@ -252,6 +245,12 @@ public: class UicSettingsT { public: + struct UiFile + { + std::vector Options; + }; + + public: UicSettingsT(); ~UicSettingsT(); @@ -267,8 +266,8 @@ public: cmFileTime ExecutableTime; std::string Executable; std::unordered_set SkipList; - std::vector TargetOptions; - std::map> Options; + std::vector Options; + std::unordered_map UiFiles; std::vector SearchPaths; cmsys::RegularExpression RegExpInclude; }; @@ -523,10 +522,8 @@ public: std::string CollapseFullPathTS(std::string const& path) const; private: - // -- Utility accessors - Logger const& Log() const { return Logger_; } // -- Abstract processing interface - bool Init(cmMakefile* makefile) override; + bool InitFromInfo() override; void InitJobs(); bool Process() override; // -- Settings file @@ -541,8 +538,6 @@ private: bool CreateDirectories(); private: - // -- Utility - Logger Logger_; // -- Settings BaseSettingsT BaseConst_; BaseEvalT BaseEval_; diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx index 1bf8ca4..b0b15d4 100644 --- a/Source/cmQtAutoRcc.cxx +++ b/Source/cmQtAutoRcc.cxx @@ -6,7 +6,6 @@ #include "cmCryptoHash.h" #include "cmDuration.h" #include "cmFileLockResult.h" -#include "cmMakefile.h" #include "cmProcessOutput.h" #include "cmQtAutoGen.h" #include "cmStringAlgorithms.h" @@ -16,112 +15,49 @@ #include -cmQtAutoRcc::cmQtAutoRcc() = default; +cmQtAutoRcc::cmQtAutoRcc() + : cmQtAutoGenerator(GenT::RCC) +{ +} cmQtAutoRcc::~cmQtAutoRcc() = default; -bool cmQtAutoRcc::Init(cmMakefile* makefile) +bool cmQtAutoRcc::InitFromInfo() { - // -- Utility lambdas - auto InfoGet = [makefile](cm::string_view key) { - return makefile->GetSafeDefinition(std::string(key)); - }; - auto InfoGetList = - [makefile](cm::string_view key) -> std::vector { - return cmExpandedList(makefile->GetSafeDefinition(std::string(key))); - }; - auto InfoGetConfig = [makefile, this](cm::string_view key) -> std::string { - if (const char* valueConf = - makefile->GetDefinition(cmStrCat(key, '_', InfoConfig()))) { - return std::string(valueConf); - } - return makefile->GetSafeDefinition(std::string(key)); - }; - auto InfoGetConfigList = - [&InfoGetConfig](cm::string_view key) -> std::vector { - return cmExpandedList(InfoGetConfig(key)); - }; - auto LogInfoError = [this](cm::string_view msg) -> bool { - this->Log().Error( - GenT::RCC, cmStrCat("In ", MessagePath(this->InfoFile()), ":\n", msg)); + // -- Required settings + if (!InfoBool("MULTI_CONFIG", MultiConfig_, true) || + !InfoString("BUILD_DIR", AutogenBuildDir_, true) || + !InfoStringConfig("INCLUDE_DIR", IncludeDir_, true) || + !InfoString("RCC_EXECUTABLE", RccExecutable_, true) || + !InfoArray("RCC_LIST_OPTIONS", RccListOptions_, false) || + !InfoString("LOCK_FILE", LockFile_, true) || + !InfoStringConfig("SETTINGS_FILE", SettingsFile_, true) || + !InfoString("SOURCE", QrcFile_, true) || + !InfoString("OUTPUT_CHECKSUM", RccPathChecksum_, true) || + !InfoString("OUTPUT_NAME", RccFileName_, true) || + !InfoArray("OPTIONS", Options_, false) || + !InfoArray("INPUTS", Inputs_, false)) { return false; - }; - - // -- Read info file - if (!makefile->ReadListFile(InfoFile())) { - return LogInfoError("File processing failed."); } - // - Configurations - Logger_.RaiseVerbosity(InfoGet("ARCC_VERBOSITY")); - MultiConfig_ = makefile->IsOn("ARCC_MULTI_CONFIG"); - - // - Directories - ProjectDirsRef().Source = InfoGet("ARCC_CMAKE_SOURCE_DIR"); - ProjectDirsRef().Binary = InfoGet("ARCC_CMAKE_BINARY_DIR"); - AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR"); - if (AutogenBuildDir_.empty()) { - return LogInfoError("Build directory empty."); - } - - IncludeDir_ = InfoGetConfig("ARCC_INCLUDE_DIR"); - if (IncludeDir_.empty()) { - return LogInfoError("Include directory empty."); - } - - // - Rcc executable - RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE"); - if (!RccExecutableTime_.Load(RccExecutable_)) { - return LogInfoError(cmStrCat( - "The rcc executable ", MessagePath(RccExecutable_), " does not exist.")); - } - RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS"); - - // - Job - LockFile_ = InfoGet("ARCC_LOCK_FILE"); - QrcFile_ = InfoGet("ARCC_SOURCE"); + // -- Derive information QrcFileName_ = cmSystemTools::GetFilenameName(QrcFile_); QrcFileDir_ = cmSystemTools::GetFilenamePath(QrcFile_); - RccPathChecksum_ = InfoGet("ARCC_OUTPUT_CHECKSUM"); - RccFileName_ = InfoGet("ARCC_OUTPUT_NAME"); - Options_ = InfoGetConfigList("ARCC_OPTIONS"); - Inputs_ = InfoGetList("ARCC_INPUTS"); - - // - Settings file - SettingsFile_ = InfoGetConfig("ARCC_SETTINGS_FILE"); - - // - Validity checks - if (LockFile_.empty()) { - return LogInfoError("Lock file name missing."); - } - if (SettingsFile_.empty()) { - return LogInfoError("Settings file name missing."); - } - if (AutogenBuildDir_.empty()) { - return LogInfoError("Autogen build directory missing."); - } - if (RccExecutable_.empty()) { - return LogInfoError("rcc executable missing."); - } - if (QrcFile_.empty()) { - return LogInfoError("rcc input file missing."); - } - if (RccFileName_.empty()) { - return LogInfoError("rcc output file missing."); - } - - // Init derived information - // ------------------------ - RccFilePublic_ = cmStrCat(AutogenBuildDir_, '/', RccPathChecksum_, '/', RccFileName_); - // Compute rcc output file name + // rcc output file name if (IsMultiConfig()) { RccFileOutput_ = cmStrCat(IncludeDir_, '/', MultiConfigOutput()); } else { RccFileOutput_ = RccFilePublic_; } + // -- Checks + if (!RccExecutableTime_.Load(RccExecutable_)) { + return LogInfoError(cmStrCat( + "The rcc executable ", MessagePath(RccExecutable_), " does not exist.")); + } + return true; } diff --git a/Source/cmQtAutoRcc.h b/Source/cmQtAutoRcc.h index 5d3bd6a..945b68f 100644 --- a/Source/cmQtAutoRcc.h +++ b/Source/cmQtAutoRcc.h @@ -12,8 +12,6 @@ #include #include -class cmMakefile; - /** \class cmQtAutoRcc * \brief AUTORCC generator */ @@ -28,12 +26,11 @@ public: private: // -- Utility - Logger const& Log() const { return Logger_; } bool IsMultiConfig() const { return MultiConfig_; } std::string MultiConfigOutput() const; // -- Abstract processing interface - bool Init(cmMakefile* makefile) override; + bool InitFromInfo() override; bool Process() override; // -- Settings file bool SettingsFileRead(); @@ -47,8 +44,6 @@ private: bool GenerateWrapper(); private: - // -- Logging - Logger Logger_; // -- Config settings bool MultiConfig_ = false; // -- Directories -- cgit v0.12