From b2a0b549bb9fea678517a52caf333eae009901dd Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Thu, 16 Nov 2017 09:59:03 +0100 Subject: Autogen: Introduce standalone RCC generator class Introduces the standalone RCC generator class `cmQtAutoGeneratorRcc`. Every instance of `cmQtAutoGeneratorRcc` class handles the `rcc` invocation for a single `.qrc` file. The class will be used in the future to allow parallel `.qrc` file processing by calling `cmake -E cmake_autorcc `. --- Source/CMakeLists.txt | 2 + Source/cmQtAutoGeneratorRcc.cxx | 738 ++++++++++++++++++++++++++++++++++++++++ Source/cmQtAutoGeneratorRcc.h | 90 +++++ Source/cmcmd.cxx | 18 +- 4 files changed, 844 insertions(+), 4 deletions(-) create mode 100644 Source/cmQtAutoGeneratorRcc.cxx create mode 100644 Source/cmQtAutoGeneratorRcc.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 54e5063..fd02196 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -316,6 +316,8 @@ set(SRCS cmQtAutoGeneratorInitializer.h cmQtAutoGenerators.cxx cmQtAutoGenerators.h + cmQtAutoGeneratorRcc.cxx + cmQtAutoGeneratorRcc.h cmRST.cxx cmRST.h cmScriptGenerator.h diff --git a/Source/cmQtAutoGeneratorRcc.cxx b/Source/cmQtAutoGeneratorRcc.cxx new file mode 100644 index 0000000..e057937 --- /dev/null +++ b/Source/cmQtAutoGeneratorRcc.cxx @@ -0,0 +1,738 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmQtAutoGen.h" +#include "cmQtAutoGeneratorRcc.h" + +#include "cmsys/FStream.hxx" +#include "cmsys/Terminal.h" + +#include "cmAlgorithms.h" +#include "cmCryptoHash.h" +#include "cmFilePathChecksum.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmOutputConverter.h" +#include "cmStateDirectory.h" +#include "cmStateSnapshot.h" +#include "cmSystemTools.h" +#include "cmake.h" + +#if defined(__APPLE__) +#include +#endif + +// -- Static variables + +static const char* SettingsKeyRcc = "ARCC_SETTINGS_HASH"; + +// -- Static functions + +static std::string HeadLine(std::string const& title) +{ + std::string head = title; + head += '\n'; + head.append(head.size() - 1, '-'); + head += '\n'; + return head; +} + +static std::string QuotedCommand(std::vector const& command) +{ + std::string res; + for (std::string const& item : command) { + if (!res.empty()) { + res.push_back(' '); + } + std::string const cesc = cmQtAutoGen::Quoted(item); + if (item.empty() || (cesc.size() > (item.size() + 2)) || + (cesc.find(' ') != std::string::npos)) { + res += cesc; + } else { + res += item; + } + } + return res; +} + +static bool ReadFile(std::string& content, std::string const& filename, + std::string* error = nullptr) +{ + bool success = false; + if (cmSystemTools::FileExists(filename)) { + std::size_t const length = cmSystemTools::FileLength(filename); + cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); + if (ifs) { + content.resize(length); + ifs.read(&content.front(), content.size()); + if (ifs) { + success = true; + } else { + content.clear(); + if (error != nullptr) { + error->append("Reading from the file failed."); + } + } + } else if (error != nullptr) { + error->append("Opening the file for reading failed."); + } + } else if (error != nullptr) { + error->append("The file does not exist."); + } + return success; +} + +/** + * @brief Tests if buildFile is older than sourceFile + * @return True if buildFile is older than sourceFile. + * False may indicate an error. + */ +static bool FileIsOlderThan(std::string const& buildFile, + std::string const& sourceFile, + std::string* error = nullptr) +{ + int result = 0; + if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) { + return (result < 0); + } + if (error != nullptr) { + error->append( + "File modification time comparison failed for the files\n "); + error->append(cmQtAutoGen::Quoted(buildFile)); + error->append("\nand\n "); + error->append(cmQtAutoGen::Quoted(sourceFile)); + } + return false; +} + +// -- Class methods + +cmQtAutoGeneratorRcc::cmQtAutoGeneratorRcc() + : MultiConfig(cmQtAutoGen::WRAP) + , Verbose(cmSystemTools::HasEnv("VERBOSE")) + , ColorOutput(true) + , SettingsChanged(false) +{ + { + std::string colorEnv; + cmSystemTools::GetEnv("COLOR", colorEnv); + if (!colorEnv.empty()) { + this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str()); + } + } +} + +bool cmQtAutoGeneratorRcc::Run(std::string const& infoFile, + std::string const& config) +{ + // Info settings + this->InfoFile = infoFile; + this->InfoDir = cmSystemTools::GetFilenamePath(infoFile); + this->InfoConfig = config; + + cmake cm(cmake::RoleScript); + cm.SetHomeOutputDirectory(this->InfoDir); + cm.SetHomeDirectory(this->InfoDir); + cm.GetCurrentSnapshot().SetDefaultDefinitions(); + cmGlobalGenerator gg(&cm); + + cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); + snapshot.GetDirectory().SetCurrentBinary(this->InfoDir); + snapshot.GetDirectory().SetCurrentSource(this->InfoDir); + + auto makefile = cm::make_unique(&gg, snapshot); + gg.SetCurrentMakefile(makefile.get()); + + return this->Process(makefile.get()); +} + +bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) +{ + // Utility lambdas + auto InfoGet = [makefile](const char* key) { + return makefile->GetSafeDefinition(key); + }; + auto InfoGetList = [makefile](const char* key) -> std::vector { + std::vector list; + cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list); + return list; + }; + auto InfoGetConfig = [makefile, this](const char* key) -> std::string { + const char* valueConf = nullptr; + { + std::string keyConf = key; + keyConf += '_'; + keyConf += this->InfoConfig; + valueConf = makefile->GetDefinition(keyConf); + } + if (valueConf == nullptr) { + valueConf = makefile->GetSafeDefinition(key); + } + return std::string(valueConf); + }; + auto InfoGetConfigList = + [&InfoGetConfig](const char* key) -> std::vector { + std::vector list; + cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); + return list; + }; + + // -- Read info file + if (!makefile->ReadListFile(this->InfoFile.c_str())) { + this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, + "File processing failed"); + return false; + } + + // -- Meta + this->MultiConfig = + cmQtAutoGen::MultiConfigType(InfoGet("ARCC_MULTI_CONFIG")); + this->ConfigSuffix = InfoGetConfig("ARCC_CONFIG_SUFFIX"); + if (this->ConfigSuffix.empty()) { + this->ConfigSuffix = "_"; + this->ConfigSuffix += this->InfoConfig; + } + + this->SettingsFile = InfoGetConfig("ARCC_SETTINGS_FILE"); + if (!this->SettingsFile.empty()) { + if (this->MultiConfig != cmQtAutoGen::SINGLE) { + this->SettingsFile = cmQtAutoGen::AppendFilenameSuffix( + this->SettingsFile, this->ConfigSuffix); + } + } + + // - Files and directories + this->ProjectSourceDir = InfoGet("ARCC_CMAKE_SOURCE_DIR"); + this->ProjectBinaryDir = InfoGet("ARCC_CMAKE_BINARY_DIR"); + this->CurrentSourceDir = InfoGet("ARCC_CMAKE_CURRENT_SOURCE_DIR"); + this->CurrentBinaryDir = InfoGet("ARCC_CMAKE_CURRENT_BINARY_DIR"); + this->AutogenBuildDir = InfoGet("ARCC_BUILD_DIR"); + + // - Qt environment + this->QtMajorVersion = InfoGet("ARCC_QT_VERSION_MAJOR"); + this->RccExecutable = InfoGet("ARCC_QT_RCC_EXECUTABLE"); + + // - Job + this->QrcFile = InfoGet("ARCC_SOURCE"); + this->RccFile = InfoGet("ARCC_OUTPUT"); + this->Options = InfoGetConfigList("ARCC_OPTIONS"); + this->Inputs = InfoGetList("ARCC_INPUTS"); + + // - Validity checks + if (this->SettingsFile.empty()) { + this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, + "Settings file name missing"); + return false; + } + if (this->AutogenBuildDir.empty()) { + this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, + "Autogen build directory missing"); + return false; + } + if (this->RccExecutable.empty()) { + this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, + "rcc executable missing"); + return false; + } + if (this->QrcFile.empty()) { + this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, + "rcc input file missing"); + return false; + } + if (this->RccFile.empty()) { + this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, + "rcc output file missing"); + return false; + } + + // Init derived information + // ------------------------ + + // Init file path checksum generator + this->FilePathChecksum.setupParentDirs( + this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir, + this->ProjectBinaryDir); + + return true; +} + +void cmQtAutoGeneratorRcc::SettingsFileRead(cmMakefile* makefile) +{ + // Compose current settings strings + { + cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); + std::string const sep(" ~~~ "); + { + std::string str; + str += this->RccExecutable; + str += sep; + str += this->QrcFile; + str += sep; + str += this->RccFile; + str += sep; + str += cmJoin(this->Options, ";"); + str += sep; + str += cmJoin(this->Inputs, ";"); + str += sep; + this->SettingsString = crypt.HashString(str); + } + } + + // Read old settings + if (makefile->ReadListFile(this->SettingsFile.c_str())) { + { + auto SMatch = [makefile](const char* key, std::string const& value) { + return (value == makefile->GetSafeDefinition(key)); + }; + if (!SMatch(SettingsKeyRcc, this->SettingsString)) { + this->SettingsChanged = true; + } + } + // In case any setting changed remove the old settings file. + // This triggers a full rebuild on the next run if the current + // build is aborted before writing the current settings in the end. + if (this->SettingsChanged) { + cmSystemTools::RemoveFile(this->SettingsFile); + } + } else { + // If the file could not be read re-generate everythiung. + this->SettingsChanged = true; + } +} + +bool cmQtAutoGeneratorRcc::SettingsFileWrite() +{ + bool success = true; + // Only write if any setting changed + if (this->SettingsChanged) { + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::RCC, "Writing settings file " + + cmQtAutoGen::Quoted(this->SettingsFile)); + } + // Compose settings file content + std::string settings; + { + auto SettingAppend = [&settings](const char* key, + std::string const& value) { + settings += "set("; + settings += key; + settings += " "; + settings += cmOutputConverter::EscapeForCMake(value); + settings += ")\n"; + }; + SettingAppend(SettingsKeyRcc, this->SettingsString); + } + // Write settings file + if (!this->FileWrite(cmQtAutoGen::RCC, this->SettingsFile, settings)) { + this->LogFileError(cmQtAutoGen::RCC, this->SettingsFile, + "Settings file writing failed"); + // Remove old settings file to trigger a full rebuild on the next run + cmSystemTools::RemoveFile(this->SettingsFile); + success = false; + } + } + return success; +} + +bool cmQtAutoGeneratorRcc::Process(cmMakefile* makefile) +{ + // Read info file + if (!this->InfoFileRead(makefile)) { + return false; + } + // Read latest settings + this->SettingsFileRead(makefile); + // Generate rcc file + if (!this->RccGenerate()) { + return false; + } + // Write latest settings + if (!this->SettingsFileWrite()) { + return false; + } + return true; +} + +/** + * @return True on success + */ +bool cmQtAutoGeneratorRcc::RccGenerate() +{ + bool success = true; + bool rccGenerated = false; + + std::string rccFileAbs; + { + std::string suffix; + switch (this->MultiConfig) { + case cmQtAutoGen::SINGLE: + break; + case cmQtAutoGen::WRAP: + suffix = "_CMAKE"; + suffix += this->ConfigSuffix; + suffix += "_"; + break; + case cmQtAutoGen::FULL: + suffix = this->ConfigSuffix; + break; + } + rccFileAbs = cmQtAutoGen::AppendFilenameSuffix(this->RccFile, suffix); + } + std::string const rccFileRel = cmSystemTools::RelativePath( + this->AutogenBuildDir.c_str(), rccFileAbs.c_str()); + + // Check if regeneration is required + bool generate = false; + std::string generateReason; + if (!cmSystemTools::FileExists(this->QrcFile)) { + { + std::string error = "Could not find the file\n "; + error += cmQtAutoGen::Quoted(this->QrcFile); + this->LogError(cmQtAutoGen::RCC, error); + } + success = false; + } + if (success && !generate && !cmSystemTools::FileExists(rccFileAbs.c_str())) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(rccFileAbs); + generateReason += " from its source file "; + generateReason += cmQtAutoGen::Quoted(this->QrcFile); + generateReason += " because it doesn't exist"; + } + generate = true; + } + if (success && !generate && this->SettingsChanged) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(rccFileAbs); + generateReason += " from "; + generateReason += cmQtAutoGen::Quoted(this->QrcFile); + generateReason += " because the RCC settings changed"; + } + generate = true; + } + if (success && !generate) { + std::string error; + if (FileIsOlderThan(rccFileAbs, this->QrcFile, &error)) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(rccFileAbs); + generateReason += " because it is older than "; + generateReason += cmQtAutoGen::Quoted(this->QrcFile); + } + generate = true; + } else { + if (!error.empty()) { + this->LogError(cmQtAutoGen::RCC, error); + success = false; + } + } + } + if (success && !generate) { + // Acquire input file list + std::vector readFiles; + std::vector const* files = nullptr; + if (!this->Inputs.empty()) { + files = &this->Inputs; + } else { + // Read input file list from qrc file + std::string error; + if (cmQtAutoGen::RccListInputs(this->QtMajorVersion, this->RccExecutable, + this->QrcFile, readFiles, &error)) { + files = &readFiles; + } else { + this->LogFileError(cmQtAutoGen::RCC, this->QrcFile, error); + success = false; + } + } + // Test if any input file is newer than the build file + if (files != nullptr) { + std::string error; + for (std::string const& resFile : *files) { + if (!cmSystemTools::FileExists(resFile.c_str())) { + error = "Could not find the file\n "; + error += cmQtAutoGen::Quoted(resFile); + error += "\nwhich is listed in\n "; + error += cmQtAutoGen::Quoted(this->QrcFile); + break; + } + if (FileIsOlderThan(rccFileAbs, resFile, &error)) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(rccFileAbs); + generateReason += " from "; + generateReason += cmQtAutoGen::Quoted(this->QrcFile); + generateReason += " because it is older than "; + generateReason += cmQtAutoGen::Quoted(resFile); + } + generate = true; + break; + } + if (!error.empty()) { + break; + } + } + // Print error + if (!error.empty()) { + this->LogError(cmQtAutoGen::RCC, error); + success = false; + } + } + } + // Regenerate on demand + if (generate) { + // Log + if (this->Verbose) { + this->LogBold("Generating RCC source " + rccFileRel); + this->LogInfo(cmQtAutoGen::RCC, generateReason); + } + + // Make sure the parent directory exists + if (this->MakeParentDirectory(cmQtAutoGen::RCC, rccFileAbs)) { + // Compose rcc command + std::vector cmd; + cmd.push_back(this->RccExecutable); + cmd.insert(cmd.end(), this->Options.begin(), this->Options.end()); + cmd.push_back("-o"); + cmd.push_back(rccFileAbs); + cmd.push_back(this->QrcFile); + + std::string output; + if (this->RunCommand(cmd, output)) { + // Success + rccGenerated = true; + } else { + { + std::string emsg = "rcc failed for\n "; + emsg += cmQtAutoGen::Quoted(this->QrcFile); + this->LogCommandError(cmQtAutoGen::RCC, emsg, cmd, output); + } + cmSystemTools::RemoveFile(rccFileAbs); + success = false; + } + } else { + // Parent directory creation failed + success = false; + } + } + + // Generate a wrapper source file on demand + if (success && (this->MultiConfig == cmQtAutoGen::WRAP)) { + // Wrapper file name + std::string const& wrapperFileAbs = this->RccFile; + std::string const wrapperFileRel = cmSystemTools::RelativePath( + this->AutogenBuildDir.c_str(), wrapperFileAbs.c_str()); + // Wrapper file content + std::string content = "// This is an autogenerated configuration " + "wrapper file. Changes will be overwritten.\n" + "#include \""; + content += cmSystemTools::GetFilenameName(rccFileRel); + content += "\"\n"; + // Write content to file + if (this->FileDiffers(wrapperFileAbs, content)) { + // Write new wrapper file + if (this->Verbose) { + this->LogBold("Generating RCC wrapper " + wrapperFileRel); + } + if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) { + this->LogFileError(cmQtAutoGen::RCC, wrapperFileAbs, + "rcc wrapper file writing failed"); + success = false; + } + } else if (rccGenerated) { + // Just touch the wrapper file + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::RCC, + "Touching RCC wrapper " + wrapperFileRel); + } + cmSystemTools::Touch(wrapperFileAbs, false); + } + } + + return success; +} + +void cmQtAutoGeneratorRcc::LogBold(std::string const& message) const +{ + cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, this->ColorOutput); +} + +void cmQtAutoGeneratorRcc::LogInfo(cmQtAutoGen::Generator genType, + std::string const& message) const +{ + std::string msg = cmQtAutoGen::GeneratorName(genType); + msg += ": "; + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + cmSystemTools::Stdout(msg.c_str(), msg.size()); +} + +void cmQtAutoGeneratorRcc::LogWarning(cmQtAutoGen::Generator genType, + std::string const& message) const +{ + std::string msg = cmQtAutoGen::GeneratorName(genType); + msg += " warning:"; + if (message.find('\n') == std::string::npos) { + // Single line message + msg.push_back(' '); + } else { + // Multi line message + msg.push_back('\n'); + } + // Message + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + cmSystemTools::Stdout(msg.c_str(), msg.size()); +} + +void cmQtAutoGeneratorRcc::LogFileWarning(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const +{ + std::string msg = " "; + msg += cmQtAutoGen::Quoted(filename); + msg.push_back('\n'); + // Message + msg += message; + this->LogWarning(genType, msg); +} + +void cmQtAutoGeneratorRcc::LogError(cmQtAutoGen::Generator genType, + std::string const& message) const +{ + std::string msg; + msg.push_back('\n'); + msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error"); + // Message + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + cmSystemTools::Stderr(msg.c_str(), msg.size()); +} + +void cmQtAutoGeneratorRcc::LogFileError(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const +{ + std::string emsg = " "; + emsg += cmQtAutoGen::Quoted(filename); + emsg += '\n'; + // Message + emsg += message; + this->LogError(genType, emsg); +} + +void cmQtAutoGeneratorRcc::LogCommandError( + cmQtAutoGen::Generator genType, std::string const& message, + std::vector const& command, std::string const& output) const +{ + std::string msg; + msg.push_back('\n'); + msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error"); + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + msg += HeadLine("Command"); + msg += QuotedCommand(command); + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + msg += HeadLine("Output"); + msg += output; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + cmSystemTools::Stderr(msg.c_str(), msg.size()); +} + +/** + * @brief Generates the parent directory of the given file on demand + * @return True on success + */ +bool cmQtAutoGeneratorRcc::MakeParentDirectory( + cmQtAutoGen::Generator genType, std::string const& filename) const +{ + bool success = true; + std::string const dirName = cmSystemTools::GetFilenamePath(filename); + if (!dirName.empty()) { + if (!cmSystemTools::MakeDirectory(dirName)) { + this->LogFileError(genType, filename, + "Could not create parent directory"); + success = false; + } + } + return success; +} + +bool cmQtAutoGeneratorRcc::FileDiffers(std::string const& filename, + std::string const& content) +{ + bool differs = true; + { + std::string oldContents; + if (ReadFile(oldContents, filename)) { + differs = (oldContents != content); + } + } + return differs; +} + +bool cmQtAutoGeneratorRcc::FileWrite(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& content) +{ + std::string error; + // Make sure the parent directory exists + if (this->MakeParentDirectory(genType, filename)) { + cmsys::ofstream outfile; + outfile.open(filename.c_str(), + (std::ios::out | std::ios::binary | std::ios::trunc)); + if (outfile) { + outfile << content; + // Check for write errors + if (!outfile.good()) { + error = "File writing failed"; + } + } else { + error = "Opening file for writing failed"; + } + } + if (!error.empty()) { + this->LogFileError(genType, filename, error); + return false; + } + return true; +} + +/** + * @brief Runs a command and returns true on success + * @return True on success + */ +bool cmQtAutoGeneratorRcc::RunCommand(std::vector const& command, + std::string& output) const +{ + // Log command + if (this->Verbose) { + std::string qcmd = QuotedCommand(command); + qcmd.push_back('\n'); + cmSystemTools::Stdout(qcmd.c_str(), qcmd.size()); + } + // Execute command + int retVal = 0; + bool res = cmSystemTools::RunSingleCommand( + command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE); + return (res && (retVal == 0)); +} diff --git a/Source/cmQtAutoGeneratorRcc.h b/Source/cmQtAutoGeneratorRcc.h new file mode 100644 index 0000000..4539461 --- /dev/null +++ b/Source/cmQtAutoGeneratorRcc.h @@ -0,0 +1,90 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmQtAutoGeneratorRcc_h +#define cmQtAutoGeneratorRcc_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmFilePathChecksum.h" +#include "cmQtAutoGen.h" + +#include +#include + +class cmMakefile; + +class cmQtAutoGeneratorRcc +{ + CM_DISABLE_COPY(cmQtAutoGeneratorRcc) +public: + cmQtAutoGeneratorRcc(); + bool Run(std::string const& infoFile, std::string const& config); + +private: + // -- Initialization & settings + bool InfoFileRead(cmMakefile* makefile); + void SettingsFileRead(cmMakefile* makefile); + bool SettingsFileWrite(); + // -- Central processing + bool Process(cmMakefile* makefile); + bool RccGenerate(); + // -- Log info + void LogBold(std::string const& message) const; + void LogInfo(cmQtAutoGen::Generator genType, + std::string const& message) const; + // -- Log warning + void LogWarning(cmQtAutoGen::Generator genType, + std::string const& message) const; + void LogFileWarning(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const; + // -- Log error + void LogError(cmQtAutoGen::Generator genType, + std::string const& message) const; + void LogFileError(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const; + void LogCommandError(cmQtAutoGen::Generator genType, + std::string const& message, + std::vector const& command, + std::string const& output) const; + // -- Utility + bool MakeParentDirectory(cmQtAutoGen::Generator genType, + std::string const& filename) const; + bool FileDiffers(std::string const& filename, std::string const& content); + bool FileWrite(cmQtAutoGen::Generator genType, std::string const& filename, + std::string const& content); + bool RunCommand(std::vector const& command, + std::string& output) const; + + // -- Info settings + std::string InfoFile; + std::string InfoDir; + std::string InfoConfig; + // -- Config settings + std::string ConfigSuffix; + cmQtAutoGen::MultiConfig MultiConfig; + // -- Settings + bool Verbose; + bool ColorOutput; + bool SettingsChanged; + std::string SettingsFile; + std::string SettingsString; + // -- Directories + std::string ProjectSourceDir; + std::string ProjectBinaryDir; + std::string CurrentSourceDir; + std::string CurrentBinaryDir; + std::string AutogenBuildDir; + cmFilePathChecksum FilePathChecksum; + // -- Qt environment + std::string QtMajorVersion; + std::string RccExecutable; + // -- Job + std::string QrcFile; + std::string RccFile; + std::vector Options; + std::vector Inputs; +}; + +#endif diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index 449db9d..a37bc3c 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -6,6 +6,7 @@ #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmQtAutoGeneratorRcc.h" #include "cmQtAutoGenerators.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" @@ -992,11 +993,20 @@ int cmcmd::ExecuteCMakeCommand(std::vector& args) } #ifdef CMAKE_BUILD_WITH_CMAKE - if (args[1] == "cmake_autogen" && args.size() >= 4) { - cmQtAutoGenerators autogen; + if ((args[1] == "cmake_autogen") && (args.size() >= 4)) { + cmQtAutoGenerators autoGen; + std::string const& infoDir = args[2]; std::string const& config = args[3]; - bool autogenSuccess = autogen.Run(args[2], config); - return autogenSuccess ? 0 : 1; + return autoGen.Run(infoDir, config) ? 0 : 1; + } + if ((args[1] == "cmake_autorcc") && (args.size() >= 3)) { + cmQtAutoGeneratorRcc autoGen; + std::string const& infoFile = args[2]; + std::string config; + if (args.size() > 3) { + config = args[3]; + }; + return autoGen.Run(infoFile, config) ? 0 : 1; } #endif -- cgit v0.12 From a87f82e0258148f3047a3487c832a569dd841e09 Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Thu, 16 Nov 2017 15:17:14 +0100 Subject: Autogen: Switch to use custom commands for RCC Instead of processing all `rcc` invocation requests in the _autogen target that calls `cmake -E cmake_autogen ...` once, use a dedicated custom command that calls `cmake -E cmake_autorcc ...` for each `.qrc` file. This allows parallel `.qrc` file processing and reduces the workload (and complexity) in the _autogen target. If only `AUTORCC` is enabled, the _autogen target won't be created at all since it is now used for `AUTOMOC` and `AUTOUIC` only. For `.qrc` files that are GENERATED a custom target is used instead of a custom command. Closes #17161 --- Modules/AutoRccInfo.cmake.in | 11 + Source/cmQtAutoGenDigest.h | 2 + Source/cmQtAutoGeneratorInitializer.cxx | 527 +++++++++++++++++++------------- 3 files changed, 331 insertions(+), 209 deletions(-) create mode 100644 Modules/AutoRccInfo.cmake.in diff --git a/Modules/AutoRccInfo.cmake.in b/Modules/AutoRccInfo.cmake.in new file mode 100644 index 0000000..7b13b9e --- /dev/null +++ b/Modules/AutoRccInfo.cmake.in @@ -0,0 +1,11 @@ +# Meta +set(ARCC_MULTI_CONFIG @_multi_config@) +# Directories and files +set(ARCC_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@/") +set(ARCC_CMAKE_SOURCE_DIR "@CMAKE_SOURCE_DIR@/") +set(ARCC_CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@/") +set(ARCC_CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@/") +set(ARCC_BUILD_DIR @_build_dir@) +# Qt environment +set(ARCC_QT_VERSION_MAJOR @_qt_version_major@) +set(ARCC_QT_RCC_EXECUTABLE @_qt_rcc_executable@) diff --git a/Source/cmQtAutoGenDigest.h b/Source/cmQtAutoGenDigest.h index 677c397..9ee1117 100644 --- a/Source/cmQtAutoGenDigest.h +++ b/Source/cmQtAutoGenDigest.h @@ -24,6 +24,8 @@ public: std::string QrcFile; std::string QrcName; std::string PathChecksum; + std::string InfoFile; + std::string SettingsFile; std::string RccFile; bool Generated; bool Unique; diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx index c7550e6..9477bc3 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGeneratorInitializer.cxx @@ -635,29 +635,6 @@ static std::string RccGetExecutable(cmGeneratorTarget const* target, return rccExec; } -static void SetupAutoTargetRcc(cmQtAutoGenDigest const& digest) -{ - std::vector rccFiles; - std::vector rccBuilds; - std::vector> rccOptions; - std::vector> rccInputs; - - for (cmQtAutoGenDigestQrc const& qrcDigest : digest.Qrcs) { - rccFiles.push_back(qrcDigest.QrcFile); - rccBuilds.push_back(qrcDigest.RccFile); - rccOptions.push_back(qrcDigest.Options); - rccInputs.push_back(qrcDigest.Resources); - } - - cmMakefile* makefile = digest.Target->Target->GetMakefile(); - AddDefinitionEscaped(makefile, "_qt_rcc_executable", - RccGetExecutable(digest.Target, digest.QtVersionMajor)); - AddDefinitionEscaped(makefile, "_rcc_files", rccFiles); - AddDefinitionEscaped(makefile, "_rcc_builds", rccBuilds); - AddDefinitionEscaped(makefile, "_rcc_options", rccOptions); - AddDefinitionEscaped(makefile, "_rcc_inputs", rccInputs); -} - void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( cmQtAutoGenDigest& digest) { @@ -681,6 +658,24 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( std::set autogenDependTargets; std::vector autogenProvides; + // Autogen target FOLDER property + std::string autogenFolder; + { + const char* folder = + makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER"); + if (folder == nullptr) { + folder = + makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER"); + } + // Inherit FOLDER property from target (#13688) + if (folder == nullptr) { + folder = SafeString(target->Target->GetProperty("FOLDER")); + } + if (folder != nullptr) { + autogenFolder = folder; + } + } + // Remove build directories on cleanup AddCleanFile(makefile, autogenBuildDir); // Remove old settings on cleanup @@ -699,45 +694,6 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( } } - // Compose command lines - cmCustomCommandLines commandLines; - { - cmCustomCommandLine currentLine; - currentLine.push_back(cmSystemTools::GetCMakeCommand()); - currentLine.push_back("-E"); - currentLine.push_back("cmake_autogen"); - currentLine.push_back(autogenInfoDir); - currentLine.push_back("$"); - commandLines.push_back(currentLine); - } - - // Compose target comment - std::string autogenComment; - { - std::vector toolNames; - if (digest.MocEnabled) { - toolNames.emplace_back("MOC"); - } - if (digest.UicEnabled) { - toolNames.emplace_back("UIC"); - } - if (digest.RccEnabled) { - toolNames.emplace_back("RCC"); - } - - std::string tools = toolNames.front(); - toolNames.erase(toolNames.begin()); - if (!toolNames.empty()) { - while (toolNames.size() > 1) { - tools += ", "; - tools += toolNames.front(); - toolNames.erase(toolNames.begin()); - } - tools += " and " + toolNames.front(); - } - autogenComment = "Automatic " + tools + " for target " + target->GetName(); - } - // Add moc compilation to generated files list if (digest.MocEnabled) { std::string const mocsComp = autogenBuildDir + "/mocs_compilation.cpp"; @@ -921,18 +877,30 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( } } } - // Path checksum + // Path checksum and file names { cmFilePathChecksum const fpathCheckSum(makefile); for (cmQtAutoGenDigestQrc& qrcDigest : digest.Qrcs) { qrcDigest.PathChecksum = fpathCheckSum.getPart(qrcDigest.QrcFile); // RCC output file name - std::string rccFile = autogenBuildDir + "/"; - rccFile += qrcDigest.PathChecksum; - rccFile += "/qrc_"; - rccFile += qrcDigest.QrcName; - rccFile += ".cpp"; - qrcDigest.RccFile = std::move(rccFile); + { + std::string rccFile = autogenBuildDir + "/"; + rccFile += qrcDigest.PathChecksum; + rccFile += "/qrc_"; + rccFile += qrcDigest.QrcName; + rccFile += ".cpp"; + qrcDigest.RccFile = std::move(rccFile); + } + { + std::string base = autogenInfoDir; + base += "/RCC"; + base += qrcDigest.QrcName; + if (!qrcDigest.Unique) { + base += qrcDigest.PathChecksum; + } + qrcDigest.InfoFile = base + "Info.cmake"; + qrcDigest.SettingsFile = base + "Settings.cmake"; + } } } // RCC options @@ -959,159 +927,239 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( } for (cmQtAutoGenDigestQrc& qrcDigest : digest.Qrcs) { // Register file at target + std::vector const ccOutput = AddGeneratedSource( + target, qrcDigest.RccFile, multiConfig, configsList, cmQtAutoGen::RCC); + + cmCustomCommandLines commandLines; { - auto files = AddGeneratedSource(target, qrcDigest.RccFile, multiConfig, - configsList, cmQtAutoGen::RCC); - for (std::string& file : files) { - autogenProvides.push_back(std::move(file)); - } + cmCustomCommandLine currentLine; + currentLine.push_back(cmSystemTools::GetCMakeCommand()); + currentLine.push_back("-E"); + currentLine.push_back("cmake_autorcc"); + currentLine.push_back(qrcDigest.InfoFile); + currentLine.push_back("$"); + commandLines.push_back(std::move(currentLine)); } - // Dependencies + std::string ccComment = "Automatic RCC for "; + ccComment += qrcDigest.QrcFile; + if (qrcDigest.Generated) { - // Add the GENERATED .qrc file to the dependencies - autogenDependFiles.insert(qrcDigest.QrcFile); + // Create custom rcc target + std::string ccName; + { + ccName = target->GetName(); + ccName += "_arcc_"; + ccName += qrcDigest.QrcName; + if (!qrcDigest.Unique) { + ccName += "_"; + ccName += qrcDigest.PathChecksum; + } + std::vector ccDepends; + // Add the .qrc file to the custom target dependencies + ccDepends.push_back(qrcDigest.QrcFile); + + cmTarget* autoRccTarget = makefile->AddUtilityCommand( + ccName, true, workingDirectory.c_str(), ccOutput, ccDepends, + commandLines, false, ccComment.c_str()); + // Create autogen generator target + localGen->AddGeneratorTarget( + new cmGeneratorTarget(autoRccTarget, localGen)); + + // Set FOLDER property in autogen target + if (!autogenFolder.empty()) { + autoRccTarget->SetProperty("FOLDER", autogenFolder.c_str()); + } + } + // Add autogen target to the origin target dependencies + target->Target->AddUtility(ccName, makefile); } else { - // Add the resource files to the dependencies + // Create custom rcc command { - std::string error; - if (cmQtAutoGen::RccListInputs(digest.QtVersionMajor, rcc, - qrcDigest.QrcFile, - qrcDigest.Resources, &error)) { - for (std::string const& fileName : qrcDigest.Resources) { - autogenDependFiles.insert(fileName); + std::vector ccByproducts; + std::vector ccDepends; + // Add the .qrc file to the custom command dependencies + ccDepends.push_back(qrcDigest.QrcFile); + + // Add the resource files to the dependencies + { + std::string error; + if (cmQtAutoGen::RccListInputs(digest.QtVersionMajor, rcc, + qrcDigest.QrcFile, + qrcDigest.Resources, &error)) { + for (std::string const& fileName : qrcDigest.Resources) { + // Add resource file to the custom command dependencies + ccDepends.push_back(fileName); + } + } else { + cmSystemTools::Error(error.c_str()); } - } else { - cmSystemTools::Error(error.c_str()); } + makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends, + /*main_dependency*/ std::string(), + commandLines, ccComment.c_str(), + workingDirectory.c_str()); } - // Run cmake again when .qrc file changes + // Reconfigure when .qrc file changes makefile->AddCMakeDependFile(qrcDigest.QrcFile); } } } - // Add user defined autogen target dependencies - { - std::string const deps = GetSafeProperty(target, "AUTOGEN_TARGET_DEPENDS"); - if (!deps.empty()) { - std::vector extraDeps; - cmSystemTools::ExpandListArgument(deps, extraDeps); - for (std::string const& depName : extraDeps) { - // Allow target and file dependencies - auto* depTarget = makefile->FindTargetToUse(depName); - if (depTarget != nullptr) { - autogenDependTargets.insert(depTarget); - } else { - autogenDependFiles.insert(depName); + // Create _autogen target + if (digest.MocEnabled || digest.UicEnabled) { + // Add user defined autogen target dependencies + { + std::string const deps = + GetSafeProperty(target, "AUTOGEN_TARGET_DEPENDS"); + if (!deps.empty()) { + std::vector extraDeps; + cmSystemTools::ExpandListArgument(deps, extraDeps); + for (std::string const& depName : extraDeps) { + // Allow target and file dependencies + auto* depTarget = makefile->FindTargetToUse(depName); + if (depTarget != nullptr) { + autogenDependTargets.insert(depTarget); + } else { + autogenDependFiles.insert(depName); + } } } } - } - // Use PRE_BUILD on demand - bool usePRE_BUILD = false; - if (globalGen->GetName().find("Visual Studio") != std::string::npos) { - // Under VS use a PRE_BUILD event instead of a separate target to - // reduce the number of targets loaded into the IDE. - // This also works around a VS 11 bug that may skip updating the target: - // https://connect.microsoft.com/VisualStudio/feedback/details/769495 - usePRE_BUILD = true; - } - // Disable PRE_BUILD in some cases - if (usePRE_BUILD) { - // Cannot use PRE_BUILD with file depends - if (!autogenDependFiles.empty()) { - usePRE_BUILD = false; + // Compose target comment + std::string autogenComment; + { + std::vector toolNames; + if (digest.MocEnabled) { + toolNames.emplace_back("MOC"); + } + if (digest.UicEnabled) { + toolNames.emplace_back("UIC"); + } + if (digest.RccEnabled) { + toolNames.emplace_back("RCC"); + } + + std::string tools = toolNames.front(); + toolNames.erase(toolNames.begin()); + if (!toolNames.empty()) { + while (toolNames.size() > 1) { + tools += ", "; + tools += toolNames.front(); + toolNames.erase(toolNames.begin()); + } + tools += " and " + toolNames.front(); + } + autogenComment = + "Automatic " + tools + " for target " + target->GetName(); } - } - // Create the autogen target/command - if (usePRE_BUILD) { - // Add additional autogen target dependencies to origin target - for (cmTarget* depTarget : autogenDependTargets) { - target->Target->AddUtility(depTarget->GetName(), makefile); + + // Compose command lines + cmCustomCommandLines commandLines; + { + cmCustomCommandLine currentLine; + currentLine.push_back(cmSystemTools::GetCMakeCommand()); + currentLine.push_back("-E"); + currentLine.push_back("cmake_autogen"); + currentLine.push_back(autogenInfoDir); + currentLine.push_back("$"); + commandLines.push_back(std::move(currentLine)); } - // 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 no_output; - const std::vector no_deps; - cmCustomCommand cc(makefile, no_output, autogenProvides, no_deps, - commandLines, autogenComment.c_str(), - workingDirectory.c_str()); - cc.SetEscapeOldStyle(false); - cc.SetEscapeAllowMakeVars(true); - target->Target->AddPreBuildCommand(cc); - } else { + // Use PRE_BUILD on demand + bool usePRE_BUILD = false; + if (globalGen->GetName().find("Visual Studio") != std::string::npos) { + // Under VS use a PRE_BUILD event instead of a separate target to + // reduce the number of targets loaded into the IDE. + // This also works around a VS 11 bug that may skip updating the target: + // https://connect.microsoft.com/VisualStudio/feedback/details/769495 + usePRE_BUILD = true; + } + // Disable PRE_BUILD in some cases + if (usePRE_BUILD) { + // Cannot use PRE_BUILD with file depends + if (!autogenDependFiles.empty()) { + usePRE_BUILD = false; + } + } + // Create the autogen target/command + if (usePRE_BUILD) { + // Add additional autogen target dependencies to origin target + for (cmTarget* depTarget : autogenDependTargets) { + target->Target->AddUtility(depTarget->GetName(), makefile); + } - // Convert file dependencies std::set to std::vector - std::vector autogenDepends(autogenDependFiles.begin(), - autogenDependFiles.end()); + // 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 no_output; + const std::vector no_deps; + cmCustomCommand cc(makefile, no_output, autogenProvides, no_deps, + commandLines, autogenComment.c_str(), + workingDirectory.c_str()); + cc.SetEscapeOldStyle(false); + cc.SetEscapeAllowMakeVars(true); + target->Target->AddPreBuildCommand(cc); + } else { - // Add link library target dependencies to the autogen target dependencies - for (std::string const& config : configsList) { - cmLinkImplementationLibraries const* libs = - target->GetLinkImplementationLibraries(config); - if (libs != nullptr) { - for (cmLinkItem const& item : libs->Libraries) { - cmGeneratorTarget const* libTarget = item.Target; - if ((libTarget != nullptr) && - !StaticLibraryCycle(target, libTarget, config)) { - std::string util; - if (configsList.size() > 1) { - util += "$<$GetName(); - if (configsList.size() > 1) { - util += ">"; + // Convert file dependencies std::set to std::vector + std::vector autogenDepends(autogenDependFiles.begin(), + autogenDependFiles.end()); + + // Add link library target dependencies to the autogen target + // dependencies + for (std::string const& config : configsList) { + cmLinkImplementationLibraries const* libs = + target->GetLinkImplementationLibraries(config); + if (libs != nullptr) { + for (cmLinkItem const& item : libs->Libraries) { + cmGeneratorTarget const* libTarget = item.Target; + if ((libTarget != nullptr) && + !StaticLibraryCycle(target, libTarget, config)) { + std::string util; + if (configsList.size() > 1) { + util += "$<$GetName(); + if (configsList.size() > 1) { + util += ">"; + } + autogenDepends.push_back(util); } - autogenDepends.push_back(util); } } } - } - // Create autogen target - cmTarget* autogenTarget = makefile->AddUtilityCommand( - autogenTargetName, true, workingDirectory.c_str(), - /*byproducts=*/autogenProvides, autogenDepends, commandLines, false, - autogenComment.c_str()); - // Create autogen generator target - localGen->AddGeneratorTarget( - new cmGeneratorTarget(autogenTarget, localGen)); - - // Forward origin utilities to autogen target - for (std::string const& depName : target->Target->GetUtilities()) { - autogenTarget->AddUtility(depName, makefile); - } - // Add additional autogen target dependencies to autogen target - for (cmTarget* depTarget : autogenDependTargets) { - autogenTarget->AddUtility(depTarget->GetName(), makefile); - } - - // Set FOLDER property in autogen target - { - const char* autogenFolder = - makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER"); - if (autogenFolder == nullptr) { - autogenFolder = - makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER"); + // Create autogen target + cmTarget* autogenTarget = makefile->AddUtilityCommand( + autogenTargetName, true, workingDirectory.c_str(), + /*byproducts=*/autogenProvides, autogenDepends, commandLines, false, + autogenComment.c_str()); + // Create autogen generator target + localGen->AddGeneratorTarget( + new cmGeneratorTarget(autogenTarget, localGen)); + + // Forward origin utilities to autogen target + for (std::string const& depName : target->Target->GetUtilities()) { + autogenTarget->AddUtility(depName, makefile); } - // Inherit FOLDER property from target (#13688) - if (autogenFolder == nullptr) { - autogenFolder = SafeString(target->Target->GetProperty("FOLDER")); + // Add additional autogen target dependencies to autogen target + for (cmTarget* depTarget : autogenDependTargets) { + autogenTarget->AddUtility(depTarget->GetName(), makefile); } - if ((autogenFolder != nullptr) && (*autogenFolder != '\0')) { - autogenTarget->SetProperty("FOLDER", autogenFolder); + + // Set FOLDER property in autogen target + if (!autogenFolder.empty()) { + autogenTarget->SetProperty("FOLDER", autogenFolder.c_str()); } - } - // Add autogen target to the origin target dependencies - target->Target->AddUtility(autogenTargetName, makefile); + // Add autogen target to the origin target dependencies + target->Target->AddUtility(autogenTargetName, makefile); + } } } @@ -1164,18 +1212,36 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( } } if (digest.RccEnabled) { - SetupAutoTargetRcc(digest); + AddDefinitionEscaped( + makefile, "_qt_rcc_executable", + RccGetExecutable(digest.Target, digest.QtVersionMajor)); } } - // Generate info file - { - std::string const infoDir = GetAutogenTargetFilesDir(target); - if (!cmSystemTools::MakeDirectory(infoDir)) { - std::string emsg = ("Could not create directory: "); - emsg += cmQtAutoGen::Quoted(infoDir); - cmSystemTools::Error(emsg.c_str()); + // Create info directory on demand + std::string const infoDir = GetAutogenTargetFilesDir(target); + if (!cmSystemTools::MakeDirectory(infoDir)) { + std::string emsg = ("Could not create directory: "); + emsg += cmQtAutoGen::Quoted(infoDir); + cmSystemTools::Error(emsg.c_str()); + } + + auto AdjustFilePermissions = [](std::string const& fileName) { + // Ensure we have write permission + mode_t perm = 0; +#if defined(_WIN32) && !defined(__CYGWIN__) + mode_t mode_write = S_IWRITE; +#else + mode_t mode_write = S_IWUSR; +#endif + cmSystemTools::GetPermissions(fileName, perm); + if (!(perm & mode_write)) { + cmSystemTools::SetPermissions(fileName, perm | mode_write); } + }; + + // Generate autogen target info file + { std::string const infoFile = infoDir + "/AutogenInfo.cmake"; { std::string infoFileIn = cmSystemTools::GetCMakeRoot(); @@ -1188,16 +1254,7 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( // -------------------------------------- // Ensure we have write permission in case .in was read-only. - mode_t perm = 0; -#if defined(_WIN32) && !defined(__CYGWIN__) - mode_t mode_write = S_IWRITE; -#else - mode_t mode_write = S_IWUSR; -#endif - cmSystemTools::GetPermissions(infoFile, perm); - if (!(perm & mode_write)) { - cmSystemTools::SetPermissions(infoFile, perm | mode_write); - } + AdjustFilePermissions(infoFile); // Open and write file cmsys::ofstream ofs(infoFile.c_str(), std::ios::app); @@ -1222,4 +1279,56 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( cmSystemTools::Error(error.c_str()); } } + + // Generate auto RCC info files + { + std::string infoFileIn = cmSystemTools::GetCMakeRoot(); + infoFileIn += "/Modules/AutoRccInfo.cmake.in"; + for (cmQtAutoGenDigestQrc const& qrcDigest : digest.Qrcs) { + // Configure info file + makefile->ConfigureFile(infoFileIn.c_str(), qrcDigest.InfoFile.c_str(), + false, true, false); + + // Append custom definitions to info file + // -------------------------------------- + + // Ensure we have write permission in case .in was read-only. + AdjustFilePermissions(qrcDigest.InfoFile); + + // Open and write file + cmsys::ofstream ofs(qrcDigest.InfoFile.c_str(), std::ios::app); + if (ofs) { + { + ofs << "# Configurations options\n"; + auto OfsWriteMap = [&ofs]( + const char* key, std::map const& map) { + for (auto const& item : map) { + ofs << "set(" << key << "_" << item.first << " " + << cmOutputConverter::EscapeForCMake(item.second) << ")\n"; + } + }; + OfsWriteMap("ARCC_CONFIG_SUFFIX", configSuffixes); + } + { + ofs << "# Job\n"; + auto OfsWrite = [&ofs](const char* key, std::string const& value) { + ofs << "set(" << key << " " + << cmOutputConverter::EscapeForCMake(value) << ")\n"; + + }; + OfsWrite("ARCC_SETTINGS_FILE", qrcDigest.SettingsFile); + OfsWrite("ARCC_SOURCE", qrcDigest.QrcFile); + OfsWrite("ARCC_OUTPUT", qrcDigest.RccFile); + OfsWrite("ARCC_OPTIONS", cmJoin(qrcDigest.Options, ";")); + OfsWrite("ARCC_INPUTS", cmJoin(qrcDigest.Resources, ";")); + } + } else { + // File open error + std::string error = "Internal CMake error when trying to open file: "; + error += cmQtAutoGen::Quoted(qrcDigest.InfoFile); + error += " for writing."; + cmSystemTools::Error(error.c_str()); + } + } + } } -- cgit v0.12 From 1cd285fe06088d6dad58a3b52c071579aa0ce8b8 Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Fri, 17 Nov 2017 12:53:20 +0100 Subject: Autogen: Remove rcc code from cmQtAutoGenerators --- Modules/AutogenInfo.cmake.in | 6 - Source/cmQtAutoGenerators.cxx | 287 ------------------------------------------ Source/cmQtAutoGenerators.h | 22 +--- 3 files changed, 1 insertion(+), 314 deletions(-) diff --git a/Modules/AutogenInfo.cmake.in b/Modules/AutogenInfo.cmake.in index 7f4b398..7d1d6d8 100644 --- a/Modules/AutogenInfo.cmake.in +++ b/Modules/AutogenInfo.cmake.in @@ -14,7 +14,6 @@ set(AM_QT_VERSION_MAJOR @_qt_version_major@) set(AM_QT_VERSION_MINOR @_qt_version_minor@) set(AM_QT_MOC_EXECUTABLE @_qt_moc_executable@) set(AM_QT_UIC_EXECUTABLE @_qt_uic_executable@) -set(AM_QT_RCC_EXECUTABLE @_qt_rcc_executable@) # MOC settings set(AM_MOC_SKIP @_moc_skip@) set(AM_MOC_DEFINITIONS @_moc_compile_defs@) @@ -30,8 +29,3 @@ set(AM_UIC_TARGET_OPTIONS @_uic_target_options@) set(AM_UIC_OPTIONS_FILES @_qt_uic_options_files@) set(AM_UIC_OPTIONS_OPTIONS @_qt_uic_options_options@) set(AM_UIC_SEARCH_PATHS @_uic_search_paths@) -# RCC settings -set(AM_RCC_SOURCES @_rcc_files@) -set(AM_RCC_BUILDS @_rcc_builds@) -set(AM_RCC_OPTIONS @_rcc_options@) -set(AM_RCC_INPUTS @_rcc_inputs@) diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx index 28a8df1..bc6a28e 100644 --- a/Source/cmQtAutoGenerators.cxx +++ b/Source/cmQtAutoGenerators.cxx @@ -32,7 +32,6 @@ static const char* SettingsKeyMoc = "AM_MOC_SETTINGS_HASH"; static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH"; -static const char* SettingsKeyRcc = "AM_RCC_SETTINGS_HASH"; // -- Static functions @@ -139,7 +138,6 @@ cmQtAutoGenerators::cmQtAutoGenerators() , MocPredefsChanged(false) , MocRelaxedMode(false) , UicSettingsChanged(false) - , RccSettingsChanged(false) { { std::string colorEnv; @@ -290,7 +288,6 @@ bool cmQtAutoGenerators::InitInfoFile(cmMakefile* makefile, this->QtMinorVersion = InfoGet("AM_QT_VERSION_MINOR"); this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE"); this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE"); - this->RccExecutable = InfoGet("AM_QT_RCC_EXECUTABLE"); // Check Qt version if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) { @@ -379,53 +376,6 @@ bool cmQtAutoGenerators::InitInfoFile(cmMakefile* makefile, } } - // - Rcc - if (this->RccEnabled()) { - // File lists - auto sources = InfoGetList("AM_RCC_SOURCES"); - auto builds = InfoGetList("AM_RCC_BUILDS"); - auto options = InfoGetLists("AM_RCC_OPTIONS"); - auto inputs = InfoGetLists("AM_RCC_INPUTS"); - - if (sources.size() != builds.size()) { - std::ostringstream ost; - ost << "sources, builds lists sizes missmatch (" << sources.size() << "/" - << builds.size() << ")"; - this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str()); - return false; - } - if (sources.size() != options.size()) { - std::ostringstream ost; - ost << "sources, options lists sizes missmatch (" << sources.size() - << "/" << options.size() << ")"; - this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str()); - return false; - } - if (sources.size() != inputs.size()) { - std::ostringstream ost; - ost << "sources, inputs lists sizes missmatch (" << sources.size() << "/" - << inputs.size() << ")"; - this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str()); - return false; - } - { - auto srcItEnd = sources.end(); - auto srcIt = sources.begin(); - auto bldIt = builds.begin(); - auto optIt = options.begin(); - auto inpIt = inputs.begin(); - while (srcIt != srcItEnd) { - this->RccJobs.push_back(RccJob{ std::move(*srcIt), std::move(*bldIt), - std::move(*optIt), - std::move(*inpIt) }); - ++srcIt; - ++bldIt; - ++optIt; - ++inpIt; - } - } - } - // Initialize source file jobs { // Utility lambdas @@ -631,20 +581,6 @@ void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile) str += sep; this->SettingsStringUic = crypt.HashString(str); } - if (this->RccEnabled()) { - std::string str; - str += this->RccExecutable; - for (const RccJob& rccJob : this->RccJobs) { - str += sep; - str += rccJob.QrcFile; - str += sep; - str += rccJob.RccFile; - str += sep; - str += cmJoin(rccJob.Options, ";"); - } - str += sep; - this->SettingsStringRcc = crypt.HashString(str); - } } // Read old settings @@ -659,9 +595,6 @@ void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile) if (!SMatch(SettingsKeyUic, this->SettingsStringUic)) { this->UicSettingsChanged = true; } - if (!SMatch(SettingsKeyRcc, this->SettingsStringRcc)) { - this->RccSettingsChanged = true; - } } // In case any setting changed remove the old settings file. // This triggers a full rebuild on the next run if the current @@ -673,7 +606,6 @@ void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile) // If the file could not be read re-generate everythiung. this->MocSettingsChanged = true; this->UicSettingsChanged = true; - this->RccSettingsChanged = true; } } @@ -699,7 +631,6 @@ bool cmQtAutoGenerators::SettingsFileWrite() }; SettingAppend(SettingsKeyMoc, this->SettingsStringMoc); SettingAppend(SettingsKeyUic, this->SettingsStringUic); - SettingAppend(SettingsKeyRcc, this->SettingsStringRcc); } // Write settings file if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) { @@ -758,9 +689,6 @@ bool cmQtAutoGenerators::Process() if (!this->UicGenerateAll()) { return false; } - if (!this->RccGenerateAll()) { - return false; - } return true; } @@ -1911,221 +1839,6 @@ bool cmQtAutoGenerators::UicGenerateFile(const UicJob& uicJob) return success; } -bool cmQtAutoGenerators::RccGenerateAll() -{ - if (!this->RccEnabled()) { - return true; - } - - // Generate rcc files - for (const RccJob& rccJob : this->RccJobs) { - if (!this->RccGenerateFile(rccJob)) { - return false; - } - } - return true; -} - -/** - * @return True on success - */ -bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob) -{ - bool success = true; - bool rccGenerated = false; - - std::string rccFileAbs; - { - std::string suffix; - switch (this->MultiConfig) { - case cmQtAutoGen::SINGLE: - break; - case cmQtAutoGen::WRAP: - suffix = "_CMAKE"; - suffix += this->ConfigSuffix; - suffix += "_"; - break; - case cmQtAutoGen::FULL: - suffix = this->ConfigSuffix; - break; - } - rccFileAbs = cmQtAutoGen::AppendFilenameSuffix(rccJob.RccFile, suffix); - } - std::string const rccFileRel = cmSystemTools::RelativePath( - this->AutogenBuildDir.c_str(), rccFileAbs.c_str()); - - // Check if regeneration is required - bool generate = false; - std::string generateReason; - if (!cmSystemTools::FileExists(rccJob.QrcFile)) { - { - std::string error = "Could not find the file\n "; - error += cmQtAutoGen::Quoted(rccJob.QrcFile); - this->LogError(cmQtAutoGen::RCC, error); - } - success = false; - } - if (success && !generate && !cmSystemTools::FileExists(rccFileAbs.c_str())) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(rccFileAbs); - generateReason += " from its source file "; - generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile); - generateReason += " because it doesn't exist"; - } - generate = true; - } - if (success && !generate && this->RccSettingsChanged) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(rccFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile); - generateReason += " because the RCC settings changed"; - } - generate = true; - } - if (success && !generate) { - std::string error; - if (FileIsOlderThan(rccFileAbs, rccJob.QrcFile, &error)) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(rccFileAbs); - generateReason += " because it is older than "; - generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile); - } - generate = true; - } else { - if (!error.empty()) { - this->LogError(cmQtAutoGen::RCC, error); - success = false; - } - } - } - if (success && !generate) { - // Acquire input file list - std::vector readFiles; - std::vector const* files = nullptr; - if (!rccJob.Inputs.empty()) { - files = &rccJob.Inputs; - } else { - // Read input file list from qrc file - std::string error; - if (cmQtAutoGen::RccListInputs(this->QtMajorVersion, this->RccExecutable, - rccJob.QrcFile, readFiles, &error)) { - files = &readFiles; - } else { - this->LogFileError(cmQtAutoGen::RCC, rccJob.QrcFile, error); - success = false; - } - } - // Test if any input file is newer than the build file - if (files != nullptr) { - std::string error; - for (std::string const& resFile : *files) { - if (!cmSystemTools::FileExists(resFile.c_str())) { - error = "Could not find the file\n "; - error += cmQtAutoGen::Quoted(resFile); - error += "\nwhich is listed in\n "; - error += cmQtAutoGen::Quoted(rccJob.QrcFile); - break; - } - if (FileIsOlderThan(rccFileAbs, resFile, &error)) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(rccFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(rccJob.QrcFile); - generateReason += " because it is older than "; - generateReason += cmQtAutoGen::Quoted(resFile); - } - generate = true; - break; - } - if (!error.empty()) { - break; - } - } - // Print error - if (!error.empty()) { - this->LogError(cmQtAutoGen::RCC, error); - success = false; - } - } - } - // Regenerate on demand - if (generate) { - // Log - if (this->Verbose) { - this->LogBold("Generating RCC source " + rccFileRel); - this->LogInfo(cmQtAutoGen::RCC, generateReason); - } - - // Make sure the parent directory exists - if (this->MakeParentDirectory(cmQtAutoGen::RCC, rccFileAbs)) { - // Compose rcc command - std::vector cmd; - cmd.push_back(this->RccExecutable); - cmd.insert(cmd.end(), rccJob.Options.begin(), rccJob.Options.end()); - cmd.push_back("-o"); - cmd.push_back(rccFileAbs); - cmd.push_back(rccJob.QrcFile); - - std::string output; - if (this->RunCommand(cmd, output)) { - // Success - rccGenerated = true; - } else { - { - std::string emsg = "rcc failed for\n "; - emsg += cmQtAutoGen::Quoted(rccJob.QrcFile); - this->LogCommandError(cmQtAutoGen::RCC, emsg, cmd, output); - } - cmSystemTools::RemoveFile(rccFileAbs); - success = false; - } - } else { - // Parent directory creation failed - success = false; - } - } - - // Generate a wrapper source file on demand - if (success && (this->MultiConfig == cmQtAutoGen::WRAP)) { - // Wrapper file name - std::string const& wrapperFileAbs = rccJob.RccFile; - std::string const wrapperFileRel = cmSystemTools::RelativePath( - this->AutogenBuildDir.c_str(), wrapperFileAbs.c_str()); - // Wrapper file content - std::string content = "// This is an autogenerated configuration " - "wrapper file. Changes will be overwritten.\n" - "#include \""; - content += cmSystemTools::GetFilenameName(rccFileRel); - content += "\"\n"; - // Write content to file - if (this->FileDiffers(wrapperFileAbs, content)) { - // Write new wrapper file - if (this->Verbose) { - this->LogBold("Generating RCC wrapper " + wrapperFileRel); - } - if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) { - this->LogFileError(cmQtAutoGen::RCC, wrapperFileAbs, - "rcc wrapper file writing failed"); - success = false; - } - } else if (rccGenerated) { - // Just touch the wrapper file - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::RCC, - "Touching RCC wrapper " + wrapperFileRel); - } - cmSystemTools::Touch(wrapperFileAbs, false); - } - } - - return success; -} - void cmQtAutoGenerators::LogBold(std::string const& message) const { cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h index a7bb538..b6e28b3 100644 --- a/Source/cmQtAutoGenerators.h +++ b/Source/cmQtAutoGenerators.h @@ -80,15 +80,6 @@ private: std::string IncludeString; }; - /// @brief RCC job - struct RccJob - { - std::string QrcFile; - std::string RccFile; - std::vector Options; - std::vector Inputs; - }; - // -- Initialization bool InitInfoFile(cmMakefile* makefile, std::string const& targetDirectory, std::string const& config); @@ -98,8 +89,7 @@ private: bool SettingsFileWrite(); bool SettingsChanged() const { - return (this->MocSettingsChanged || this->RccSettingsChanged || - this->UicSettingsChanged); + return (this->MocSettingsChanged || this->UicSettingsChanged); } // -- Central processing @@ -146,11 +136,6 @@ private: bool UicGenerateAll(); bool UicGenerateFile(const UicJob& uicJob); - // -- Rcc - bool RccEnabled() const { return !this->RccExecutable.empty(); } - bool RccGenerateAll(); - bool RccGenerateFile(const RccJob& rccJob); - // -- Log info void LogBold(std::string const& message) const; void LogInfo(cmQtAutoGen::Generator genType, @@ -193,7 +178,6 @@ private: std::string SettingsFile; std::string SettingsStringMoc; std::string SettingsStringUic; - std::string SettingsStringRcc; // -- Directories std::string ProjectSourceDir; std::string ProjectBinaryDir; @@ -206,7 +190,6 @@ private: std::string QtMinorVersion; std::string MocExecutable; std::string UicExecutable; - std::string RccExecutable; // -- File lists std::map HeaderJobs; std::map SourceJobs; @@ -240,9 +223,6 @@ private: std::vector UicSearchPaths; cmsys::RegularExpression UicRegExpInclude; std::vector> UicJobs; - // -- Rcc - bool RccSettingsChanged; - std::vector RccJobs; }; #endif -- cgit v0.12 From 27ed3b3537676e6090a0845e4805bb4a65d05bae Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Fri, 17 Nov 2017 13:04:26 +0100 Subject: Autogen: Rename cmQtAutoGenerators to cmQtAutoGeneratorMocUic --- Source/CMakeLists.txt | 4 +- Source/cmQtAutoGeneratorMocUic.cxx | 2045 ++++++++++++++++++++++++++++++++++++ Source/cmQtAutoGeneratorMocUic.h | 228 ++++ Source/cmQtAutoGenerators.cxx | 2045 ------------------------------------ Source/cmQtAutoGenerators.h | 228 ---- Source/cmcmd.cxx | 4 +- 6 files changed, 2277 insertions(+), 2277 deletions(-) create mode 100644 Source/cmQtAutoGeneratorMocUic.cxx create mode 100644 Source/cmQtAutoGeneratorMocUic.h delete mode 100644 Source/cmQtAutoGenerators.cxx delete mode 100644 Source/cmQtAutoGenerators.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index fd02196..cddef68 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -314,8 +314,8 @@ set(SRCS cmQtAutoGenDigest.h cmQtAutoGeneratorInitializer.cxx cmQtAutoGeneratorInitializer.h - cmQtAutoGenerators.cxx - cmQtAutoGenerators.h + cmQtAutoGeneratorMocUic.cxx + cmQtAutoGeneratorMocUic.h cmQtAutoGeneratorRcc.cxx cmQtAutoGeneratorRcc.h cmRST.cxx diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx new file mode 100644 index 0000000..36fc090 --- /dev/null +++ b/Source/cmQtAutoGeneratorMocUic.cxx @@ -0,0 +1,2045 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmQtAutoGen.h" +#include "cmQtAutoGeneratorMocUic.h" + +#include "cmsys/FStream.hxx" +#include "cmsys/Terminal.h" +#include +#include +#include +#include +#include +#include +#include + +#include "cmAlgorithms.h" +#include "cmCryptoHash.h" +#include "cmFilePathChecksum.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmOutputConverter.h" +#include "cmStateDirectory.h" +#include "cmStateSnapshot.h" +#include "cmSystemTools.h" +#include "cmake.h" + +#if defined(__APPLE__) +#include +#endif + +// -- Static variables + +static const char* SettingsKeyMoc = "AM_MOC_SETTINGS_HASH"; +static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH"; + +// -- Static functions + +static std::string HeadLine(std::string const& title) +{ + std::string head = title; + head += '\n'; + head.append(head.size() - 1, '-'); + head += '\n'; + return head; +} + +static std::string QuotedCommand(std::vector const& command) +{ + std::string res; + for (std::string const& item : command) { + if (!res.empty()) { + res.push_back(' '); + } + std::string const cesc = cmQtAutoGen::Quoted(item); + if (item.empty() || (cesc.size() > (item.size() + 2)) || + (cesc.find(' ') != std::string::npos)) { + res += cesc; + } else { + res += item; + } + } + return res; +} + +static std::string SubDirPrefix(std::string const& fileName) +{ + std::string res(cmSystemTools::GetFilenamePath(fileName)); + if (!res.empty()) { + res += '/'; + } + return res; +} + +static bool ReadFile(std::string& content, std::string const& filename, + std::string* error = nullptr) +{ + bool success = false; + if (cmSystemTools::FileExists(filename)) { + std::size_t const length = cmSystemTools::FileLength(filename); + cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); + if (ifs) { + content.resize(length); + ifs.read(&content.front(), content.size()); + if (ifs) { + success = true; + } else { + content.clear(); + if (error != nullptr) { + error->append("Reading from the file failed."); + } + } + } else if (error != nullptr) { + error->append("Opening the file for reading failed."); + } + } else if (error != nullptr) { + error->append("The file does not exist."); + } + return success; +} + +/** + * @brief Tests if buildFile is older than sourceFile + * @return True if buildFile is older than sourceFile. + * False may indicate an error. + */ +static bool FileIsOlderThan(std::string const& buildFile, + std::string const& sourceFile, + std::string* error = nullptr) +{ + int result = 0; + if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) { + return (result < 0); + } + if (error != nullptr) { + error->append( + "File modification time comparison failed for the files\n "); + error->append(cmQtAutoGen::Quoted(buildFile)); + error->append("\nand\n "); + error->append(cmQtAutoGen::Quoted(sourceFile)); + } + return false; +} + +static bool ListContains(std::vector const& list, + std::string const& entry) +{ + return (std::find(list.begin(), list.end(), entry) != list.end()); +} + +// -- Class methods + +cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic() + : MultiConfig(cmQtAutoGen::WRAP) + , IncludeProjectDirsBefore(false) + , Verbose(cmSystemTools::HasEnv("VERBOSE")) + , ColorOutput(true) + , MocSettingsChanged(false) + , MocPredefsChanged(false) + , MocRelaxedMode(false) + , UicSettingsChanged(false) +{ + { + std::string colorEnv; + cmSystemTools::GetEnv("COLOR", colorEnv); + if (!colorEnv.empty()) { + this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str()); + } + } + + // Precompile regular expressions + this->MocRegExpInclude.compile( + "[\n][ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); + this->UicRegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); +} + +bool cmQtAutoGeneratorMocUic::Run(std::string const& targetDirectory, + std::string const& config) +{ + cmake cm(cmake::RoleScript); + cm.SetHomeOutputDirectory(targetDirectory); + cm.SetHomeDirectory(targetDirectory); + cm.GetCurrentSnapshot().SetDefaultDefinitions(); + cmGlobalGenerator gg(&cm); + + cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); + snapshot.GetDirectory().SetCurrentBinary(targetDirectory); + snapshot.GetDirectory().SetCurrentSource(targetDirectory); + + auto makefile = cm::make_unique(&gg, snapshot); + gg.SetCurrentMakefile(makefile.get()); + + bool success = false; + if (this->InitInfoFile(makefile.get(), targetDirectory, config)) { + // Read latest settings + this->SettingsFileRead(makefile.get()); + if (this->Process()) { + // Write current settings + if (this->SettingsFileWrite()) { + success = true; + } + } + } + return success; +} + +bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile, + std::string const& targetDirectory, + std::string const& config) +{ + // -- Meta + this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); + + // Utility lambdas + auto InfoGet = [makefile](const char* key) { + return makefile->GetSafeDefinition(key); + }; + auto InfoGetBool = [makefile](const char* key) { + return makefile->IsOn(key); + }; + auto InfoGetList = [makefile](const char* key) -> std::vector { + std::vector list; + cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list); + return list; + }; + auto InfoGetLists = + [makefile](const char* key) -> std::vector> { + std::vector> lists; + { + std::string const value = makefile->GetSafeDefinition(key); + std::string::size_type pos = 0; + while (pos < value.size()) { + std::string::size_type next = value.find(cmQtAutoGen::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); + { + std::string subValue(itBeg, itEnd); + std::vector list; + cmSystemTools::ExpandListArgument(subValue, list); + lists.push_back(std::move(list)); + } + } + pos += length; + pos += cmQtAutoGen::listSep.size(); + } + } + return lists; + }; + auto InfoGetConfig = [makefile, &config](const char* key) -> std::string { + const char* valueConf = nullptr; + { + std::string keyConf = key; + keyConf += '_'; + keyConf += config; + valueConf = makefile->GetDefinition(keyConf); + } + if (valueConf == nullptr) { + valueConf = makefile->GetSafeDefinition(key); + } + return std::string(valueConf); + }; + auto InfoGetConfigList = + [&InfoGetConfig](const char* key) -> std::vector { + std::vector list; + cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); + return list; + }; + + // -- Read info file + this->InfoFile = cmSystemTools::CollapseFullPath(targetDirectory); + cmSystemTools::ConvertToUnixSlashes(this->InfoFile); + this->InfoFile += "/AutogenInfo.cmake"; + if (!makefile->ReadListFile(this->InfoFile.c_str())) { + this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + "File processing failed"); + return false; + } + + // -- Meta + this->MultiConfig = cmQtAutoGen::MultiConfigType(InfoGet("AM_MULTI_CONFIG")); + this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX"); + if (this->ConfigSuffix.empty()) { + this->ConfigSuffix = "_"; + this->ConfigSuffix += config; + } + + // - Files and directories + this->ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); + this->ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); + this->CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); + this->CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); + this->IncludeProjectDirsBefore = + InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); + this->AutogenBuildDir = InfoGet("AM_BUILD_DIR"); + if (this->AutogenBuildDir.empty()) { + this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + "Autogen build directory missing"); + return false; + } + + // - Qt environment + this->QtMajorVersion = InfoGet("AM_QT_VERSION_MAJOR"); + this->QtMinorVersion = InfoGet("AM_QT_VERSION_MINOR"); + this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE"); + this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE"); + + // Check Qt version + if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) { + this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + "Unsupported Qt version: " + + cmQtAutoGen::Quoted(this->QtMajorVersion)); + return false; + } + + // - Moc + if (this->MocEnabled()) { + this->MocSkipList = InfoGetList("AM_MOC_SKIP"); + this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); +#ifdef _WIN32 + { + std::string const win32("WIN32"); + if (!ListContains(this->MocDefinitions, win32)) { + this->MocDefinitions.push_back(win32); + } + } +#endif + this->MocIncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); + this->MocOptions = InfoGetList("AM_MOC_OPTIONS"); + this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); + { + std::vector const MocMacroNames = + InfoGetList("AM_MOC_MACRO_NAMES"); + for (std::string const& item : MocMacroNames) { + this->MocMacroFilters.emplace_back( + item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); + } + } + { + std::vector const mocDependFilters = + InfoGetList("AM_MOC_DEPEND_FILTERS"); + // Insert Q_PLUGIN_METADATA dependency filter + if (this->QtMajorVersion != "4") { + this->MocDependFilterPush("Q_PLUGIN_METADATA", + "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" + "[^\\)]*FILE[ \t]*\"([^\"]+)\""); + } + // Insert user defined dependency filters + if ((mocDependFilters.size() % 2) == 0) { + for (std::vector::const_iterator + dit = mocDependFilters.begin(), + ditEnd = mocDependFilters.end(); + dit != ditEnd; dit += 2) { + if (!this->MocDependFilterPush(*dit, *(dit + 1))) { + return false; + } + } + } else { + this->LogFileError( + cmQtAutoGen::MOC, this->InfoFile, + "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); + return false; + } + } + this->MocPredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); + } + + // - Uic + if (this->UicEnabled()) { + this->UicSkipList = InfoGetList("AM_UIC_SKIP"); + this->UicSearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); + this->UicTargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); + { + auto sources = InfoGetList("AM_UIC_OPTIONS_FILES"); + auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS"); + // Compare list sizes + if (sources.size() != options.size()) { + std::ostringstream ost; + ost << "files/options lists sizes missmatch (" << sources.size() << "/" + << options.size() << ")"; + this->LogFileError(cmQtAutoGen::UIC, this->InfoFile, ost.str()); + return false; + } + auto fitEnd = sources.cend(); + auto fit = sources.begin(); + auto oit = options.begin(); + while (fit != fitEnd) { + this->UicOptions[*fit] = std::move(*oit); + ++fit; + ++oit; + } + } + } + + // Initialize source file jobs + { + // Utility lambdas + auto AddJob = [this](std::map& jobs, + std::string&& sourceFile) { + const bool moc = !this->MocSkip(sourceFile); + const bool uic = !this->UicSkip(sourceFile); + if (moc || uic) { + SourceJob& job = jobs[std::move(sourceFile)]; + job.Moc = moc; + job.Uic = uic; + } + }; + + // Add header jobs + for (std::string& hdr : InfoGetList("AM_HEADERS")) { + AddJob(this->HeaderJobs, std::move(hdr)); + } + // Add source jobs + { + std::vector sources = InfoGetList("AM_SOURCES"); + // Add header(s) for the source file + for (std::string const& src : sources) { + const bool srcMoc = !this->MocSkip(src); + const bool srcUic = !this->UicSkip(src); + if (!srcMoc && !srcUic) { + continue; + } + // Search for the default header file and a private header + std::array headerBases; + headerBases[0] = SubDirPrefix(src); + headerBases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src); + headerBases[1] = headerBases[0]; + headerBases[1] += "_p"; + for (std::string const& headerBase : headerBases) { + std::string header; + if (this->FindHeader(header, headerBase)) { + const bool moc = srcMoc && !this->MocSkip(header); + const bool uic = srcUic && !this->UicSkip(header); + if (moc || uic) { + SourceJob& job = this->HeaderJobs[std::move(header)]; + job.Moc = moc; + job.Uic = uic; + } + } + } + } + // Add Source jobs + for (std::string& src : sources) { + AddJob(this->SourceJobs, std::move(src)); + } + } + } + + // Init derived information + // ------------------------ + + // Init file path checksum generator + this->FilePathChecksum.setupParentDirs( + this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir, + this->ProjectBinaryDir); + + // include directory + this->AutogenIncludeDir = "include"; + if (this->MultiConfig != cmQtAutoGen::SINGLE) { + this->AutogenIncludeDir += this->ConfigSuffix; + } + this->AutogenIncludeDir += "/"; + + // Moc variables + if (this->MocEnabled()) { + // Mocs compilation file + this->MocCompFileRel = "mocs_compilation"; + if (this->MultiConfig == cmQtAutoGen::FULL) { + this->MocCompFileRel += this->ConfigSuffix; + } + this->MocCompFileRel += ".cpp"; + this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, this->MocCompFileRel); + + // Moc predefs file + if (!this->MocPredefsCmd.empty()) { + this->MocPredefsFileRel = "moc_predefs"; + if (this->MultiConfig != cmQtAutoGen::SINGLE) { + this->MocPredefsFileRel += this->ConfigSuffix; + } + this->MocPredefsFileRel += ".h"; + this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, this->MocPredefsFileRel); + } + + // Sort include directories on demand + if (this->IncludeProjectDirsBefore) { + // Move strings to temporary list + std::list includes; + includes.insert(includes.end(), this->MocIncludePaths.begin(), + this->MocIncludePaths.end()); + this->MocIncludePaths.clear(); + this->MocIncludePaths.reserve(includes.size()); + // Append project directories only + { + std::array const movePaths = { + { &this->ProjectBinaryDir, &this->ProjectSourceDir } + }; + for (std::string const* ppath : movePaths) { + std::list::iterator it = includes.begin(); + while (it != includes.end()) { + std::string const& path = *it; + if (cmSystemTools::StringStartsWith(path, ppath->c_str())) { + this->MocIncludePaths.push_back(path); + it = includes.erase(it); + } else { + ++it; + } + } + } + } + // Append remaining directories + this->MocIncludePaths.insert(this->MocIncludePaths.end(), + includes.begin(), includes.end()); + } + // Compose moc includes list + { + std::set frameworkPaths; + for (std::string const& path : this->MocIncludePaths) { + this->MocIncludes.push_back("-I" + path); + // Extract framework path + if (cmHasLiteralSuffix(path, ".framework/Headers")) { + // Go up twice to get to the framework root + std::vector pathComponents; + cmSystemTools::SplitPath(path, pathComponents); + std::string frameworkPath = cmSystemTools::JoinPath( + pathComponents.begin(), pathComponents.end() - 2); + frameworkPaths.insert(frameworkPath); + } + } + // Append framework includes + for (std::string const& path : frameworkPaths) { + this->MocIncludes.push_back("-F"); + this->MocIncludes.push_back(path); + } + } + // Setup single list with all options + { + // Add includes + this->MocAllOptions.insert(this->MocAllOptions.end(), + this->MocIncludes.begin(), + this->MocIncludes.end()); + // Add definitions + for (std::string const& def : this->MocDefinitions) { + this->MocAllOptions.push_back("-D" + def); + } + // Add options + this->MocAllOptions.insert(this->MocAllOptions.end(), + this->MocOptions.begin(), + this->MocOptions.end()); + } + } + + // - Old settings file + { + this->SettingsFile = cmSystemTools::CollapseFullPath(targetDirectory); + cmSystemTools::ConvertToUnixSlashes(this->SettingsFile); + this->SettingsFile += "/AutogenOldSettings"; + if (this->MultiConfig != cmQtAutoGen::SINGLE) { + this->SettingsFile += this->ConfigSuffix; + } + this->SettingsFile += ".cmake"; + } + + return true; +} + +void cmQtAutoGeneratorMocUic::SettingsFileRead(cmMakefile* makefile) +{ + // Compose current settings strings + { + cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); + std::string const sep(" ~~~ "); + if (this->MocEnabled()) { + std::string str; + str += this->MocExecutable; + str += sep; + str += cmJoin(this->MocAllOptions, ";"); + str += sep; + str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE"; + str += sep; + str += cmJoin(this->MocPredefsCmd, ";"); + str += sep; + this->SettingsStringMoc = crypt.HashString(str); + } + if (this->UicEnabled()) { + std::string str; + str += this->UicExecutable; + str += sep; + str += cmJoin(this->UicTargetOptions, ";"); + for (const auto& item : this->UicOptions) { + str += sep; + str += item.first; + str += sep; + str += cmJoin(item.second, ";"); + } + str += sep; + this->SettingsStringUic = crypt.HashString(str); + } + } + + // Read old settings + if (makefile->ReadListFile(this->SettingsFile.c_str())) { + { + auto SMatch = [makefile](const char* key, std::string const& value) { + return (value == makefile->GetSafeDefinition(key)); + }; + if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) { + this->MocSettingsChanged = true; + } + if (!SMatch(SettingsKeyUic, this->SettingsStringUic)) { + this->UicSettingsChanged = true; + } + } + // In case any setting changed remove the old settings file. + // This triggers a full rebuild on the next run if the current + // build is aborted before writing the current settings in the end. + if (this->SettingsChanged()) { + cmSystemTools::RemoveFile(this->SettingsFile); + } + } else { + // If the file could not be read re-generate everythiung. + this->MocSettingsChanged = true; + this->UicSettingsChanged = true; + } +} + +bool cmQtAutoGeneratorMocUic::SettingsFileWrite() +{ + bool success = true; + // Only write if any setting changed + if (this->SettingsChanged()) { + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " + + cmQtAutoGen::Quoted(this->SettingsFile)); + } + // Compose settings file content + std::string settings; + { + auto SettingAppend = [&settings](const char* key, + std::string const& value) { + settings += "set("; + settings += key; + settings += " "; + settings += cmOutputConverter::EscapeForCMake(value); + settings += ")\n"; + }; + SettingAppend(SettingsKeyMoc, this->SettingsStringMoc); + SettingAppend(SettingsKeyUic, this->SettingsStringUic); + } + // Write settings file + if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) { + this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile, + "Settings file writing failed"); + // Remove old settings file to trigger a full rebuild on the next run + cmSystemTools::RemoveFile(this->SettingsFile); + success = false; + } + } + return success; +} + +bool cmQtAutoGeneratorMocUic::Process() +{ + // the program goes through all .cpp files to see which moc files are + // included. It is not really interesting how the moc file is named, but + // what file the moc is created from. Once a moc is included the same moc + // may not be included in the mocs_compilation.cpp file anymore. + // OTOH if there's a header containing Q_OBJECT where no corresponding + // moc file is included anywhere a moc_.cpp file is created and + // included in the mocs_compilation.cpp file. + + // Create AUTOGEN include directory + { + std::string const incDirAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, this->AutogenIncludeDir); + if (!cmSystemTools::MakeDirectory(incDirAbs)) { + this->LogFileError(cmQtAutoGen::GEN, incDirAbs, + "Could not create directory"); + return false; + } + } + + // Parse source files + for (const auto& item : this->SourceJobs) { + if (!this->ParseSourceFile(item.first, item.second)) { + return false; + } + } + // Parse header files + for (const auto& item : this->HeaderJobs) { + if (!this->ParseHeaderFile(item.first, item.second)) { + return false; + } + } + // Read missing dependency information + if (!this->ParsePostprocess()) { + return false; + } + + // Generate files + if (!this->MocGenerateAll()) { + return false; + } + if (!this->UicGenerateAll()) { + return false; + } + + return true; +} + +/** + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::ParseSourceFile(std::string const& absFilename, + const SourceJob& job) +{ + std::string contentText; + std::string error; + bool success = ReadFile(contentText, absFilename, &error); + if (success) { + if (!contentText.empty()) { + if (job.Moc) { + success = this->MocParseSourceContent(absFilename, contentText); + } + if (success && job.Uic) { + success = this->UicParseContent(absFilename, contentText); + } + } else { + this->LogFileWarning(cmQtAutoGen::GEN, absFilename, + "The source file is empty"); + } + } else { + this->LogFileError(cmQtAutoGen::GEN, absFilename, + "Could not read the source file: " + error); + } + return success; +} + +/** + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::ParseHeaderFile(std::string const& absFilename, + const SourceJob& job) +{ + std::string contentText; + std::string error; + bool success = ReadFile(contentText, absFilename, &error); + if (success) { + if (!contentText.empty()) { + if (job.Moc) { + this->MocParseHeaderContent(absFilename, contentText); + } + if (job.Uic) { + success = this->UicParseContent(absFilename, contentText); + } + } else { + this->LogFileWarning(cmQtAutoGen::GEN, absFilename, + "The header file is empty"); + } + } else { + this->LogFileError(cmQtAutoGen::GEN, absFilename, + "Could not read the header file: " + error); + } + return success; +} + +/** + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::ParsePostprocess() +{ + bool success = true; + // Read missing dependencies + for (auto& item : this->MocJobsIncluded) { + if (!item->DependsValid) { + std::string content; + std::string error; + if (ReadFile(content, item->SourceFile, &error)) { + this->MocFindDepends(item->SourceFile, content, item->Depends); + item->DependsValid = true; + } else { + std::string emsg = "Could not read file\n "; + emsg += item->SourceFile; + emsg += "\nrequired by moc include \""; + emsg += item->IncludeString; + emsg += "\".\n"; + emsg += error; + this->LogFileError(cmQtAutoGen::MOC, item->Includer, emsg); + success = false; + break; + } + } + } + return success; +} + +/** + * @brief Tests if the file should be ignored for moc scanning + * @return True if the file should be ignored + */ +bool cmQtAutoGeneratorMocUic::MocSkip(std::string const& absFilename) const +{ + if (this->MocEnabled()) { + // Test if the file name is on the skip list + if (!ListContains(this->MocSkipList, absFilename)) { + return false; + } + } + return true; +} + +/** + * @brief Tests if the C++ content requires moc processing + * @return True if moc is required + */ +bool cmQtAutoGeneratorMocUic::MocRequired(std::string const& contentText, + std::string* macroName) +{ + for (KeyRegExp& filter : this->MocMacroFilters) { + // Run a simple find string operation before the expensive + // regular expression check + if (contentText.find(filter.Key) != std::string::npos) { + if (filter.RegExp.find(contentText)) { + // Return macro name on demand + if (macroName != nullptr) { + *macroName = filter.Key; + } + return true; + } + } + } + return false; +} + +std::string cmQtAutoGeneratorMocUic::MocStringMacros() const +{ + std::string res; + const auto itB = this->MocMacroFilters.cbegin(); + const auto itE = this->MocMacroFilters.cend(); + const auto itL = itE - 1; + auto itC = itB; + for (; itC != itE; ++itC) { + // Separator + if (itC != itB) { + if (itC != itL) { + res += ", "; + } else { + res += " or "; + } + } + // Key + res += itC->Key; + } + return res; +} + +std::string cmQtAutoGeneratorMocUic::MocStringHeaders( + std::string const& fileBase) const +{ + std::string res = fileBase; + res += ".{"; + res += cmJoin(this->HeaderExtensions, ","); + res += "}"; + return res; +} + +std::string cmQtAutoGeneratorMocUic::MocFindIncludedHeader( + std::string const& sourcePath, std::string const& includeBase) const +{ + std::string header; + // Search in vicinity of the source + if (!this->FindHeader(header, sourcePath + includeBase)) { + // Search in include directories + for (std::string const& path : this->MocIncludePaths) { + std::string fullPath = path; + fullPath.push_back('/'); + fullPath += includeBase; + if (this->FindHeader(header, fullPath)) { + break; + } + } + } + // Sanitize + if (!header.empty()) { + header = cmSystemTools::GetRealPath(header); + } + return header; +} + +bool cmQtAutoGeneratorMocUic::MocFindIncludedFile( + std::string& absFile, std::string const& sourcePath, + std::string const& includeString) const +{ + bool success = false; + // Search in vicinity of the source + { + std::string testPath = sourcePath; + testPath += includeString; + if (cmSystemTools::FileExists(testPath.c_str())) { + absFile = cmSystemTools::GetRealPath(testPath); + success = true; + } + } + // Search in include directories + if (!success) { + for (std::string const& path : this->MocIncludePaths) { + std::string fullPath = path; + fullPath.push_back('/'); + fullPath += includeString; + if (cmSystemTools::FileExists(fullPath.c_str())) { + absFile = cmSystemTools::GetRealPath(fullPath); + success = true; + break; + } + } + } + return success; +} + +bool cmQtAutoGeneratorMocUic::MocDependFilterPush(std::string const& key, + std::string const& regExp) +{ + std::string error; + if (!key.empty()) { + if (!regExp.empty()) { + KeyRegExp filter; + filter.Key = key; + if (filter.RegExp.compile(regExp)) { + this->MocDependFilters.push_back(std::move(filter)); + } else { + error = "Regular expression compiling failed"; + } + } else { + error = "Regular expression is empty"; + } + } else { + error = "Key is empty"; + } + if (!error.empty()) { + std::string emsg = "AUTOMOC_DEPEND_FILTERS: "; + emsg += error; + emsg += "\n"; + emsg += " Key: "; + emsg += cmQtAutoGen::Quoted(key); + emsg += "\n"; + emsg += " RegExp: "; + emsg += cmQtAutoGen::Quoted(regExp); + emsg += "\n"; + this->LogError(cmQtAutoGen::MOC, emsg); + return false; + } + return true; +} + +void cmQtAutoGeneratorMocUic::MocFindDepends(std::string const& absFilename, + std::string const& contentText, + std::set& depends) +{ + if (this->MocDependFilters.empty() && contentText.empty()) { + return; + } + + std::vector matches; + for (KeyRegExp& filter : this->MocDependFilters) { + // Run a simple find string check + if (contentText.find(filter.Key) != std::string::npos) { + // Run the expensive regular expression check loop + const char* contentChars = contentText.c_str(); + while (filter.RegExp.find(contentChars)) { + std::string match = filter.RegExp.match(1); + if (!match.empty()) { + matches.emplace_back(std::move(match)); + } + contentChars += filter.RegExp.end(); + } + } + } + + if (!matches.empty()) { + std::string const sourcePath = SubDirPrefix(absFilename); + for (std::string const& match : matches) { + // Find the dependency file + std::string incFile; + if (this->MocFindIncludedFile(incFile, sourcePath, match)) { + depends.insert(incFile); + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::MOC, "Found dependency:\n " + + cmQtAutoGen::Quoted(absFilename) + "\n " + + cmQtAutoGen::Quoted(incFile)); + } + } else { + this->LogFileWarning(cmQtAutoGen::MOC, absFilename, + "Could not find dependency file " + + cmQtAutoGen::Quoted(match)); + } + } + } +} + +/** + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::MocParseSourceContent( + std::string const& absFilename, std::string const& contentText) +{ + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); + } + + auto AddJob = [this, &absFilename](std::string const& sourceFile, + std::string const& includeString, + std::string const* content) { + auto job = cm::make_unique(); + job->SourceFile = sourceFile; + job->BuildFileRel = this->AutogenIncludeDir; + job->BuildFileRel += includeString; + job->Includer = absFilename; + job->IncludeString = includeString; + job->DependsValid = (content != nullptr); + if (job->DependsValid) { + this->MocFindDepends(sourceFile, *content, job->Depends); + } + this->MocJobsIncluded.push_back(std::move(job)); + }; + + struct MocInc + { + std::string Inc; // full include string + std::string Dir; // include string directory + std::string Base; // include string file base + }; + + // Extract moc includes from file + std::vector mocIncsUsc; + std::vector mocIncsDot; + { + const char* contentChars = contentText.c_str(); + if (strstr(contentChars, "moc") != nullptr) { + while (this->MocRegExpInclude.find(contentChars)) { + std::string incString = this->MocRegExpInclude.match(1); + std::string incDir(SubDirPrefix(incString)); + std::string incBase = + cmSystemTools::GetFilenameWithoutLastExtension(incString); + if (cmHasLiteralPrefix(incBase, "moc_")) { + // moc_.cxx + // Remove the moc_ part from the base name + mocIncsUsc.push_back(MocInc{ std::move(incString), std::move(incDir), + incBase.substr(4) }); + } else { + // .moc + mocIncsDot.push_back(MocInc{ std::move(incString), std::move(incDir), + std::move(incBase) }); + } + // Forward content pointer + contentChars += this->MocRegExpInclude.end(); + } + } + } + + std::string selfMacroName; + const bool selfRequiresMoc = this->MocRequired(contentText, &selfMacroName); + + // Check if there is anything to do + if (!selfRequiresMoc && mocIncsUsc.empty() && mocIncsDot.empty()) { + return true; + } + + // Scan file variables + std::string const scanFileDir = SubDirPrefix(absFilename); + std::string const scanFileBase = + cmSystemTools::GetFilenameWithoutLastExtension(absFilename); + // Relaxed mode variables + bool ownDotMocIncluded = false; + std::string ownMocUscInclude; + std::string ownMocUscHeader; + + // Process moc_.cxx includes + for (const MocInc& mocInc : mocIncsUsc) { + std::string const header = + this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base); + if (!header.empty()) { + // Check if header is skipped + if (this->MocSkip(header)) { + continue; + } + // Register moc job + AddJob(header, mocInc.Inc, nullptr); + // Store meta information for relaxed mode + if (this->MocRelaxedMode && (mocInc.Base == scanFileBase)) { + ownMocUscInclude = mocInc.Inc; + ownMocUscHeader = header; + } + } else { + std::string emsg = "The file includes the moc file "; + emsg += cmQtAutoGen::Quoted(mocInc.Inc); + emsg += ", but could not find the header "; + emsg += cmQtAutoGen::Quoted(this->MocStringHeaders(mocInc.Base)); + this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); + return false; + } + } + + // Process .moc includes + for (const MocInc& mocInc : mocIncsDot) { + const bool ownMoc = (mocInc.Base == scanFileBase); + if (this->MocRelaxedMode) { + // Relaxed mode + if (selfRequiresMoc && ownMoc) { + // Add self + AddJob(absFilename, mocInc.Inc, &contentText); + ownDotMocIncluded = true; + } else { + // In relaxed mode try to find a header instead but issue a warning. + // This is for KDE4 compatibility + std::string const header = + this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base); + if (!header.empty()) { + // Check if header is skipped + if (this->MocSkip(header)) { + continue; + } + // Register moc job + AddJob(header, mocInc.Inc, nullptr); + if (!selfRequiresMoc) { + if (ownMoc) { + std::string emsg = "The file includes the moc file "; + emsg += cmQtAutoGen::Quoted(mocInc.Inc); + emsg += ", but does not contain a "; + emsg += this->MocStringMacros(); + emsg += " macro.\nRunning moc on\n "; + emsg += cmQtAutoGen::Quoted(header); + emsg += "!\nBetter include "; + emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += " for a compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; + this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); + } else { + std::string emsg = "The file includes the moc file "; + emsg += cmQtAutoGen::Quoted(mocInc.Inc); + emsg += " instead of "; + emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += ".\nRunning moc on\n "; + emsg += cmQtAutoGen::Quoted(header); + emsg += "!\nBetter include "; + emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); + emsg += " for compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; + this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); + } + } + } else { + std::string emsg = "The file includes the moc file "; + emsg += cmQtAutoGen::Quoted(mocInc.Inc); + emsg += ", which seems to be the moc file from a different " + "source file. CMake also could not find a matching " + "header."; + this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); + return false; + } + } + } else { + // Strict mode + if (ownMoc) { + // Include self + AddJob(absFilename, mocInc.Inc, &contentText); + ownDotMocIncluded = true; + // Accept but issue a warning if moc isn't required + if (!selfRequiresMoc) { + std::string emsg = "The file includes the moc file "; + emsg += cmQtAutoGen::Quoted(mocInc.Inc); + emsg += ", but does not contain a "; + emsg += this->MocStringMacros(); + emsg += " macro."; + this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); + } + } else { + // Don't allow .moc include other than self in strict mode + std::string emsg = "The file includes the moc file "; + emsg += cmQtAutoGen::Quoted(mocInc.Inc); + emsg += ", which seems to be the moc file from a different " + "source file.\nThis is not supported. Include "; + emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); + emsg += " to run moc on this source file."; + this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); + return false; + } + } + } + + if (selfRequiresMoc && !ownDotMocIncluded) { + // In this case, check whether the scanned file itself contains a Q_OBJECT. + // If this is the case, the moc_foo.cpp should probably be generated from + // foo.cpp instead of foo.h, because otherwise it won't build. + // But warn, since this is not how it is supposed to be used. + if (this->MocRelaxedMode && !ownMocUscInclude.empty()) { + // This is for KDE4 compatibility: + std::string emsg = "The file contains a "; + emsg += selfMacroName; + emsg += " macro, but does not include "; + emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); + emsg += ". Instead it includes "; + emsg += cmQtAutoGen::Quoted(ownMocUscInclude); + emsg += ".\nRunning moc on\n "; + emsg += cmQtAutoGen::Quoted(absFilename); + emsg += "!\nBetter include "; + emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); + emsg += " for compatibility with strict mode.\n" + "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; + this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); + + // Remove own header job + { + auto itC = this->MocJobsIncluded.begin(); + auto itE = this->MocJobsIncluded.end(); + for (; itC != itE; ++itC) { + if ((*itC)->SourceFile == ownMocUscHeader) { + if ((*itC)->IncludeString == ownMocUscInclude) { + this->MocJobsIncluded.erase(itC); + break; + } + } + } + } + // Add own source job + AddJob(absFilename, ownMocUscInclude, &contentText); + } else { + // Otherwise always error out since it will not compile: + std::string emsg = "The file contains a "; + emsg += selfMacroName; + emsg += " macro, but does not include "; + emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); + emsg += "!\nConsider to\n - add #include \""; + emsg += scanFileBase; + emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; + this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); + return false; + } + } + return true; +} + +void cmQtAutoGeneratorMocUic::MocParseHeaderContent( + std::string const& absFilename, std::string const& contentText) +{ + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); + } + + auto const fit = + std::find_if(this->MocJobsIncluded.cbegin(), this->MocJobsIncluded.cend(), + [&absFilename](std::unique_ptr const& job) { + return job->SourceFile == absFilename; + }); + if (fit == this->MocJobsIncluded.cend()) { + if (this->MocRequired(contentText)) { + auto job = cm::make_unique(); + job->SourceFile = absFilename; + { + std::string& bld = job->BuildFileRel; + bld = this->FilePathChecksum.getPart(absFilename); + bld += '/'; + bld += "moc_"; + bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename); + if (this->MultiConfig != cmQtAutoGen::SINGLE) { + bld += this->ConfigSuffix; + } + bld += ".cpp"; + } + this->MocFindDepends(absFilename, contentText, job->Depends); + this->MocJobsAuto.push_back(std::move(job)); + } + } +} + +bool cmQtAutoGeneratorMocUic::MocGenerateAll() +{ + if (!this->MocEnabled()) { + return true; + } + + // Look for name collisions in included moc files + { + bool collision = false; + std::map> collisions; + for (auto const& job : this->MocJobsIncluded) { + auto& list = collisions[job->IncludeString]; + if (!list.empty()) { + collision = true; + } + list.push_back(job.get()); + } + if (collision) { + std::string emsg = + "Included moc files with the same name will be " + "generated from different sources.\n" + "Consider to\n" + " - not include the \"moc_.cpp\" file\n" + " - add a directory prefix to a \".moc\" include " + "(e.g \"sub/.moc\")\n" + " - rename the source file(s)\n" + "Include conflicts\n" + "-----------------\n"; + const auto& colls = collisions; + for (auto const& coll : colls) { + if (coll.second.size() > 1) { + emsg += cmQtAutoGen::Quoted(coll.first); + emsg += " included in\n"; + for (const MocJobIncluded* job : coll.second) { + emsg += " - "; + emsg += cmQtAutoGen::Quoted(job->Includer); + emsg += "\n"; + } + emsg += "would be generated from\n"; + for (const MocJobIncluded* job : coll.second) { + emsg += " - "; + emsg += cmQtAutoGen::Quoted(job->SourceFile); + emsg += "\n"; + } + } + } + this->LogError(cmQtAutoGen::MOC, emsg); + return false; + } + } + + // (Re)generate moc_predefs.h on demand + if (!this->MocPredefsCmd.empty()) { + if (this->MocSettingsChanged || + !cmSystemTools::FileExists(this->MocPredefsFileAbs)) { + if (this->Verbose) { + this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel); + } + + std::string output; + { + // Compose command + std::vector cmd = this->MocPredefsCmd; + // Add includes + cmd.insert(cmd.end(), this->MocIncludes.begin(), + this->MocIncludes.end()); + // Add definitions + for (std::string const& def : this->MocDefinitions) { + cmd.push_back("-D" + def); + } + // Execute command + if (!this->RunCommand(cmd, output)) { + this->LogCommandError(cmQtAutoGen::MOC, + "moc_predefs generation failed", cmd, output); + return false; + } + } + + // (Re)write predefs file only on demand + if (this->FileDiffers(this->MocPredefsFileAbs, output)) { + if (this->FileWrite(cmQtAutoGen::MOC, this->MocPredefsFileAbs, + output)) { + this->MocPredefsChanged = true; + } else { + this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs, + "moc_predefs file writing failed"); + return false; + } + } else { + // Touch to update the time stamp + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::MOC, + "Touching moc_predefs " + this->MocPredefsFileRel); + } + cmSystemTools::Touch(this->MocPredefsFileAbs, false); + } + } + + // Add moc_predefs.h to moc file dependencies + for (auto const& item : this->MocJobsIncluded) { + item->Depends.insert(this->MocPredefsFileAbs); + } + for (auto const& item : this->MocJobsAuto) { + item->Depends.insert(this->MocPredefsFileAbs); + } + } + + // Generate moc files that are included by source files. + for (auto const& item : this->MocJobsIncluded) { + if (!this->MocGenerateFile(*item)) { + return false; + } + } + // Generate moc files that are _not_ included by source files. + bool autoNameGenerated = false; + for (auto const& item : this->MocJobsAuto) { + if (!this->MocGenerateFile(*item, &autoNameGenerated)) { + return false; + } + } + + // Compose mocs compilation file content + { + std::string mocs = + "// This file is autogenerated. Changes will be overwritten.\n"; + if (this->MocJobsAuto.empty()) { + // Placeholder content + mocs += + "// No files found that require moc or the moc files are included\n"; + mocs += "enum some_compilers { need_more_than_nothing };\n"; + } else { + // Valid content + for (const auto& item : this->MocJobsAuto) { + mocs += "#include \""; + mocs += item->BuildFileRel; + mocs += "\"\n"; + } + } + + if (this->FileDiffers(this->MocCompFileAbs, mocs)) { + // Actually write mocs compilation file + if (this->Verbose) { + this->LogBold("Generating MOC compilation " + this->MocCompFileRel); + } + if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, mocs)) { + this->LogFileError(cmQtAutoGen::MOC, this->MocCompFileAbs, + "mocs compilation file writing failed"); + return false; + } + } else if (autoNameGenerated) { + // Only touch mocs compilation file + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::MOC, + "Touching mocs compilation " + this->MocCompFileRel); + } + cmSystemTools::Touch(this->MocCompFileAbs, false); + } + } + + return true; +} + +/** + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob, + bool* generated) +{ + bool success = true; + + std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, mocJob.BuildFileRel); + + bool generate = false; + std::string generateReason; + if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(mocFileAbs); + generateReason += " from its source file "; + generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); + generateReason += " because it doesn't exist"; + } + generate = true; + } + if (!generate && this->MocSettingsChanged) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(mocFileAbs); + generateReason += " from "; + generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); + generateReason += " because the MOC settings changed"; + } + generate = true; + } + if (!generate && this->MocPredefsChanged) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(mocFileAbs); + generateReason += " from "; + generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); + generateReason += " because moc_predefs.h changed"; + } + generate = true; + } + if (!generate) { + std::string error; + if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(mocFileAbs); + generateReason += " because it's older than its source file "; + generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); + } + generate = true; + } else { + if (!error.empty()) { + this->LogError(cmQtAutoGen::MOC, error); + success = false; + } + } + } + if (success && !generate) { + // Test if a dependency file is newer + std::string error; + for (std::string const& depFile : mocJob.Depends) { + if (FileIsOlderThan(mocFileAbs, depFile, &error)) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(mocFileAbs); + generateReason += " from "; + generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); + generateReason += " because it is older than "; + generateReason += cmQtAutoGen::Quoted(depFile); + } + generate = true; + break; + } + if (!error.empty()) { + this->LogError(cmQtAutoGen::MOC, error); + success = false; + break; + } + } + } + + if (generate) { + // Log + if (this->Verbose) { + this->LogBold("Generating MOC source " + mocJob.BuildFileRel); + this->LogInfo(cmQtAutoGen::MOC, generateReason); + } + + // Make sure the parent directory exists + if (this->MakeParentDirectory(cmQtAutoGen::MOC, mocFileAbs)) { + // Compose moc command + std::vector cmd; + cmd.push_back(this->MocExecutable); + // Add options + cmd.insert(cmd.end(), this->MocAllOptions.begin(), + this->MocAllOptions.end()); + // Add predefs include + if (!this->MocPredefsFileAbs.empty()) { + cmd.push_back("--include"); + cmd.push_back(this->MocPredefsFileAbs); + } + cmd.push_back("-o"); + cmd.push_back(mocFileAbs); + cmd.push_back(mocJob.SourceFile); + + // Execute moc command + std::string output; + if (this->RunCommand(cmd, output)) { + // Success + if (generated != nullptr) { + *generated = true; + } + } else { + // Moc command failed + { + std::string emsg = "moc failed for\n "; + emsg += cmQtAutoGen::Quoted(mocJob.SourceFile); + this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output); + } + cmSystemTools::RemoveFile(mocFileAbs); + success = false; + } + } else { + // Parent directory creation failed + success = false; + } + } + return success; +} + +/** + * @brief Tests if the file name is in the skip list + */ +bool cmQtAutoGeneratorMocUic::UicSkip(std::string const& absFilename) const +{ + if (this->UicEnabled()) { + // Test if the file name is on the skip list + if (!ListContains(this->UicSkipList, absFilename)) { + return false; + } + } + return true; +} + +bool cmQtAutoGeneratorMocUic::UicParseContent(std::string const& absFilename, + std::string const& contentText) +{ + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename); + } + + std::vector includes; + // Extracte includes + { + const char* contentChars = contentText.c_str(); + if (strstr(contentChars, "ui_") != nullptr) { + while (this->UicRegExpInclude.find(contentChars)) { + includes.push_back(this->UicRegExpInclude.match(1)); + contentChars += this->UicRegExpInclude.end(); + } + } + } + + for (std::string const& includeString : includes) { + std::string uiInputFile; + if (!UicFindIncludedFile(uiInputFile, absFilename, includeString)) { + return false; + } + // Check if this file should be skipped + if (this->UicSkip(uiInputFile)) { + continue; + } + // Check if the job already exists + bool jobExists = false; + for (const auto& job : this->UicJobs) { + if ((job->SourceFile == uiInputFile) && + (job->IncludeString == includeString)) { + jobExists = true; + break; + } + } + if (!jobExists) { + auto job = cm::make_unique(); + job->SourceFile = uiInputFile; + job->BuildFileRel = this->AutogenIncludeDir; + job->BuildFileRel += includeString; + job->Includer = absFilename; + job->IncludeString = includeString; + this->UicJobs.push_back(std::move(job)); + } + } + + return true; +} + +bool cmQtAutoGeneratorMocUic::UicFindIncludedFile( + std::string& absFile, std::string const& sourceFile, + std::string const& includeString) +{ + bool success = false; + std::string searchFile = + cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3); + searchFile += ".ui"; + // Collect search paths list + std::vector testFiles; + { + std::string const searchPath = SubDirPrefix(includeString); + + std::string searchFileFull; + if (!searchPath.empty()) { + searchFileFull = searchPath; + searchFileFull += searchFile; + } + // Vicinity of the source + { + std::string const sourcePath = SubDirPrefix(sourceFile); + testFiles.push_back(sourcePath + searchFile); + if (!searchPath.empty()) { + testFiles.push_back(sourcePath + searchFileFull); + } + } + // AUTOUIC search paths + if (!this->UicSearchPaths.empty()) { + for (std::string const& sPath : this->UicSearchPaths) { + testFiles.push_back((sPath + "/").append(searchFile)); + } + if (!searchPath.empty()) { + for (std::string const& sPath : this->UicSearchPaths) { + testFiles.push_back((sPath + "/").append(searchFileFull)); + } + } + } + } + + // Search for the .ui file! + for (std::string const& testFile : testFiles) { + if (cmSystemTools::FileExists(testFile.c_str())) { + absFile = cmSystemTools::GetRealPath(testFile); + success = true; + break; + } + } + + // Log error + if (!success) { + std::string emsg = "Could not find "; + emsg += cmQtAutoGen::Quoted(searchFile); + emsg += " in\n"; + for (std::string const& testFile : testFiles) { + emsg += " "; + emsg += cmQtAutoGen::Quoted(testFile); + emsg += "\n"; + } + this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg); + } + + return success; +} + +bool cmQtAutoGeneratorMocUic::UicGenerateAll() +{ + if (!this->UicEnabled()) { + return true; + } + + // Look for name collisions in included uic files + { + bool collision = false; + std::map> collisions; + for (auto const& job : this->UicJobs) { + auto& list = collisions[job->IncludeString]; + if (!list.empty()) { + collision = true; + } + list.push_back(job.get()); + } + if (collision) { + std::string emsg = + "Included uic files with the same name will be " + "generated from different sources.\n" + "Consider to\n" + " - add a directory prefix to a \"ui_.h\" include " + "(e.g \"sub/ui_.h\")\n" + " - rename the .ui file(s) and adjust the \"ui_.h\" " + "include(s)\n" + "Include conflicts\n" + "-----------------\n"; + const auto& colls = collisions; + for (auto const& coll : colls) { + if (coll.second.size() > 1) { + emsg += cmQtAutoGen::Quoted(coll.first); + emsg += " included in\n"; + for (const UicJob* job : coll.second) { + emsg += " - "; + emsg += cmQtAutoGen::Quoted(job->Includer); + emsg += "\n"; + } + emsg += "would be generated from\n"; + for (const UicJob* job : coll.second) { + emsg += " - "; + emsg += cmQtAutoGen::Quoted(job->SourceFile); + emsg += "\n"; + } + } + } + this->LogError(cmQtAutoGen::UIC, emsg); + return false; + } + } + + // Generate ui header files + for (const auto& item : this->UicJobs) { + if (!this->UicGenerateFile(*item)) { + return false; + } + } + + return true; +} + +/** + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob) +{ + bool success = true; + + std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, uicJob.BuildFileRel); + + bool generate = false; + std::string generateReason; + if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(uicFileAbs); + generateReason += " from its source file "; + generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); + generateReason += " because it doesn't exist"; + } + generate = true; + } + if (!generate && this->UicSettingsChanged) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(uicFileAbs); + generateReason += " from "; + generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); + generateReason += " because the UIC settings changed"; + } + generate = true; + } + if (!generate) { + std::string error; + if (FileIsOlderThan(uicFileAbs, uicJob.SourceFile, &error)) { + if (this->Verbose) { + generateReason = "Generating "; + generateReason += cmQtAutoGen::Quoted(uicFileAbs); + generateReason += " because it's older than its source file "; + generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); + } + generate = true; + } else { + if (!error.empty()) { + this->LogError(cmQtAutoGen::UIC, error); + success = false; + } + } + } + if (generate) { + // Log + if (this->Verbose) { + this->LogBold("Generating UIC header " + uicJob.BuildFileRel); + this->LogInfo(cmQtAutoGen::UIC, generateReason); + } + + // Make sure the parent directory exists + if (this->MakeParentDirectory(cmQtAutoGen::UIC, uicFileAbs)) { + // Compose uic command + std::vector cmd; + cmd.push_back(this->UicExecutable); + { + std::vector allOpts = this->UicTargetOptions; + auto optionIt = this->UicOptions.find(uicJob.SourceFile); + if (optionIt != this->UicOptions.end()) { + cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second, + (this->QtMajorVersion == "5")); + } + cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); + } + cmd.push_back("-o"); + cmd.push_back(uicFileAbs); + cmd.push_back(uicJob.SourceFile); + + std::string output; + if (this->RunCommand(cmd, output)) { + // Success + } else { + // Command failed + { + std::string emsg = "uic failed for\n "; + emsg += cmQtAutoGen::Quoted(uicJob.SourceFile); + emsg += "\nincluded by\n "; + emsg += cmQtAutoGen::Quoted(uicJob.Includer); + this->LogCommandError(cmQtAutoGen::UIC, emsg, cmd, output); + } + cmSystemTools::RemoveFile(uicFileAbs); + success = false; + } + } else { + // Parent directory creation failed + success = false; + } + } + return success; +} + +void cmQtAutoGeneratorMocUic::LogBold(std::string const& message) const +{ + cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, this->ColorOutput); +} + +void cmQtAutoGeneratorMocUic::LogInfo(cmQtAutoGen::Generator genType, + std::string const& message) const +{ + std::string msg = cmQtAutoGen::GeneratorName(genType); + msg += ": "; + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + cmSystemTools::Stdout(msg.c_str(), msg.size()); +} + +void cmQtAutoGeneratorMocUic::LogWarning(cmQtAutoGen::Generator genType, + std::string const& message) const +{ + std::string msg = cmQtAutoGen::GeneratorName(genType); + msg += " warning:"; + if (message.find('\n') == std::string::npos) { + // Single line message + msg.push_back(' '); + } else { + // Multi line message + msg.push_back('\n'); + } + // Message + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + cmSystemTools::Stdout(msg.c_str(), msg.size()); +} + +void cmQtAutoGeneratorMocUic::LogFileWarning(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const +{ + std::string msg = " "; + msg += cmQtAutoGen::Quoted(filename); + msg.push_back('\n'); + // Message + msg += message; + this->LogWarning(genType, msg); +} + +void cmQtAutoGeneratorMocUic::LogError(cmQtAutoGen::Generator genType, + std::string const& message) const +{ + std::string msg; + msg.push_back('\n'); + msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error"); + // Message + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + cmSystemTools::Stderr(msg.c_str(), msg.size()); +} + +void cmQtAutoGeneratorMocUic::LogFileError(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const +{ + std::string emsg = " "; + emsg += cmQtAutoGen::Quoted(filename); + emsg += '\n'; + // Message + emsg += message; + this->LogError(genType, emsg); +} + +void cmQtAutoGeneratorMocUic::LogCommandError( + cmQtAutoGen::Generator genType, std::string const& message, + std::vector const& command, std::string const& output) const +{ + std::string msg; + msg.push_back('\n'); + msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error"); + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + msg += HeadLine("Command"); + msg += QuotedCommand(command); + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + msg += HeadLine("Output"); + msg += output; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + cmSystemTools::Stderr(msg.c_str(), msg.size()); +} + +/** + * @brief Generates the parent directory of the given file on demand + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::MakeParentDirectory( + cmQtAutoGen::Generator genType, std::string const& filename) const +{ + bool success = true; + std::string const dirName = cmSystemTools::GetFilenamePath(filename); + if (!dirName.empty()) { + if (!cmSystemTools::MakeDirectory(dirName)) { + this->LogFileError(genType, filename, + "Could not create parent directory"); + success = false; + } + } + return success; +} + +bool cmQtAutoGeneratorMocUic::FileDiffers(std::string const& filename, + std::string const& content) +{ + bool differs = true; + { + std::string oldContents; + if (ReadFile(oldContents, filename)) { + differs = (oldContents != content); + } + } + return differs; +} + +bool cmQtAutoGeneratorMocUic::FileWrite(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& content) +{ + std::string error; + // Make sure the parent directory exists + if (this->MakeParentDirectory(genType, filename)) { + cmsys::ofstream outfile; + outfile.open(filename.c_str(), + (std::ios::out | std::ios::binary | std::ios::trunc)); + if (outfile) { + outfile << content; + // Check for write errors + if (!outfile.good()) { + error = "File writing failed"; + } + } else { + error = "Opening file for writing failed"; + } + } + if (!error.empty()) { + this->LogFileError(genType, filename, error); + return false; + } + return true; +} + +/** + * @brief Runs a command and returns true on success + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::RunCommand( + std::vector const& command, std::string& output) const +{ + // Log command + if (this->Verbose) { + std::string qcmd = QuotedCommand(command); + qcmd.push_back('\n'); + cmSystemTools::Stdout(qcmd.c_str(), qcmd.size()); + } + // Execute command + int retVal = 0; + bool res = cmSystemTools::RunSingleCommand( + command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE); + return (res && (retVal == 0)); +} + +/** + * @brief Tries to find the header file to the given file base path by + * appending different header extensions + * @return True on success + */ +bool cmQtAutoGeneratorMocUic::FindHeader(std::string& header, + std::string const& testBasePath) const +{ + for (std::string const& ext : this->HeaderExtensions) { + std::string testFilePath(testBasePath); + testFilePath.push_back('.'); + testFilePath += ext; + if (cmSystemTools::FileExists(testFilePath.c_str())) { + header = testFilePath; + return true; + } + } + return false; +} diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoGeneratorMocUic.h new file mode 100644 index 0000000..579b4f4 --- /dev/null +++ b/Source/cmQtAutoGeneratorMocUic.h @@ -0,0 +1,228 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmQtAutoGeneratorMocUic_h +#define cmQtAutoGeneratorMocUic_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmFilePathChecksum.h" +#include "cmQtAutoGen.h" +#include "cmsys/RegularExpression.hxx" + +#include +#include // IWYU pragma: keep +#include +#include +#include + +class cmMakefile; + +class cmQtAutoGeneratorMocUic +{ + CM_DISABLE_COPY(cmQtAutoGeneratorMocUic) +public: + cmQtAutoGeneratorMocUic(); + bool Run(std::string const& targetDirectory, std::string const& config); + +private: + // -- Types + + /// @brief Search key plus regular expression pair + struct KeyRegExp + { + KeyRegExp() = default; + + KeyRegExp(const char* key, const char* regExp) + : Key(key) + , RegExp(regExp) + { + } + + KeyRegExp(std::string const& key, std::string const& regExp) + : Key(key) + , RegExp(regExp) + { + } + + std::string Key; + cmsys::RegularExpression RegExp; + }; + + /// @brief Source file job + struct SourceJob + { + bool Moc = false; + bool Uic = false; + }; + + /// @brief MOC job + struct MocJobAuto + { + std::string SourceFile; + std::string BuildFileRel; + std::set Depends; + }; + + /// @brief MOC job + struct MocJobIncluded : MocJobAuto + { + bool DependsValid = false; + std::string Includer; + std::string IncludeString; + }; + + /// @brief UIC job + struct UicJob + { + std::string SourceFile; + std::string BuildFileRel; + std::string Includer; + std::string IncludeString; + }; + + // -- Initialization + bool InitInfoFile(cmMakefile* makefile, std::string const& targetDirectory, + std::string const& config); + + // -- Settings file + void SettingsFileRead(cmMakefile* makefile); + bool SettingsFileWrite(); + bool SettingsChanged() const + { + return (this->MocSettingsChanged || this->UicSettingsChanged); + } + + // -- Central processing + bool Process(); + + // -- Source parsing + bool ParseSourceFile(std::string const& absFilename, const SourceJob& job); + bool ParseHeaderFile(std::string const& absFilename, const SourceJob& job); + bool ParsePostprocess(); + + // -- Moc + bool MocEnabled() const { return !this->MocExecutable.empty(); } + bool MocSkip(std::string const& absFilename) const; + bool MocRequired(std::string const& contentText, + std::string* macroName = nullptr); + // Moc strings + std::string MocStringMacros() const; + std::string MocStringHeaders(std::string const& fileBase) const; + std::string MocFindIncludedHeader(std::string const& sourcePath, + std::string const& includeBase) const; + bool MocFindIncludedFile(std::string& absFile, std::string const& sourceFile, + std::string const& includeString) const; + // Moc depends + bool MocDependFilterPush(std::string const& key, std::string const& regExp); + void MocFindDepends(std::string const& absFilename, + std::string const& contentText, + std::set& depends); + // Moc + bool MocParseSourceContent(std::string const& absFilename, + std::string const& contentText); + void MocParseHeaderContent(std::string const& absFilename, + std::string const& contentText); + + bool MocGenerateAll(); + bool MocGenerateFile(const MocJobAuto& mocJob, bool* generated = nullptr); + + // -- Uic + bool UicEnabled() const { return !this->UicExecutable.empty(); } + bool UicSkip(std::string const& absFilename) const; + bool UicParseContent(std::string const& fileName, + std::string const& contentText); + bool UicFindIncludedFile(std::string& absFile, std::string const& sourceFile, + std::string const& includeString); + bool UicGenerateAll(); + bool UicGenerateFile(const UicJob& uicJob); + + // -- Log info + void LogBold(std::string const& message) const; + void LogInfo(cmQtAutoGen::Generator genType, + std::string const& message) const; + // -- Log warning + void LogWarning(cmQtAutoGen::Generator genType, + std::string const& message) const; + void LogFileWarning(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const; + // -- Log error + void LogError(cmQtAutoGen::Generator genType, + std::string const& message) const; + void LogFileError(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const; + void LogCommandError(cmQtAutoGen::Generator genType, + std::string const& message, + std::vector const& command, + std::string const& output) const; + + // -- Utility + bool MakeParentDirectory(cmQtAutoGen::Generator genType, + std::string const& filename) const; + bool FileDiffers(std::string const& filename, std::string const& content); + bool FileWrite(cmQtAutoGen::Generator genType, std::string const& filename, + std::string const& content); + bool FindHeader(std::string& header, std::string const& testBasePath) const; + bool RunCommand(std::vector const& command, + std::string& output) const; + + // -- Meta + std::string InfoFile; + std::string ConfigSuffix; + cmQtAutoGen::MultiConfig MultiConfig; + // -- Settings + bool IncludeProjectDirsBefore; + bool Verbose; + bool ColorOutput; + std::string SettingsFile; + std::string SettingsStringMoc; + std::string SettingsStringUic; + // -- Directories + std::string ProjectSourceDir; + std::string ProjectBinaryDir; + std::string CurrentSourceDir; + std::string CurrentBinaryDir; + std::string AutogenBuildDir; + std::string AutogenIncludeDir; + // -- Qt environment + std::string QtMajorVersion; + std::string QtMinorVersion; + std::string MocExecutable; + std::string UicExecutable; + // -- File lists + std::map HeaderJobs; + std::map SourceJobs; + std::vector HeaderExtensions; + cmFilePathChecksum FilePathChecksum; + // -- Moc + bool MocSettingsChanged; + bool MocPredefsChanged; + bool MocRelaxedMode; + std::string MocCompFileRel; + std::string MocCompFileAbs; + std::string MocPredefsFileRel; + std::string MocPredefsFileAbs; + std::vector MocSkipList; + std::vector MocIncludePaths; + std::vector MocIncludes; + std::vector MocDefinitions; + std::vector MocOptions; + std::vector MocAllOptions; + std::vector MocPredefsCmd; + std::vector MocDependFilters; + std::vector MocMacroFilters; + cmsys::RegularExpression MocRegExpInclude; + std::vector> MocJobsIncluded; + std::vector> MocJobsAuto; + // -- Uic + bool UicSettingsChanged; + std::vector UicSkipList; + std::vector UicTargetOptions; + std::map> UicOptions; + std::vector UicSearchPaths; + cmsys::RegularExpression UicRegExpInclude; + std::vector> UicJobs; +}; + +#endif diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx deleted file mode 100644 index bc6a28e..0000000 --- a/Source/cmQtAutoGenerators.cxx +++ /dev/null @@ -1,2045 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#include "cmQtAutoGen.h" -#include "cmQtAutoGenerators.h" - -#include "cmsys/FStream.hxx" -#include "cmsys/Terminal.h" -#include -#include -#include -#include -#include -#include -#include - -#include "cmAlgorithms.h" -#include "cmCryptoHash.h" -#include "cmFilePathChecksum.h" -#include "cmGlobalGenerator.h" -#include "cmMakefile.h" -#include "cmOutputConverter.h" -#include "cmStateDirectory.h" -#include "cmStateSnapshot.h" -#include "cmSystemTools.h" -#include "cmake.h" - -#if defined(__APPLE__) -#include -#endif - -// -- Static variables - -static const char* SettingsKeyMoc = "AM_MOC_SETTINGS_HASH"; -static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH"; - -// -- Static functions - -static std::string HeadLine(std::string const& title) -{ - std::string head = title; - head += '\n'; - head.append(head.size() - 1, '-'); - head += '\n'; - return head; -} - -static std::string QuotedCommand(std::vector const& command) -{ - std::string res; - for (std::string const& item : command) { - if (!res.empty()) { - res.push_back(' '); - } - std::string const cesc = cmQtAutoGen::Quoted(item); - if (item.empty() || (cesc.size() > (item.size() + 2)) || - (cesc.find(' ') != std::string::npos)) { - res += cesc; - } else { - res += item; - } - } - return res; -} - -static std::string SubDirPrefix(std::string const& fileName) -{ - std::string res(cmSystemTools::GetFilenamePath(fileName)); - if (!res.empty()) { - res += '/'; - } - return res; -} - -static bool ReadFile(std::string& content, std::string const& filename, - std::string* error = nullptr) -{ - bool success = false; - if (cmSystemTools::FileExists(filename)) { - std::size_t const length = cmSystemTools::FileLength(filename); - cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); - if (ifs) { - content.resize(length); - ifs.read(&content.front(), content.size()); - if (ifs) { - success = true; - } else { - content.clear(); - if (error != nullptr) { - error->append("Reading from the file failed."); - } - } - } else if (error != nullptr) { - error->append("Opening the file for reading failed."); - } - } else if (error != nullptr) { - error->append("The file does not exist."); - } - return success; -} - -/** - * @brief Tests if buildFile is older than sourceFile - * @return True if buildFile is older than sourceFile. - * False may indicate an error. - */ -static bool FileIsOlderThan(std::string const& buildFile, - std::string const& sourceFile, - std::string* error = nullptr) -{ - int result = 0; - if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) { - return (result < 0); - } - if (error != nullptr) { - error->append( - "File modification time comparison failed for the files\n "); - error->append(cmQtAutoGen::Quoted(buildFile)); - error->append("\nand\n "); - error->append(cmQtAutoGen::Quoted(sourceFile)); - } - return false; -} - -static bool ListContains(std::vector const& list, - std::string const& entry) -{ - return (std::find(list.begin(), list.end(), entry) != list.end()); -} - -// -- Class methods - -cmQtAutoGenerators::cmQtAutoGenerators() - : MultiConfig(cmQtAutoGen::WRAP) - , IncludeProjectDirsBefore(false) - , Verbose(cmSystemTools::HasEnv("VERBOSE")) - , ColorOutput(true) - , MocSettingsChanged(false) - , MocPredefsChanged(false) - , MocRelaxedMode(false) - , UicSettingsChanged(false) -{ - { - std::string colorEnv; - cmSystemTools::GetEnv("COLOR", colorEnv); - if (!colorEnv.empty()) { - this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str()); - } - } - - // Precompile regular expressions - this->MocRegExpInclude.compile( - "[\n][ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?moc_[^ \">/]+\\.cpp|[^ \">]+\\.moc)[\">]"); - this->UicRegExpInclude.compile("[\n][ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); -} - -bool cmQtAutoGenerators::Run(std::string const& targetDirectory, - std::string const& config) -{ - cmake cm(cmake::RoleScript); - cm.SetHomeOutputDirectory(targetDirectory); - cm.SetHomeDirectory(targetDirectory); - cm.GetCurrentSnapshot().SetDefaultDefinitions(); - cmGlobalGenerator gg(&cm); - - cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); - snapshot.GetDirectory().SetCurrentBinary(targetDirectory); - snapshot.GetDirectory().SetCurrentSource(targetDirectory); - - auto makefile = cm::make_unique(&gg, snapshot); - gg.SetCurrentMakefile(makefile.get()); - - bool success = false; - if (this->InitInfoFile(makefile.get(), targetDirectory, config)) { - // Read latest settings - this->SettingsFileRead(makefile.get()); - if (this->Process()) { - // Write current settings - if (this->SettingsFileWrite()) { - success = true; - } - } - } - return success; -} - -bool cmQtAutoGenerators::InitInfoFile(cmMakefile* makefile, - std::string const& targetDirectory, - std::string const& config) -{ - // -- Meta - this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); - - // Utility lambdas - auto InfoGet = [makefile](const char* key) { - return makefile->GetSafeDefinition(key); - }; - auto InfoGetBool = [makefile](const char* key) { - return makefile->IsOn(key); - }; - auto InfoGetList = [makefile](const char* key) -> std::vector { - std::vector list; - cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list); - return list; - }; - auto InfoGetLists = - [makefile](const char* key) -> std::vector> { - std::vector> lists; - { - std::string const value = makefile->GetSafeDefinition(key); - std::string::size_type pos = 0; - while (pos < value.size()) { - std::string::size_type next = value.find(cmQtAutoGen::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); - { - std::string subValue(itBeg, itEnd); - std::vector list; - cmSystemTools::ExpandListArgument(subValue, list); - lists.push_back(std::move(list)); - } - } - pos += length; - pos += cmQtAutoGen::listSep.size(); - } - } - return lists; - }; - auto InfoGetConfig = [makefile, &config](const char* key) -> std::string { - const char* valueConf = nullptr; - { - std::string keyConf = key; - keyConf += '_'; - keyConf += config; - valueConf = makefile->GetDefinition(keyConf); - } - if (valueConf == nullptr) { - valueConf = makefile->GetSafeDefinition(key); - } - return std::string(valueConf); - }; - auto InfoGetConfigList = - [&InfoGetConfig](const char* key) -> std::vector { - std::vector list; - cmSystemTools::ExpandListArgument(InfoGetConfig(key), list); - return list; - }; - - // -- Read info file - this->InfoFile = cmSystemTools::CollapseFullPath(targetDirectory); - cmSystemTools::ConvertToUnixSlashes(this->InfoFile); - this->InfoFile += "/AutogenInfo.cmake"; - if (!makefile->ReadListFile(this->InfoFile.c_str())) { - this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, - "File processing failed"); - return false; - } - - // -- Meta - this->MultiConfig = cmQtAutoGen::MultiConfigType(InfoGet("AM_MULTI_CONFIG")); - this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX"); - if (this->ConfigSuffix.empty()) { - this->ConfigSuffix = "_"; - this->ConfigSuffix += config; - } - - // - Files and directories - this->ProjectSourceDir = InfoGet("AM_CMAKE_SOURCE_DIR"); - this->ProjectBinaryDir = InfoGet("AM_CMAKE_BINARY_DIR"); - this->CurrentSourceDir = InfoGet("AM_CMAKE_CURRENT_SOURCE_DIR"); - this->CurrentBinaryDir = InfoGet("AM_CMAKE_CURRENT_BINARY_DIR"); - this->IncludeProjectDirsBefore = - InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); - this->AutogenBuildDir = InfoGet("AM_BUILD_DIR"); - if (this->AutogenBuildDir.empty()) { - this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, - "Autogen build directory missing"); - return false; - } - - // - Qt environment - this->QtMajorVersion = InfoGet("AM_QT_VERSION_MAJOR"); - this->QtMinorVersion = InfoGet("AM_QT_VERSION_MINOR"); - this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE"); - this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE"); - - // Check Qt version - if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) { - this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, - "Unsupported Qt version: " + - cmQtAutoGen::Quoted(this->QtMajorVersion)); - return false; - } - - // - Moc - if (this->MocEnabled()) { - this->MocSkipList = InfoGetList("AM_MOC_SKIP"); - this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); -#ifdef _WIN32 - { - std::string const win32("WIN32"); - if (!ListContains(this->MocDefinitions, win32)) { - this->MocDefinitions.push_back(win32); - } - } -#endif - this->MocIncludePaths = InfoGetConfigList("AM_MOC_INCLUDES"); - this->MocOptions = InfoGetList("AM_MOC_OPTIONS"); - this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); - { - std::vector const MocMacroNames = - InfoGetList("AM_MOC_MACRO_NAMES"); - for (std::string const& item : MocMacroNames) { - this->MocMacroFilters.emplace_back( - item, ("[\n][ \t]*{?[ \t]*" + item).append("[^a-zA-Z0-9_]")); - } - } - { - std::vector const mocDependFilters = - InfoGetList("AM_MOC_DEPEND_FILTERS"); - // Insert Q_PLUGIN_METADATA dependency filter - if (this->QtMajorVersion != "4") { - this->MocDependFilterPush("Q_PLUGIN_METADATA", - "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" - "[^\\)]*FILE[ \t]*\"([^\"]+)\""); - } - // Insert user defined dependency filters - if ((mocDependFilters.size() % 2) == 0) { - for (std::vector::const_iterator - dit = mocDependFilters.begin(), - ditEnd = mocDependFilters.end(); - dit != ditEnd; dit += 2) { - if (!this->MocDependFilterPush(*dit, *(dit + 1))) { - return false; - } - } - } else { - this->LogFileError( - cmQtAutoGen::MOC, this->InfoFile, - "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); - return false; - } - } - this->MocPredefsCmd = InfoGetList("AM_MOC_PREDEFS_CMD"); - } - - // - Uic - if (this->UicEnabled()) { - this->UicSkipList = InfoGetList("AM_UIC_SKIP"); - this->UicSearchPaths = InfoGetList("AM_UIC_SEARCH_PATHS"); - this->UicTargetOptions = InfoGetConfigList("AM_UIC_TARGET_OPTIONS"); - { - auto sources = InfoGetList("AM_UIC_OPTIONS_FILES"); - auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS"); - // Compare list sizes - if (sources.size() != options.size()) { - std::ostringstream ost; - ost << "files/options lists sizes missmatch (" << sources.size() << "/" - << options.size() << ")"; - this->LogFileError(cmQtAutoGen::UIC, this->InfoFile, ost.str()); - return false; - } - auto fitEnd = sources.cend(); - auto fit = sources.begin(); - auto oit = options.begin(); - while (fit != fitEnd) { - this->UicOptions[*fit] = std::move(*oit); - ++fit; - ++oit; - } - } - } - - // Initialize source file jobs - { - // Utility lambdas - auto AddJob = [this](std::map& jobs, - std::string&& sourceFile) { - const bool moc = !this->MocSkip(sourceFile); - const bool uic = !this->UicSkip(sourceFile); - if (moc || uic) { - SourceJob& job = jobs[std::move(sourceFile)]; - job.Moc = moc; - job.Uic = uic; - } - }; - - // Add header jobs - for (std::string& hdr : InfoGetList("AM_HEADERS")) { - AddJob(this->HeaderJobs, std::move(hdr)); - } - // Add source jobs - { - std::vector sources = InfoGetList("AM_SOURCES"); - // Add header(s) for the source file - for (std::string const& src : sources) { - const bool srcMoc = !this->MocSkip(src); - const bool srcUic = !this->UicSkip(src); - if (!srcMoc && !srcUic) { - continue; - } - // Search for the default header file and a private header - std::array headerBases; - headerBases[0] = SubDirPrefix(src); - headerBases[0] += cmSystemTools::GetFilenameWithoutLastExtension(src); - headerBases[1] = headerBases[0]; - headerBases[1] += "_p"; - for (std::string const& headerBase : headerBases) { - std::string header; - if (this->FindHeader(header, headerBase)) { - const bool moc = srcMoc && !this->MocSkip(header); - const bool uic = srcUic && !this->UicSkip(header); - if (moc || uic) { - SourceJob& job = this->HeaderJobs[std::move(header)]; - job.Moc = moc; - job.Uic = uic; - } - } - } - } - // Add Source jobs - for (std::string& src : sources) { - AddJob(this->SourceJobs, std::move(src)); - } - } - } - - // Init derived information - // ------------------------ - - // Init file path checksum generator - this->FilePathChecksum.setupParentDirs( - this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir, - this->ProjectBinaryDir); - - // include directory - this->AutogenIncludeDir = "include"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - this->AutogenIncludeDir += this->ConfigSuffix; - } - this->AutogenIncludeDir += "/"; - - // Moc variables - if (this->MocEnabled()) { - // Mocs compilation file - this->MocCompFileRel = "mocs_compilation"; - if (this->MultiConfig == cmQtAutoGen::FULL) { - this->MocCompFileRel += this->ConfigSuffix; - } - this->MocCompFileRel += ".cpp"; - this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->MocCompFileRel); - - // Moc predefs file - if (!this->MocPredefsCmd.empty()) { - this->MocPredefsFileRel = "moc_predefs"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - this->MocPredefsFileRel += this->ConfigSuffix; - } - this->MocPredefsFileRel += ".h"; - this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->MocPredefsFileRel); - } - - // Sort include directories on demand - if (this->IncludeProjectDirsBefore) { - // Move strings to temporary list - std::list includes; - includes.insert(includes.end(), this->MocIncludePaths.begin(), - this->MocIncludePaths.end()); - this->MocIncludePaths.clear(); - this->MocIncludePaths.reserve(includes.size()); - // Append project directories only - { - std::array const movePaths = { - { &this->ProjectBinaryDir, &this->ProjectSourceDir } - }; - for (std::string const* ppath : movePaths) { - std::list::iterator it = includes.begin(); - while (it != includes.end()) { - std::string const& path = *it; - if (cmSystemTools::StringStartsWith(path, ppath->c_str())) { - this->MocIncludePaths.push_back(path); - it = includes.erase(it); - } else { - ++it; - } - } - } - } - // Append remaining directories - this->MocIncludePaths.insert(this->MocIncludePaths.end(), - includes.begin(), includes.end()); - } - // Compose moc includes list - { - std::set frameworkPaths; - for (std::string const& path : this->MocIncludePaths) { - this->MocIncludes.push_back("-I" + path); - // Extract framework path - if (cmHasLiteralSuffix(path, ".framework/Headers")) { - // Go up twice to get to the framework root - std::vector pathComponents; - cmSystemTools::SplitPath(path, pathComponents); - std::string frameworkPath = cmSystemTools::JoinPath( - pathComponents.begin(), pathComponents.end() - 2); - frameworkPaths.insert(frameworkPath); - } - } - // Append framework includes - for (std::string const& path : frameworkPaths) { - this->MocIncludes.push_back("-F"); - this->MocIncludes.push_back(path); - } - } - // Setup single list with all options - { - // Add includes - this->MocAllOptions.insert(this->MocAllOptions.end(), - this->MocIncludes.begin(), - this->MocIncludes.end()); - // Add definitions - for (std::string const& def : this->MocDefinitions) { - this->MocAllOptions.push_back("-D" + def); - } - // Add options - this->MocAllOptions.insert(this->MocAllOptions.end(), - this->MocOptions.begin(), - this->MocOptions.end()); - } - } - - // - Old settings file - { - this->SettingsFile = cmSystemTools::CollapseFullPath(targetDirectory); - cmSystemTools::ConvertToUnixSlashes(this->SettingsFile); - this->SettingsFile += "/AutogenOldSettings"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - this->SettingsFile += this->ConfigSuffix; - } - this->SettingsFile += ".cmake"; - } - - return true; -} - -void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile) -{ - // Compose current settings strings - { - cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); - std::string const sep(" ~~~ "); - if (this->MocEnabled()) { - std::string str; - str += this->MocExecutable; - str += sep; - str += cmJoin(this->MocAllOptions, ";"); - str += sep; - str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE"; - str += sep; - str += cmJoin(this->MocPredefsCmd, ";"); - str += sep; - this->SettingsStringMoc = crypt.HashString(str); - } - if (this->UicEnabled()) { - std::string str; - str += this->UicExecutable; - str += sep; - str += cmJoin(this->UicTargetOptions, ";"); - for (const auto& item : this->UicOptions) { - str += sep; - str += item.first; - str += sep; - str += cmJoin(item.second, ";"); - } - str += sep; - this->SettingsStringUic = crypt.HashString(str); - } - } - - // Read old settings - if (makefile->ReadListFile(this->SettingsFile.c_str())) { - { - auto SMatch = [makefile](const char* key, std::string const& value) { - return (value == makefile->GetSafeDefinition(key)); - }; - if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) { - this->MocSettingsChanged = true; - } - if (!SMatch(SettingsKeyUic, this->SettingsStringUic)) { - this->UicSettingsChanged = true; - } - } - // In case any setting changed remove the old settings file. - // This triggers a full rebuild on the next run if the current - // build is aborted before writing the current settings in the end. - if (this->SettingsChanged()) { - cmSystemTools::RemoveFile(this->SettingsFile); - } - } else { - // If the file could not be read re-generate everythiung. - this->MocSettingsChanged = true; - this->UicSettingsChanged = true; - } -} - -bool cmQtAutoGenerators::SettingsFileWrite() -{ - bool success = true; - // Only write if any setting changed - if (this->SettingsChanged()) { - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " + - cmQtAutoGen::Quoted(this->SettingsFile)); - } - // Compose settings file content - std::string settings; - { - auto SettingAppend = [&settings](const char* key, - std::string const& value) { - settings += "set("; - settings += key; - settings += " "; - settings += cmOutputConverter::EscapeForCMake(value); - settings += ")\n"; - }; - SettingAppend(SettingsKeyMoc, this->SettingsStringMoc); - SettingAppend(SettingsKeyUic, this->SettingsStringUic); - } - // Write settings file - if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) { - this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile, - "Settings file writing failed"); - // Remove old settings file to trigger a full rebuild on the next run - cmSystemTools::RemoveFile(this->SettingsFile); - success = false; - } - } - return success; -} - -bool cmQtAutoGenerators::Process() -{ - // the program goes through all .cpp files to see which moc files are - // included. It is not really interesting how the moc file is named, but - // what file the moc is created from. Once a moc is included the same moc - // may not be included in the mocs_compilation.cpp file anymore. - // OTOH if there's a header containing Q_OBJECT where no corresponding - // moc file is included anywhere a moc_.cpp file is created and - // included in the mocs_compilation.cpp file. - - // Create AUTOGEN include directory - { - std::string const incDirAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->AutogenIncludeDir); - if (!cmSystemTools::MakeDirectory(incDirAbs)) { - this->LogFileError(cmQtAutoGen::GEN, incDirAbs, - "Could not create directory"); - return false; - } - } - - // Parse source files - for (const auto& item : this->SourceJobs) { - if (!this->ParseSourceFile(item.first, item.second)) { - return false; - } - } - // Parse header files - for (const auto& item : this->HeaderJobs) { - if (!this->ParseHeaderFile(item.first, item.second)) { - return false; - } - } - // Read missing dependency information - if (!this->ParsePostprocess()) { - return false; - } - - // Generate files - if (!this->MocGenerateAll()) { - return false; - } - if (!this->UicGenerateAll()) { - return false; - } - - return true; -} - -/** - * @return True on success - */ -bool cmQtAutoGenerators::ParseSourceFile(std::string const& absFilename, - const SourceJob& job) -{ - std::string contentText; - std::string error; - bool success = ReadFile(contentText, absFilename, &error); - if (success) { - if (!contentText.empty()) { - if (job.Moc) { - success = this->MocParseSourceContent(absFilename, contentText); - } - if (success && job.Uic) { - success = this->UicParseContent(absFilename, contentText); - } - } else { - this->LogFileWarning(cmQtAutoGen::GEN, absFilename, - "The source file is empty"); - } - } else { - this->LogFileError(cmQtAutoGen::GEN, absFilename, - "Could not read the source file: " + error); - } - return success; -} - -/** - * @return True on success - */ -bool cmQtAutoGenerators::ParseHeaderFile(std::string const& absFilename, - const SourceJob& job) -{ - std::string contentText; - std::string error; - bool success = ReadFile(contentText, absFilename, &error); - if (success) { - if (!contentText.empty()) { - if (job.Moc) { - this->MocParseHeaderContent(absFilename, contentText); - } - if (job.Uic) { - success = this->UicParseContent(absFilename, contentText); - } - } else { - this->LogFileWarning(cmQtAutoGen::GEN, absFilename, - "The header file is empty"); - } - } else { - this->LogFileError(cmQtAutoGen::GEN, absFilename, - "Could not read the header file: " + error); - } - return success; -} - -/** - * @return True on success - */ -bool cmQtAutoGenerators::ParsePostprocess() -{ - bool success = true; - // Read missing dependencies - for (auto& item : this->MocJobsIncluded) { - if (!item->DependsValid) { - std::string content; - std::string error; - if (ReadFile(content, item->SourceFile, &error)) { - this->MocFindDepends(item->SourceFile, content, item->Depends); - item->DependsValid = true; - } else { - std::string emsg = "Could not read file\n "; - emsg += item->SourceFile; - emsg += "\nrequired by moc include \""; - emsg += item->IncludeString; - emsg += "\".\n"; - emsg += error; - this->LogFileError(cmQtAutoGen::MOC, item->Includer, emsg); - success = false; - break; - } - } - } - return success; -} - -/** - * @brief Tests if the file should be ignored for moc scanning - * @return True if the file should be ignored - */ -bool cmQtAutoGenerators::MocSkip(std::string const& absFilename) const -{ - if (this->MocEnabled()) { - // Test if the file name is on the skip list - if (!ListContains(this->MocSkipList, absFilename)) { - return false; - } - } - return true; -} - -/** - * @brief Tests if the C++ content requires moc processing - * @return True if moc is required - */ -bool cmQtAutoGenerators::MocRequired(std::string const& contentText, - std::string* macroName) -{ - for (KeyRegExp& filter : this->MocMacroFilters) { - // Run a simple find string operation before the expensive - // regular expression check - if (contentText.find(filter.Key) != std::string::npos) { - if (filter.RegExp.find(contentText)) { - // Return macro name on demand - if (macroName != nullptr) { - *macroName = filter.Key; - } - return true; - } - } - } - return false; -} - -std::string cmQtAutoGenerators::MocStringMacros() const -{ - std::string res; - const auto itB = this->MocMacroFilters.cbegin(); - const auto itE = this->MocMacroFilters.cend(); - const auto itL = itE - 1; - auto itC = itB; - for (; itC != itE; ++itC) { - // Separator - if (itC != itB) { - if (itC != itL) { - res += ", "; - } else { - res += " or "; - } - } - // Key - res += itC->Key; - } - return res; -} - -std::string cmQtAutoGenerators::MocStringHeaders( - std::string const& fileBase) const -{ - std::string res = fileBase; - res += ".{"; - res += cmJoin(this->HeaderExtensions, ","); - res += "}"; - return res; -} - -std::string cmQtAutoGenerators::MocFindIncludedHeader( - std::string const& sourcePath, std::string const& includeBase) const -{ - std::string header; - // Search in vicinity of the source - if (!this->FindHeader(header, sourcePath + includeBase)) { - // Search in include directories - for (std::string const& path : this->MocIncludePaths) { - std::string fullPath = path; - fullPath.push_back('/'); - fullPath += includeBase; - if (this->FindHeader(header, fullPath)) { - break; - } - } - } - // Sanitize - if (!header.empty()) { - header = cmSystemTools::GetRealPath(header); - } - return header; -} - -bool cmQtAutoGenerators::MocFindIncludedFile( - std::string& absFile, std::string const& sourcePath, - std::string const& includeString) const -{ - bool success = false; - // Search in vicinity of the source - { - std::string testPath = sourcePath; - testPath += includeString; - if (cmSystemTools::FileExists(testPath.c_str())) { - absFile = cmSystemTools::GetRealPath(testPath); - success = true; - } - } - // Search in include directories - if (!success) { - for (std::string const& path : this->MocIncludePaths) { - std::string fullPath = path; - fullPath.push_back('/'); - fullPath += includeString; - if (cmSystemTools::FileExists(fullPath.c_str())) { - absFile = cmSystemTools::GetRealPath(fullPath); - success = true; - break; - } - } - } - return success; -} - -bool cmQtAutoGenerators::MocDependFilterPush(std::string const& key, - std::string const& regExp) -{ - std::string error; - if (!key.empty()) { - if (!regExp.empty()) { - KeyRegExp filter; - filter.Key = key; - if (filter.RegExp.compile(regExp)) { - this->MocDependFilters.push_back(std::move(filter)); - } else { - error = "Regular expression compiling failed"; - } - } else { - error = "Regular expression is empty"; - } - } else { - error = "Key is empty"; - } - if (!error.empty()) { - std::string emsg = "AUTOMOC_DEPEND_FILTERS: "; - emsg += error; - emsg += "\n"; - emsg += " Key: "; - emsg += cmQtAutoGen::Quoted(key); - emsg += "\n"; - emsg += " RegExp: "; - emsg += cmQtAutoGen::Quoted(regExp); - emsg += "\n"; - this->LogError(cmQtAutoGen::MOC, emsg); - return false; - } - return true; -} - -void cmQtAutoGenerators::MocFindDepends(std::string const& absFilename, - std::string const& contentText, - std::set& depends) -{ - if (this->MocDependFilters.empty() && contentText.empty()) { - return; - } - - std::vector matches; - for (KeyRegExp& filter : this->MocDependFilters) { - // Run a simple find string check - if (contentText.find(filter.Key) != std::string::npos) { - // Run the expensive regular expression check loop - const char* contentChars = contentText.c_str(); - while (filter.RegExp.find(contentChars)) { - std::string match = filter.RegExp.match(1); - if (!match.empty()) { - matches.emplace_back(std::move(match)); - } - contentChars += filter.RegExp.end(); - } - } - } - - if (!matches.empty()) { - std::string const sourcePath = SubDirPrefix(absFilename); - for (std::string const& match : matches) { - // Find the dependency file - std::string incFile; - if (this->MocFindIncludedFile(incFile, sourcePath, match)) { - depends.insert(incFile); - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, "Found dependency:\n " + - cmQtAutoGen::Quoted(absFilename) + "\n " + - cmQtAutoGen::Quoted(incFile)); - } - } else { - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, - "Could not find dependency file " + - cmQtAutoGen::Quoted(match)); - } - } - } -} - -/** - * @return True on success - */ -bool cmQtAutoGenerators::MocParseSourceContent(std::string const& absFilename, - std::string const& contentText) -{ - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); - } - - auto AddJob = [this, &absFilename](std::string const& sourceFile, - std::string const& includeString, - std::string const* content) { - auto job = cm::make_unique(); - job->SourceFile = sourceFile; - job->BuildFileRel = this->AutogenIncludeDir; - job->BuildFileRel += includeString; - job->Includer = absFilename; - job->IncludeString = includeString; - job->DependsValid = (content != nullptr); - if (job->DependsValid) { - this->MocFindDepends(sourceFile, *content, job->Depends); - } - this->MocJobsIncluded.push_back(std::move(job)); - }; - - struct MocInc - { - std::string Inc; // full include string - std::string Dir; // include string directory - std::string Base; // include string file base - }; - - // Extract moc includes from file - std::vector mocIncsUsc; - std::vector mocIncsDot; - { - const char* contentChars = contentText.c_str(); - if (strstr(contentChars, "moc") != nullptr) { - while (this->MocRegExpInclude.find(contentChars)) { - std::string incString = this->MocRegExpInclude.match(1); - std::string incDir(SubDirPrefix(incString)); - std::string incBase = - cmSystemTools::GetFilenameWithoutLastExtension(incString); - if (cmHasLiteralPrefix(incBase, "moc_")) { - // moc_.cxx - // Remove the moc_ part from the base name - mocIncsUsc.push_back(MocInc{ std::move(incString), std::move(incDir), - incBase.substr(4) }); - } else { - // .moc - mocIncsDot.push_back(MocInc{ std::move(incString), std::move(incDir), - std::move(incBase) }); - } - // Forward content pointer - contentChars += this->MocRegExpInclude.end(); - } - } - } - - std::string selfMacroName; - const bool selfRequiresMoc = this->MocRequired(contentText, &selfMacroName); - - // Check if there is anything to do - if (!selfRequiresMoc && mocIncsUsc.empty() && mocIncsDot.empty()) { - return true; - } - - // Scan file variables - std::string const scanFileDir = SubDirPrefix(absFilename); - std::string const scanFileBase = - cmSystemTools::GetFilenameWithoutLastExtension(absFilename); - // Relaxed mode variables - bool ownDotMocIncluded = false; - std::string ownMocUscInclude; - std::string ownMocUscHeader; - - // Process moc_.cxx includes - for (const MocInc& mocInc : mocIncsUsc) { - std::string const header = - this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base); - if (!header.empty()) { - // Check if header is skipped - if (this->MocSkip(header)) { - continue; - } - // Register moc job - AddJob(header, mocInc.Inc, nullptr); - // Store meta information for relaxed mode - if (this->MocRelaxedMode && (mocInc.Base == scanFileBase)) { - ownMocUscInclude = mocInc.Inc; - ownMocUscHeader = header; - } - } else { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", but could not find the header "; - emsg += cmQtAutoGen::Quoted(this->MocStringHeaders(mocInc.Base)); - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - - // Process .moc includes - for (const MocInc& mocInc : mocIncsDot) { - const bool ownMoc = (mocInc.Base == scanFileBase); - if (this->MocRelaxedMode) { - // Relaxed mode - if (selfRequiresMoc && ownMoc) { - // Add self - AddJob(absFilename, mocInc.Inc, &contentText); - ownDotMocIncluded = true; - } else { - // In relaxed mode try to find a header instead but issue a warning. - // This is for KDE4 compatibility - std::string const header = - this->MocFindIncludedHeader(scanFileDir, mocInc.Dir + mocInc.Base); - if (!header.empty()) { - // Check if header is skipped - if (this->MocSkip(header)) { - continue; - } - // Register moc job - AddJob(header, mocInc.Inc, nullptr); - if (!selfRequiresMoc) { - if (ownMoc) { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", but does not contain a "; - emsg += this->MocStringMacros(); - emsg += " macro.\nRunning moc on\n "; - emsg += cmQtAutoGen::Quoted(header); - emsg += "!\nBetter include "; - emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += " for a compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - } else { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += " instead of "; - emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += ".\nRunning moc on\n "; - emsg += cmQtAutoGen::Quoted(header); - emsg += "!\nBetter include "; - emsg += cmQtAutoGen::Quoted("moc_" + mocInc.Base + ".cpp"); - emsg += " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - } - } - } else { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", which seems to be the moc file from a different " - "source file. CMake also could not find a matching " - "header."; - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - } else { - // Strict mode - if (ownMoc) { - // Include self - AddJob(absFilename, mocInc.Inc, &contentText); - ownDotMocIncluded = true; - // Accept but issue a warning if moc isn't required - if (!selfRequiresMoc) { - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", but does not contain a "; - emsg += this->MocStringMacros(); - emsg += " macro."; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - } - } else { - // Don't allow .moc include other than self in strict mode - std::string emsg = "The file includes the moc file "; - emsg += cmQtAutoGen::Quoted(mocInc.Inc); - emsg += ", which seems to be the moc file from a different " - "source file.\nThis is not supported. Include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += " to run moc on this source file."; - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - } - - if (selfRequiresMoc && !ownDotMocIncluded) { - // In this case, check whether the scanned file itself contains a Q_OBJECT. - // If this is the case, the moc_foo.cpp should probably be generated from - // foo.cpp instead of foo.h, because otherwise it won't build. - // But warn, since this is not how it is supposed to be used. - if (this->MocRelaxedMode && !ownMocUscInclude.empty()) { - // This is for KDE4 compatibility: - std::string emsg = "The file contains a "; - emsg += selfMacroName; - emsg += " macro, but does not include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += ". Instead it includes "; - emsg += cmQtAutoGen::Quoted(ownMocUscInclude); - emsg += ".\nRunning moc on\n "; - emsg += cmQtAutoGen::Quoted(absFilename); - emsg += "!\nBetter include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, emsg); - - // Remove own header job - { - auto itC = this->MocJobsIncluded.begin(); - auto itE = this->MocJobsIncluded.end(); - for (; itC != itE; ++itC) { - if ((*itC)->SourceFile == ownMocUscHeader) { - if ((*itC)->IncludeString == ownMocUscInclude) { - this->MocJobsIncluded.erase(itC); - break; - } - } - } - } - // Add own source job - AddJob(absFilename, ownMocUscInclude, &contentText); - } else { - // Otherwise always error out since it will not compile: - std::string emsg = "The file contains a "; - emsg += selfMacroName; - emsg += " macro, but does not include "; - emsg += cmQtAutoGen::Quoted(scanFileBase + ".moc"); - emsg += "!\nConsider to\n - add #include \""; - emsg += scanFileBase; - emsg += ".moc\"\n - enable SKIP_AUTOMOC for this file"; - this->LogFileError(cmQtAutoGen::MOC, absFilename, emsg); - return false; - } - } - return true; -} - -void cmQtAutoGenerators::MocParseHeaderContent(std::string const& absFilename, - std::string const& contentText) -{ - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); - } - - auto const fit = - std::find_if(this->MocJobsIncluded.cbegin(), this->MocJobsIncluded.cend(), - [&absFilename](std::unique_ptr const& job) { - return job->SourceFile == absFilename; - }); - if (fit == this->MocJobsIncluded.cend()) { - if (this->MocRequired(contentText)) { - auto job = cm::make_unique(); - job->SourceFile = absFilename; - { - std::string& bld = job->BuildFileRel; - bld = this->FilePathChecksum.getPart(absFilename); - bld += '/'; - bld += "moc_"; - bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename); - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - bld += this->ConfigSuffix; - } - bld += ".cpp"; - } - this->MocFindDepends(absFilename, contentText, job->Depends); - this->MocJobsAuto.push_back(std::move(job)); - } - } -} - -bool cmQtAutoGenerators::MocGenerateAll() -{ - if (!this->MocEnabled()) { - return true; - } - - // Look for name collisions in included moc files - { - bool collision = false; - std::map> collisions; - for (auto const& job : this->MocJobsIncluded) { - auto& list = collisions[job->IncludeString]; - if (!list.empty()) { - collision = true; - } - list.push_back(job.get()); - } - if (collision) { - std::string emsg = - "Included moc files with the same name will be " - "generated from different sources.\n" - "Consider to\n" - " - not include the \"moc_.cpp\" file\n" - " - add a directory prefix to a \".moc\" include " - "(e.g \"sub/.moc\")\n" - " - rename the source file(s)\n" - "Include conflicts\n" - "-----------------\n"; - const auto& colls = collisions; - for (auto const& coll : colls) { - if (coll.second.size() > 1) { - emsg += cmQtAutoGen::Quoted(coll.first); - emsg += " included in\n"; - for (const MocJobIncluded* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->Includer); - emsg += "\n"; - } - emsg += "would be generated from\n"; - for (const MocJobIncluded* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->SourceFile); - emsg += "\n"; - } - } - } - this->LogError(cmQtAutoGen::MOC, emsg); - return false; - } - } - - // (Re)generate moc_predefs.h on demand - if (!this->MocPredefsCmd.empty()) { - if (this->MocSettingsChanged || - !cmSystemTools::FileExists(this->MocPredefsFileAbs)) { - if (this->Verbose) { - this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel); - } - - std::string output; - { - // Compose command - std::vector cmd = this->MocPredefsCmd; - // Add includes - cmd.insert(cmd.end(), this->MocIncludes.begin(), - this->MocIncludes.end()); - // Add definitions - for (std::string const& def : this->MocDefinitions) { - cmd.push_back("-D" + def); - } - // Execute command - if (!this->RunCommand(cmd, output)) { - this->LogCommandError(cmQtAutoGen::MOC, - "moc_predefs generation failed", cmd, output); - return false; - } - } - - // (Re)write predefs file only on demand - if (this->FileDiffers(this->MocPredefsFileAbs, output)) { - if (this->FileWrite(cmQtAutoGen::MOC, this->MocPredefsFileAbs, - output)) { - this->MocPredefsChanged = true; - } else { - this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs, - "moc_predefs file writing failed"); - return false; - } - } else { - // Touch to update the time stamp - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, - "Touching moc_predefs " + this->MocPredefsFileRel); - } - cmSystemTools::Touch(this->MocPredefsFileAbs, false); - } - } - - // Add moc_predefs.h to moc file dependencies - for (auto const& item : this->MocJobsIncluded) { - item->Depends.insert(this->MocPredefsFileAbs); - } - for (auto const& item : this->MocJobsAuto) { - item->Depends.insert(this->MocPredefsFileAbs); - } - } - - // Generate moc files that are included by source files. - for (auto const& item : this->MocJobsIncluded) { - if (!this->MocGenerateFile(*item)) { - return false; - } - } - // Generate moc files that are _not_ included by source files. - bool autoNameGenerated = false; - for (auto const& item : this->MocJobsAuto) { - if (!this->MocGenerateFile(*item, &autoNameGenerated)) { - return false; - } - } - - // Compose mocs compilation file content - { - std::string mocs = - "// This file is autogenerated. Changes will be overwritten.\n"; - if (this->MocJobsAuto.empty()) { - // Placeholder content - mocs += - "// No files found that require moc or the moc files are included\n"; - mocs += "enum some_compilers { need_more_than_nothing };\n"; - } else { - // Valid content - for (const auto& item : this->MocJobsAuto) { - mocs += "#include \""; - mocs += item->BuildFileRel; - mocs += "\"\n"; - } - } - - if (this->FileDiffers(this->MocCompFileAbs, mocs)) { - // Actually write mocs compilation file - if (this->Verbose) { - this->LogBold("Generating MOC compilation " + this->MocCompFileRel); - } - if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, mocs)) { - this->LogFileError(cmQtAutoGen::MOC, this->MocCompFileAbs, - "mocs compilation file writing failed"); - return false; - } - } else if (autoNameGenerated) { - // Only touch mocs compilation file - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, - "Touching mocs compilation " + this->MocCompFileRel); - } - cmSystemTools::Touch(this->MocCompFileAbs, false); - } - } - - return true; -} - -/** - * @return True on success - */ -bool cmQtAutoGenerators::MocGenerateFile(const MocJobAuto& mocJob, - bool* generated) -{ - bool success = true; - - std::string const mocFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, mocJob.BuildFileRel); - - bool generate = false; - std::string generateReason; - if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from its source file "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because it doesn't exist"; - } - generate = true; - } - if (!generate && this->MocSettingsChanged) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because the MOC settings changed"; - } - generate = true; - } - if (!generate && this->MocPredefsChanged) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because moc_predefs.h changed"; - } - generate = true; - } - if (!generate) { - std::string error; - if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " because it's older than its source file "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - } - generate = true; - } else { - if (!error.empty()) { - this->LogError(cmQtAutoGen::MOC, error); - success = false; - } - } - } - if (success && !generate) { - // Test if a dependency file is newer - std::string error; - for (std::string const& depFile : mocJob.Depends) { - if (FileIsOlderThan(mocFileAbs, depFile, &error)) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(mocFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(mocJob.SourceFile); - generateReason += " because it is older than "; - generateReason += cmQtAutoGen::Quoted(depFile); - } - generate = true; - break; - } - if (!error.empty()) { - this->LogError(cmQtAutoGen::MOC, error); - success = false; - break; - } - } - } - - if (generate) { - // Log - if (this->Verbose) { - this->LogBold("Generating MOC source " + mocJob.BuildFileRel); - this->LogInfo(cmQtAutoGen::MOC, generateReason); - } - - // Make sure the parent directory exists - if (this->MakeParentDirectory(cmQtAutoGen::MOC, mocFileAbs)) { - // Compose moc command - std::vector cmd; - cmd.push_back(this->MocExecutable); - // Add options - cmd.insert(cmd.end(), this->MocAllOptions.begin(), - this->MocAllOptions.end()); - // Add predefs include - if (!this->MocPredefsFileAbs.empty()) { - cmd.push_back("--include"); - cmd.push_back(this->MocPredefsFileAbs); - } - cmd.push_back("-o"); - cmd.push_back(mocFileAbs); - cmd.push_back(mocJob.SourceFile); - - // Execute moc command - std::string output; - if (this->RunCommand(cmd, output)) { - // Success - if (generated != nullptr) { - *generated = true; - } - } else { - // Moc command failed - { - std::string emsg = "moc failed for\n "; - emsg += cmQtAutoGen::Quoted(mocJob.SourceFile); - this->LogCommandError(cmQtAutoGen::MOC, emsg, cmd, output); - } - cmSystemTools::RemoveFile(mocFileAbs); - success = false; - } - } else { - // Parent directory creation failed - success = false; - } - } - return success; -} - -/** - * @brief Tests if the file name is in the skip list - */ -bool cmQtAutoGenerators::UicSkip(std::string const& absFilename) const -{ - if (this->UicEnabled()) { - // Test if the file name is on the skip list - if (!ListContains(this->UicSkipList, absFilename)) { - return false; - } - } - return true; -} - -bool cmQtAutoGenerators::UicParseContent(std::string const& absFilename, - std::string const& contentText) -{ - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename); - } - - std::vector includes; - // Extracte includes - { - const char* contentChars = contentText.c_str(); - if (strstr(contentChars, "ui_") != nullptr) { - while (this->UicRegExpInclude.find(contentChars)) { - includes.push_back(this->UicRegExpInclude.match(1)); - contentChars += this->UicRegExpInclude.end(); - } - } - } - - for (std::string const& includeString : includes) { - std::string uiInputFile; - if (!UicFindIncludedFile(uiInputFile, absFilename, includeString)) { - return false; - } - // Check if this file should be skipped - if (this->UicSkip(uiInputFile)) { - continue; - } - // Check if the job already exists - bool jobExists = false; - for (const auto& job : this->UicJobs) { - if ((job->SourceFile == uiInputFile) && - (job->IncludeString == includeString)) { - jobExists = true; - break; - } - } - if (!jobExists) { - auto job = cm::make_unique(); - job->SourceFile = uiInputFile; - job->BuildFileRel = this->AutogenIncludeDir; - job->BuildFileRel += includeString; - job->Includer = absFilename; - job->IncludeString = includeString; - this->UicJobs.push_back(std::move(job)); - } - } - - return true; -} - -bool cmQtAutoGenerators::UicFindIncludedFile(std::string& absFile, - std::string const& sourceFile, - std::string const& includeString) -{ - bool success = false; - std::string searchFile = - cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3); - searchFile += ".ui"; - // Collect search paths list - std::vector testFiles; - { - std::string const searchPath = SubDirPrefix(includeString); - - std::string searchFileFull; - if (!searchPath.empty()) { - searchFileFull = searchPath; - searchFileFull += searchFile; - } - // Vicinity of the source - { - std::string const sourcePath = SubDirPrefix(sourceFile); - testFiles.push_back(sourcePath + searchFile); - if (!searchPath.empty()) { - testFiles.push_back(sourcePath + searchFileFull); - } - } - // AUTOUIC search paths - if (!this->UicSearchPaths.empty()) { - for (std::string const& sPath : this->UicSearchPaths) { - testFiles.push_back((sPath + "/").append(searchFile)); - } - if (!searchPath.empty()) { - for (std::string const& sPath : this->UicSearchPaths) { - testFiles.push_back((sPath + "/").append(searchFileFull)); - } - } - } - } - - // Search for the .ui file! - for (std::string const& testFile : testFiles) { - if (cmSystemTools::FileExists(testFile.c_str())) { - absFile = cmSystemTools::GetRealPath(testFile); - success = true; - break; - } - } - - // Log error - if (!success) { - std::string emsg = "Could not find "; - emsg += cmQtAutoGen::Quoted(searchFile); - emsg += " in\n"; - for (std::string const& testFile : testFiles) { - emsg += " "; - emsg += cmQtAutoGen::Quoted(testFile); - emsg += "\n"; - } - this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg); - } - - return success; -} - -bool cmQtAutoGenerators::UicGenerateAll() -{ - if (!this->UicEnabled()) { - return true; - } - - // Look for name collisions in included uic files - { - bool collision = false; - std::map> collisions; - for (auto const& job : this->UicJobs) { - auto& list = collisions[job->IncludeString]; - if (!list.empty()) { - collision = true; - } - list.push_back(job.get()); - } - if (collision) { - std::string emsg = - "Included uic files with the same name will be " - "generated from different sources.\n" - "Consider to\n" - " - add a directory prefix to a \"ui_.h\" include " - "(e.g \"sub/ui_.h\")\n" - " - rename the .ui file(s) and adjust the \"ui_.h\" " - "include(s)\n" - "Include conflicts\n" - "-----------------\n"; - const auto& colls = collisions; - for (auto const& coll : colls) { - if (coll.second.size() > 1) { - emsg += cmQtAutoGen::Quoted(coll.first); - emsg += " included in\n"; - for (const UicJob* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->Includer); - emsg += "\n"; - } - emsg += "would be generated from\n"; - for (const UicJob* job : coll.second) { - emsg += " - "; - emsg += cmQtAutoGen::Quoted(job->SourceFile); - emsg += "\n"; - } - } - } - this->LogError(cmQtAutoGen::UIC, emsg); - return false; - } - } - - // Generate ui header files - for (const auto& item : this->UicJobs) { - if (!this->UicGenerateFile(*item)) { - return false; - } - } - - return true; -} - -/** - * @return True on success - */ -bool cmQtAutoGenerators::UicGenerateFile(const UicJob& uicJob) -{ - bool success = true; - - std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, uicJob.BuildFileRel); - - bool generate = false; - std::string generateReason; - if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(uicFileAbs); - generateReason += " from its source file "; - generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); - generateReason += " because it doesn't exist"; - } - generate = true; - } - if (!generate && this->UicSettingsChanged) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(uicFileAbs); - generateReason += " from "; - generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); - generateReason += " because the UIC settings changed"; - } - generate = true; - } - if (!generate) { - std::string error; - if (FileIsOlderThan(uicFileAbs, uicJob.SourceFile, &error)) { - if (this->Verbose) { - generateReason = "Generating "; - generateReason += cmQtAutoGen::Quoted(uicFileAbs); - generateReason += " because it's older than its source file "; - generateReason += cmQtAutoGen::Quoted(uicJob.SourceFile); - } - generate = true; - } else { - if (!error.empty()) { - this->LogError(cmQtAutoGen::UIC, error); - success = false; - } - } - } - if (generate) { - // Log - if (this->Verbose) { - this->LogBold("Generating UIC header " + uicJob.BuildFileRel); - this->LogInfo(cmQtAutoGen::UIC, generateReason); - } - - // Make sure the parent directory exists - if (this->MakeParentDirectory(cmQtAutoGen::UIC, uicFileAbs)) { - // Compose uic command - std::vector cmd; - cmd.push_back(this->UicExecutable); - { - std::vector allOpts = this->UicTargetOptions; - auto optionIt = this->UicOptions.find(uicJob.SourceFile); - if (optionIt != this->UicOptions.end()) { - cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second, - (this->QtMajorVersion == "5")); - } - cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); - } - cmd.push_back("-o"); - cmd.push_back(uicFileAbs); - cmd.push_back(uicJob.SourceFile); - - std::string output; - if (this->RunCommand(cmd, output)) { - // Success - } else { - // Command failed - { - std::string emsg = "uic failed for\n "; - emsg += cmQtAutoGen::Quoted(uicJob.SourceFile); - emsg += "\nincluded by\n "; - emsg += cmQtAutoGen::Quoted(uicJob.Includer); - this->LogCommandError(cmQtAutoGen::UIC, emsg, cmd, output); - } - cmSystemTools::RemoveFile(uicFileAbs); - success = false; - } - } else { - // Parent directory creation failed - success = false; - } - } - return success; -} - -void cmQtAutoGenerators::LogBold(std::string const& message) const -{ - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, this->ColorOutput); -} - -void cmQtAutoGenerators::LogInfo(cmQtAutoGen::Generator genType, - std::string const& message) const -{ - std::string msg = cmQtAutoGen::GeneratorName(genType); - msg += ": "; - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - cmSystemTools::Stdout(msg.c_str(), msg.size()); -} - -void cmQtAutoGenerators::LogWarning(cmQtAutoGen::Generator genType, - std::string const& message) const -{ - std::string msg = cmQtAutoGen::GeneratorName(genType); - msg += " warning:"; - if (message.find('\n') == std::string::npos) { - // Single line message - msg.push_back(' '); - } else { - // Multi line message - msg.push_back('\n'); - } - // Message - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - cmSystemTools::Stdout(msg.c_str(), msg.size()); -} - -void cmQtAutoGenerators::LogFileWarning(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const -{ - std::string msg = " "; - msg += cmQtAutoGen::Quoted(filename); - msg.push_back('\n'); - // Message - msg += message; - this->LogWarning(genType, msg); -} - -void cmQtAutoGenerators::LogError(cmQtAutoGen::Generator genType, - std::string const& message) const -{ - std::string msg; - msg.push_back('\n'); - msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error"); - // Message - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - cmSystemTools::Stderr(msg.c_str(), msg.size()); -} - -void cmQtAutoGenerators::LogFileError(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const -{ - std::string emsg = " "; - emsg += cmQtAutoGen::Quoted(filename); - emsg += '\n'; - // Message - emsg += message; - this->LogError(genType, emsg); -} - -void cmQtAutoGenerators::LogCommandError( - cmQtAutoGen::Generator genType, std::string const& message, - std::vector const& command, std::string const& output) const -{ - std::string msg; - msg.push_back('\n'); - msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error"); - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - msg += HeadLine("Command"); - msg += QuotedCommand(command); - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - msg += HeadLine("Output"); - msg += output; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - cmSystemTools::Stderr(msg.c_str(), msg.size()); -} - -/** - * @brief Generates the parent directory of the given file on demand - * @return True on success - */ -bool cmQtAutoGenerators::MakeParentDirectory(cmQtAutoGen::Generator genType, - std::string const& filename) const -{ - bool success = true; - std::string const dirName = cmSystemTools::GetFilenamePath(filename); - if (!dirName.empty()) { - if (!cmSystemTools::MakeDirectory(dirName)) { - this->LogFileError(genType, filename, - "Could not create parent directory"); - success = false; - } - } - return success; -} - -bool cmQtAutoGenerators::FileDiffers(std::string const& filename, - std::string const& content) -{ - bool differs = true; - { - std::string oldContents; - if (ReadFile(oldContents, filename)) { - differs = (oldContents != content); - } - } - return differs; -} - -bool cmQtAutoGenerators::FileWrite(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& content) -{ - std::string error; - // Make sure the parent directory exists - if (this->MakeParentDirectory(genType, filename)) { - cmsys::ofstream outfile; - outfile.open(filename.c_str(), - (std::ios::out | std::ios::binary | std::ios::trunc)); - if (outfile) { - outfile << content; - // Check for write errors - if (!outfile.good()) { - error = "File writing failed"; - } - } else { - error = "Opening file for writing failed"; - } - } - if (!error.empty()) { - this->LogFileError(genType, filename, error); - return false; - } - return true; -} - -/** - * @brief Runs a command and returns true on success - * @return True on success - */ -bool cmQtAutoGenerators::RunCommand(std::vector const& command, - std::string& output) const -{ - // Log command - if (this->Verbose) { - std::string qcmd = QuotedCommand(command); - qcmd.push_back('\n'); - cmSystemTools::Stdout(qcmd.c_str(), qcmd.size()); - } - // Execute command - int retVal = 0; - bool res = cmSystemTools::RunSingleCommand( - command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE); - return (res && (retVal == 0)); -} - -/** - * @brief Tries to find the header file to the given file base path by - * appending different header extensions - * @return True on success - */ -bool cmQtAutoGenerators::FindHeader(std::string& header, - std::string const& testBasePath) const -{ - for (std::string const& ext : this->HeaderExtensions) { - std::string testFilePath(testBasePath); - testFilePath.push_back('.'); - testFilePath += ext; - if (cmSystemTools::FileExists(testFilePath.c_str())) { - header = testFilePath; - return true; - } - } - return false; -} diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h deleted file mode 100644 index b6e28b3..0000000 --- a/Source/cmQtAutoGenerators.h +++ /dev/null @@ -1,228 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGenerators_h -#define cmQtAutoGenerators_h - -#include "cmConfigure.h" // IWYU pragma: keep - -#include "cmFilePathChecksum.h" -#include "cmQtAutoGen.h" -#include "cmsys/RegularExpression.hxx" - -#include -#include // IWYU pragma: keep -#include -#include -#include - -class cmMakefile; - -class cmQtAutoGenerators -{ - CM_DISABLE_COPY(cmQtAutoGenerators) -public: - cmQtAutoGenerators(); - bool Run(std::string const& targetDirectory, std::string const& config); - -private: - // -- Types - - /// @brief Search key plus regular expression pair - struct KeyRegExp - { - KeyRegExp() = default; - - KeyRegExp(const char* key, const char* regExp) - : Key(key) - , RegExp(regExp) - { - } - - KeyRegExp(std::string const& key, std::string const& regExp) - : Key(key) - , RegExp(regExp) - { - } - - std::string Key; - cmsys::RegularExpression RegExp; - }; - - /// @brief Source file job - struct SourceJob - { - bool Moc = false; - bool Uic = false; - }; - - /// @brief MOC job - struct MocJobAuto - { - std::string SourceFile; - std::string BuildFileRel; - std::set Depends; - }; - - /// @brief MOC job - struct MocJobIncluded : MocJobAuto - { - bool DependsValid = false; - std::string Includer; - std::string IncludeString; - }; - - /// @brief UIC job - struct UicJob - { - std::string SourceFile; - std::string BuildFileRel; - std::string Includer; - std::string IncludeString; - }; - - // -- Initialization - bool InitInfoFile(cmMakefile* makefile, std::string const& targetDirectory, - std::string const& config); - - // -- Settings file - void SettingsFileRead(cmMakefile* makefile); - bool SettingsFileWrite(); - bool SettingsChanged() const - { - return (this->MocSettingsChanged || this->UicSettingsChanged); - } - - // -- Central processing - bool Process(); - - // -- Source parsing - bool ParseSourceFile(std::string const& absFilename, const SourceJob& job); - bool ParseHeaderFile(std::string const& absFilename, const SourceJob& job); - bool ParsePostprocess(); - - // -- Moc - bool MocEnabled() const { return !this->MocExecutable.empty(); } - bool MocSkip(std::string const& absFilename) const; - bool MocRequired(std::string const& contentText, - std::string* macroName = nullptr); - // Moc strings - std::string MocStringMacros() const; - std::string MocStringHeaders(std::string const& fileBase) const; - std::string MocFindIncludedHeader(std::string const& sourcePath, - std::string const& includeBase) const; - bool MocFindIncludedFile(std::string& absFile, std::string const& sourceFile, - std::string const& includeString) const; - // Moc depends - bool MocDependFilterPush(std::string const& key, std::string const& regExp); - void MocFindDepends(std::string const& absFilename, - std::string const& contentText, - std::set& depends); - // Moc - bool MocParseSourceContent(std::string const& absFilename, - std::string const& contentText); - void MocParseHeaderContent(std::string const& absFilename, - std::string const& contentText); - - bool MocGenerateAll(); - bool MocGenerateFile(const MocJobAuto& mocJob, bool* generated = nullptr); - - // -- Uic - bool UicEnabled() const { return !this->UicExecutable.empty(); } - bool UicSkip(std::string const& absFilename) const; - bool UicParseContent(std::string const& fileName, - std::string const& contentText); - bool UicFindIncludedFile(std::string& absFile, std::string const& sourceFile, - std::string const& includeString); - bool UicGenerateAll(); - bool UicGenerateFile(const UicJob& uicJob); - - // -- Log info - void LogBold(std::string const& message) const; - void LogInfo(cmQtAutoGen::Generator genType, - std::string const& message) const; - // -- Log warning - void LogWarning(cmQtAutoGen::Generator genType, - std::string const& message) const; - void LogFileWarning(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const; - // -- Log error - void LogError(cmQtAutoGen::Generator genType, - std::string const& message) const; - void LogFileError(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const; - void LogCommandError(cmQtAutoGen::Generator genType, - std::string const& message, - std::vector const& command, - std::string const& output) const; - - // -- Utility - bool MakeParentDirectory(cmQtAutoGen::Generator genType, - std::string const& filename) const; - bool FileDiffers(std::string const& filename, std::string const& content); - bool FileWrite(cmQtAutoGen::Generator genType, std::string const& filename, - std::string const& content); - bool FindHeader(std::string& header, std::string const& testBasePath) const; - bool RunCommand(std::vector const& command, - std::string& output) const; - - // -- Meta - std::string InfoFile; - std::string ConfigSuffix; - cmQtAutoGen::MultiConfig MultiConfig; - // -- Settings - bool IncludeProjectDirsBefore; - bool Verbose; - bool ColorOutput; - std::string SettingsFile; - std::string SettingsStringMoc; - std::string SettingsStringUic; - // -- Directories - std::string ProjectSourceDir; - std::string ProjectBinaryDir; - std::string CurrentSourceDir; - std::string CurrentBinaryDir; - std::string AutogenBuildDir; - std::string AutogenIncludeDir; - // -- Qt environment - std::string QtMajorVersion; - std::string QtMinorVersion; - std::string MocExecutable; - std::string UicExecutable; - // -- File lists - std::map HeaderJobs; - std::map SourceJobs; - std::vector HeaderExtensions; - cmFilePathChecksum FilePathChecksum; - // -- Moc - bool MocSettingsChanged; - bool MocPredefsChanged; - bool MocRelaxedMode; - std::string MocCompFileRel; - std::string MocCompFileAbs; - std::string MocPredefsFileRel; - std::string MocPredefsFileAbs; - std::vector MocSkipList; - std::vector MocIncludePaths; - std::vector MocIncludes; - std::vector MocDefinitions; - std::vector MocOptions; - std::vector MocAllOptions; - std::vector MocPredefsCmd; - std::vector MocDependFilters; - std::vector MocMacroFilters; - cmsys::RegularExpression MocRegExpInclude; - std::vector> MocJobsIncluded; - std::vector> MocJobsAuto; - // -- Uic - bool UicSettingsChanged; - std::vector UicSkipList; - std::vector UicTargetOptions; - std::map> UicOptions; - std::vector UicSearchPaths; - cmsys::RegularExpression UicRegExpInclude; - std::vector> UicJobs; -}; - -#endif diff --git a/Source/cmcmd.cxx b/Source/cmcmd.cxx index a37bc3c..3d9f65a 100644 --- a/Source/cmcmd.cxx +++ b/Source/cmcmd.cxx @@ -6,8 +6,8 @@ #include "cmGlobalGenerator.h" #include "cmLocalGenerator.h" #include "cmMakefile.h" +#include "cmQtAutoGeneratorMocUic.h" #include "cmQtAutoGeneratorRcc.h" -#include "cmQtAutoGenerators.h" #include "cmStateDirectory.h" #include "cmStateSnapshot.h" #include "cmSystemTools.h" @@ -994,7 +994,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector& args) #ifdef CMAKE_BUILD_WITH_CMAKE if ((args[1] == "cmake_autogen") && (args.size() >= 4)) { - cmQtAutoGenerators autoGen; + cmQtAutoGeneratorMocUic autoGen; std::string const& infoDir = args[2]; std::string const& config = args[3]; return autoGen.Run(infoDir, config) ? 0 : 1; -- cgit v0.12 From 75819b8626abf0e64895ef19acd27dbd0fa9255b Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Sat, 18 Nov 2017 11:29:26 +0100 Subject: Autogen: Add and use cmQtAutoGenerator base class Adds the new base class `cmQtAutoGenerator` which contains common variables and methods used by `cmQtAutoGeneratorMocUic` and `cmQtAutoGeneratorRcc`. --- Modules/AutogenInfo.cmake.in | 1 + Source/CMakeLists.txt | 2 + Source/cmQtAutoGenerator.cxx | 320 +++++++++++++++++++++++++ Source/cmQtAutoGenerator.h | 76 ++++++ Source/cmQtAutoGeneratorInitializer.cxx | 13 +- Source/cmQtAutoGeneratorMocUic.cxx | 412 +++++--------------------------- Source/cmQtAutoGeneratorMocUic.h | 40 +--- Source/cmQtAutoGeneratorRcc.cxx | 343 ++------------------------ Source/cmQtAutoGeneratorRcc.h | 40 +--- 9 files changed, 488 insertions(+), 759 deletions(-) create mode 100644 Source/cmQtAutoGenerator.cxx create mode 100644 Source/cmQtAutoGenerator.h diff --git a/Modules/AutogenInfo.cmake.in b/Modules/AutogenInfo.cmake.in index 7d1d6d8..8fa3684 100644 --- a/Modules/AutogenInfo.cmake.in +++ b/Modules/AutogenInfo.cmake.in @@ -9,6 +9,7 @@ set(AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE "@CMAKE_INCLUDE_DIRECTORIES_PROJ set(AM_BUILD_DIR @_build_dir@) set(AM_SOURCES @_sources@) set(AM_HEADERS @_headers@) +set(AM_SETTINGS_FILE @_settings_file@) # Qt environment set(AM_QT_VERSION_MAJOR @_qt_version_major@) set(AM_QT_VERSION_MINOR @_qt_version_minor@) diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index cddef68..6b086fa 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -312,6 +312,8 @@ set(SRCS cmQtAutoGen.cxx cmQtAutoGen.h cmQtAutoGenDigest.h + cmQtAutoGenerator.cxx + cmQtAutoGenerator.h cmQtAutoGeneratorInitializer.cxx cmQtAutoGeneratorInitializer.h cmQtAutoGeneratorMocUic.cxx diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx new file mode 100644 index 0000000..52193af --- /dev/null +++ b/Source/cmQtAutoGenerator.cxx @@ -0,0 +1,320 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmQtAutoGen.h" +#include "cmQtAutoGenerator.h" + +#include "cmsys/FStream.hxx" +#include "cmsys/Terminal.h" + +#include "cmAlgorithms.h" +#include "cmGlobalGenerator.h" +#include "cmMakefile.h" +#include "cmStateDirectory.h" +#include "cmStateSnapshot.h" +#include "cmSystemTools.h" +#include "cmake.h" + +// -- Static functions + +static std::string HeadLine(std::string const& title) +{ + std::string head = title; + head += '\n'; + head.append(head.size() - 1, '-'); + head += '\n'; + return head; +} + +static std::string QuotedCommand(std::vector const& command) +{ + std::string res; + for (std::string const& item : command) { + if (!res.empty()) { + res.push_back(' '); + } + std::string const cesc = cmQtAutoGen::Quoted(item); + if (item.empty() || (cesc.size() > (item.size() + 2)) || + (cesc.find(' ') != std::string::npos)) { + res += cesc; + } else { + res += item; + } + } + return res; +} + +// -- Class methods + +cmQtAutoGenerator::cmQtAutoGenerator() + : Verbose(cmSystemTools::HasEnv("VERBOSE")) + , ColorOutput(true) +{ + { + std::string colorEnv; + cmSystemTools::GetEnv("COLOR", colorEnv); + if (!colorEnv.empty()) { + this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str()); + } + } +} + +bool cmQtAutoGenerator::Run(std::string const& infoFile, + std::string const& config) +{ + // Info settings + this->InfoFile = infoFile; + cmSystemTools::ConvertToUnixSlashes(this->InfoFile); + this->InfoDir = cmSystemTools::GetFilenamePath(infoFile); + this->InfoConfig = config; + + cmake cm(cmake::RoleScript); + cm.SetHomeOutputDirectory(this->InfoDir); + cm.SetHomeDirectory(this->InfoDir); + cm.GetCurrentSnapshot().SetDefaultDefinitions(); + cmGlobalGenerator gg(&cm); + + cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); + snapshot.GetDirectory().SetCurrentBinary(this->InfoDir); + snapshot.GetDirectory().SetCurrentSource(this->InfoDir); + + auto makefile = cm::make_unique(&gg, snapshot); + gg.SetCurrentMakefile(makefile.get()); + + return this->Process(makefile.get()); +} + +void cmQtAutoGenerator::LogBold(std::string const& message) const +{ + cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | + cmsysTerminal_Color_ForegroundBold, + message.c_str(), true, this->ColorOutput); +} + +void cmQtAutoGenerator::LogInfo(cmQtAutoGen::Generator genType, + std::string const& message) const +{ + std::string msg = cmQtAutoGen::GeneratorName(genType); + msg += ": "; + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + cmSystemTools::Stdout(msg.c_str(), msg.size()); +} + +void cmQtAutoGenerator::LogWarning(cmQtAutoGen::Generator genType, + std::string const& message) const +{ + std::string msg = cmQtAutoGen::GeneratorName(genType); + msg += " warning:"; + if (message.find('\n') == std::string::npos) { + // Single line message + msg.push_back(' '); + } else { + // Multi line message + msg.push_back('\n'); + } + // Message + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + cmSystemTools::Stdout(msg.c_str(), msg.size()); +} + +void cmQtAutoGenerator::LogFileWarning(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const +{ + std::string msg = " "; + msg += cmQtAutoGen::Quoted(filename); + msg.push_back('\n'); + // Message + msg += message; + this->LogWarning(genType, msg); +} + +void cmQtAutoGenerator::LogError(cmQtAutoGen::Generator genType, + std::string const& message) const +{ + std::string msg; + msg.push_back('\n'); + msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error"); + // Message + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + cmSystemTools::Stderr(msg.c_str(), msg.size()); +} + +void cmQtAutoGenerator::LogFileError(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const +{ + std::string emsg = " "; + emsg += cmQtAutoGen::Quoted(filename); + emsg += '\n'; + // Message + emsg += message; + this->LogError(genType, emsg); +} + +void cmQtAutoGenerator::LogCommandError( + cmQtAutoGen::Generator genType, std::string const& message, + std::vector const& command, std::string const& output) const +{ + std::string msg; + msg.push_back('\n'); + msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error"); + msg += message; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + msg += HeadLine("Command"); + msg += QuotedCommand(command); + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + msg += HeadLine("Output"); + msg += output; + if (msg.back() != '\n') { + msg.push_back('\n'); + } + msg.push_back('\n'); + cmSystemTools::Stderr(msg.c_str(), msg.size()); +} + +/** + * @brief Generates the parent directory of the given file on demand + * @return True on success + */ +bool cmQtAutoGenerator::MakeParentDirectory(cmQtAutoGen::Generator genType, + std::string const& filename) const +{ + bool success = true; + std::string const dirName = cmSystemTools::GetFilenamePath(filename); + if (!dirName.empty()) { + if (!cmSystemTools::MakeDirectory(dirName)) { + this->LogFileError(genType, filename, + "Could not create parent directory"); + success = false; + } + } + return success; +} + +/** + * @brief Tests if buildFile is older than sourceFile + * @return True if buildFile is older than sourceFile. + * False may indicate an error. + */ +bool cmQtAutoGenerator::FileIsOlderThan(std::string const& buildFile, + std::string const& sourceFile, + std::string* error) +{ + int result = 0; + if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) { + return (result < 0); + } + if (error != nullptr) { + error->append( + "File modification time comparison failed for the files\n "); + error->append(cmQtAutoGen::Quoted(buildFile)); + error->append("\nand\n "); + error->append(cmQtAutoGen::Quoted(sourceFile)); + } + return false; +} + +bool cmQtAutoGenerator::FileRead(std::string& content, + std::string const& filename, + std::string* error) +{ + bool success = false; + if (cmSystemTools::FileExists(filename)) { + std::size_t const length = cmSystemTools::FileLength(filename); + cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); + if (ifs) { + content.resize(length); + ifs.read(&content.front(), content.size()); + if (ifs) { + success = true; + } else { + content.clear(); + if (error != nullptr) { + error->append("Reading from the file failed."); + } + } + } else if (error != nullptr) { + error->append("Opening the file for reading failed."); + } + } else if (error != nullptr) { + error->append("The file does not exist."); + } + return success; +} + +bool cmQtAutoGenerator::FileWrite(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& content) +{ + std::string error; + // Make sure the parent directory exists + if (this->MakeParentDirectory(genType, filename)) { + cmsys::ofstream outfile; + outfile.open(filename.c_str(), + (std::ios::out | std::ios::binary | std::ios::trunc)); + if (outfile) { + outfile << content; + // Check for write errors + if (!outfile.good()) { + error = "File writing failed"; + } + } else { + error = "Opening file for writing failed"; + } + } + if (!error.empty()) { + this->LogFileError(genType, filename, error); + return false; + } + return true; +} + +bool cmQtAutoGenerator::FileDiffers(std::string const& filename, + std::string const& content) +{ + bool differs = true; + { + std::string oldContents; + if (this->FileRead(oldContents, filename)) { + differs = (oldContents != content); + } + } + return differs; +} + +/** + * @brief Runs a command and returns true on success + * @return True on success + */ +bool cmQtAutoGenerator::RunCommand(std::vector const& command, + std::string& output) const +{ + // Log command + if (this->Verbose) { + std::string qcmd = QuotedCommand(command); + qcmd.push_back('\n'); + cmSystemTools::Stdout(qcmd.c_str(), qcmd.size()); + } + // Execute command + int retVal = 0; + bool res = cmSystemTools::RunSingleCommand( + command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE); + return (res && (retVal == 0)); +} diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h new file mode 100644 index 0000000..285340d --- /dev/null +++ b/Source/cmQtAutoGenerator.h @@ -0,0 +1,76 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cmQtAutoGenerator_h +#define cmQtAutoGenerator_h + +#include "cmConfigure.h" // IWYU pragma: keep + +#include "cmQtAutoGen.h" + +#include +#include + +class cmMakefile; + +class cmQtAutoGenerator +{ + CM_DISABLE_COPY(cmQtAutoGenerator) +public: + cmQtAutoGenerator(); + virtual ~cmQtAutoGenerator() = default; + bool Run(std::string const& infoFile, std::string const& config); + + std::string const& GetInfoFile() const { return InfoFile; } + std::string const& GetInfoDir() const { return InfoDir; } + std::string const& GetInfoConfig() const { return InfoConfig; } + bool GetVerbose() const { return Verbose; } + +protected: + // -- Central processing + virtual bool Process(cmMakefile* makefile) = 0; + + // -- Log info + void LogBold(std::string const& message) const; + void LogInfo(cmQtAutoGen::Generator genType, + std::string const& message) const; + // -- Log warning + void LogWarning(cmQtAutoGen::Generator genType, + std::string const& message) const; + void LogFileWarning(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const; + // -- Log error + void LogError(cmQtAutoGen::Generator genType, + std::string const& message) const; + void LogFileError(cmQtAutoGen::Generator genType, + std::string const& filename, + std::string const& message) const; + void LogCommandError(cmQtAutoGen::Generator genType, + std::string const& message, + std::vector const& command, + std::string const& output) const; + // -- Utility + bool MakeParentDirectory(cmQtAutoGen::Generator genType, + std::string const& filename) const; + bool FileIsOlderThan(std::string const& buildFile, + std::string const& sourceFile, + std::string* error = nullptr); + bool FileRead(std::string& content, std::string const& filename, + std::string* error = nullptr); + bool FileWrite(cmQtAutoGen::Generator genType, std::string const& filename, + std::string const& content); + bool FileDiffers(std::string const& filename, std::string const& content); + bool RunCommand(std::vector const& command, + std::string& output) const; + +private: + // -- Info settings + std::string InfoFile; + std::string InfoDir; + std::string InfoConfig; + // -- Settings + bool Verbose; + bool ColorOutput; +}; + +#endif diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx index 9477bc3..1d18bad 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGeneratorInitializer.cxx @@ -1061,7 +1061,7 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( currentLine.push_back(cmSystemTools::GetCMakeCommand()); currentLine.push_back("-E"); currentLine.push_back("cmake_autogen"); - currentLine.push_back(autogenInfoDir); + currentLine.push_back(autogenInfoDir + "/AutogenInfo.cmake"); currentLine.push_back("$"); commandLines.push_back(std::move(currentLine)); } @@ -1197,12 +1197,19 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( cmQtAutoGen::MultiConfigName(multiConfig)); AddDefinitionEscaped(makefile, "_build_dir", GetAutogenTargetBuildDir(target)); - AddDefinitionEscaped(makefile, "_sources", digest.Sources); - AddDefinitionEscaped(makefile, "_headers", digest.Headers); AddDefinitionEscaped(makefile, "_qt_version_major", digest.QtVersionMajor); AddDefinitionEscaped(makefile, "_qt_version_minor", digest.QtVersionMinor); { if (digest.MocEnabled || digest.UicEnabled) { + { + std::string settingsFile = GetAutogenTargetFilesDir(target); + cmSystemTools::ConvertToUnixSlashes(settingsFile); + settingsFile += "/AutogenOldSettings.cmake"; + AddDefinitionEscaped(makefile, "_settings_file", settingsFile); + } + AddDefinitionEscaped(makefile, "_sources", digest.Sources); + AddDefinitionEscaped(makefile, "_headers", digest.Headers); + SetupAcquireSkipFiles(digest, setup); if (digest.MocEnabled) { SetupAutoTargetMoc(digest, configDefault, configsList, setup); diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx index 36fc090..ea99b1f 100644 --- a/Source/cmQtAutoGeneratorMocUic.cxx +++ b/Source/cmQtAutoGeneratorMocUic.cxx @@ -3,8 +3,6 @@ #include "cmQtAutoGen.h" #include "cmQtAutoGeneratorMocUic.h" -#include "cmsys/FStream.hxx" -#include "cmsys/Terminal.h" #include #include #include @@ -15,12 +13,8 @@ #include "cmAlgorithms.h" #include "cmCryptoHash.h" -#include "cmFilePathChecksum.h" -#include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmOutputConverter.h" -#include "cmStateDirectory.h" -#include "cmStateSnapshot.h" #include "cmSystemTools.h" #include "cmake.h" @@ -35,33 +29,6 @@ static const char* SettingsKeyUic = "AM_UIC_SETTINGS_HASH"; // -- Static functions -static std::string HeadLine(std::string const& title) -{ - std::string head = title; - head += '\n'; - head.append(head.size() - 1, '-'); - head += '\n'; - return head; -} - -static std::string QuotedCommand(std::vector const& command) -{ - std::string res; - for (std::string const& item : command) { - if (!res.empty()) { - res.push_back(' '); - } - std::string const cesc = cmQtAutoGen::Quoted(item); - if (item.empty() || (cesc.size() > (item.size() + 2)) || - (cesc.find(' ') != std::string::npos)) { - res += cesc; - } else { - res += item; - } - } - return res; -} - static std::string SubDirPrefix(std::string const& fileName) { std::string res(cmSystemTools::GetFilenamePath(fileName)); @@ -71,56 +38,6 @@ static std::string SubDirPrefix(std::string const& fileName) return res; } -static bool ReadFile(std::string& content, std::string const& filename, - std::string* error = nullptr) -{ - bool success = false; - if (cmSystemTools::FileExists(filename)) { - std::size_t const length = cmSystemTools::FileLength(filename); - cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); - if (ifs) { - content.resize(length); - ifs.read(&content.front(), content.size()); - if (ifs) { - success = true; - } else { - content.clear(); - if (error != nullptr) { - error->append("Reading from the file failed."); - } - } - } else if (error != nullptr) { - error->append("Opening the file for reading failed."); - } - } else if (error != nullptr) { - error->append("The file does not exist."); - } - return success; -} - -/** - * @brief Tests if buildFile is older than sourceFile - * @return True if buildFile is older than sourceFile. - * False may indicate an error. - */ -static bool FileIsOlderThan(std::string const& buildFile, - std::string const& sourceFile, - std::string* error = nullptr) -{ - int result = 0; - if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) { - return (result < 0); - } - if (error != nullptr) { - error->append( - "File modification time comparison failed for the files\n "); - error->append(cmQtAutoGen::Quoted(buildFile)); - error->append("\nand\n "); - error->append(cmQtAutoGen::Quoted(sourceFile)); - } - return false; -} - static bool ListContains(std::vector const& list, std::string const& entry) { @@ -132,21 +49,11 @@ static bool ListContains(std::vector const& list, cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic() : MultiConfig(cmQtAutoGen::WRAP) , IncludeProjectDirsBefore(false) - , Verbose(cmSystemTools::HasEnv("VERBOSE")) - , ColorOutput(true) , MocSettingsChanged(false) , MocPredefsChanged(false) , MocRelaxedMode(false) , UicSettingsChanged(false) { - { - std::string colorEnv; - cmSystemTools::GetEnv("COLOR", colorEnv); - if (!colorEnv.empty()) { - this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str()); - } - } - // Precompile regular expressions this->MocRegExpInclude.compile( "[\n][ \t]*#[ \t]*include[ \t]+" @@ -155,39 +62,7 @@ cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic() "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); } -bool cmQtAutoGeneratorMocUic::Run(std::string const& targetDirectory, - std::string const& config) -{ - cmake cm(cmake::RoleScript); - cm.SetHomeOutputDirectory(targetDirectory); - cm.SetHomeDirectory(targetDirectory); - cm.GetCurrentSnapshot().SetDefaultDefinitions(); - cmGlobalGenerator gg(&cm); - - cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); - snapshot.GetDirectory().SetCurrentBinary(targetDirectory); - snapshot.GetDirectory().SetCurrentSource(targetDirectory); - - auto makefile = cm::make_unique(&gg, snapshot); - gg.SetCurrentMakefile(makefile.get()); - - bool success = false; - if (this->InitInfoFile(makefile.get(), targetDirectory, config)) { - // Read latest settings - this->SettingsFileRead(makefile.get()); - if (this->Process()) { - // Write current settings - if (this->SettingsFileWrite()) { - success = true; - } - } - } - return success; -} - -bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile, - std::string const& targetDirectory, - std::string const& config) +bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) { // -- Meta this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); @@ -231,12 +106,12 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile, } return lists; }; - auto InfoGetConfig = [makefile, &config](const char* key) -> std::string { + auto InfoGetConfig = [makefile, this](const char* key) -> std::string { const char* valueConf = nullptr; { std::string keyConf = key; keyConf += '_'; - keyConf += config; + keyConf += this->GetInfoConfig(); valueConf = makefile->GetDefinition(keyConf); } if (valueConf == nullptr) { @@ -252,11 +127,8 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile, }; // -- Read info file - this->InfoFile = cmSystemTools::CollapseFullPath(targetDirectory); - cmSystemTools::ConvertToUnixSlashes(this->InfoFile); - this->InfoFile += "/AutogenInfo.cmake"; - if (!makefile->ReadListFile(this->InfoFile.c_str())) { - this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + if (!makefile->ReadListFile(this->GetInfoFile().c_str())) { + this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(), "File processing failed"); return false; } @@ -266,7 +138,19 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile, this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX"); if (this->ConfigSuffix.empty()) { this->ConfigSuffix = "_"; - this->ConfigSuffix += config; + this->ConfigSuffix += this->GetInfoConfig(); + } + + this->SettingsFile = InfoGetConfig("AM_SETTINGS_FILE"); + if (!this->SettingsFile.empty()) { + if (this->MultiConfig != cmQtAutoGen::SINGLE) { + this->SettingsFile = cmQtAutoGen::AppendFilenameSuffix( + this->SettingsFile, this->ConfigSuffix); + } + } else { + this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(), + "Settings file is missing"); + return false; } // - Files and directories @@ -278,7 +162,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile, InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); this->AutogenBuildDir = InfoGet("AM_BUILD_DIR"); if (this->AutogenBuildDir.empty()) { - this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(), "Autogen build directory missing"); return false; } @@ -291,7 +175,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile, // Check Qt version if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) { - this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(), "Unsupported Qt version: " + cmQtAutoGen::Quoted(this->QtMajorVersion)); return false; @@ -341,7 +225,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile, } } else { this->LogFileError( - cmQtAutoGen::MOC, this->InfoFile, + cmQtAutoGen::MOC, this->GetInfoFile(), "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); return false; } @@ -362,7 +246,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile, std::ostringstream ost; ost << "files/options lists sizes missmatch (" << sources.size() << "/" << options.size() << ")"; - this->LogFileError(cmQtAutoGen::UIC, this->InfoFile, ost.str()); + this->LogFileError(cmQtAutoGen::UIC, this->GetInfoFile(), ost.str()); return false; } auto fitEnd = sources.cend(); @@ -535,17 +419,6 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile, } } - // - Old settings file - { - this->SettingsFile = cmSystemTools::CollapseFullPath(targetDirectory); - cmSystemTools::ConvertToUnixSlashes(this->SettingsFile); - this->SettingsFile += "/AutogenOldSettings"; - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - this->SettingsFile += this->ConfigSuffix; - } - this->SettingsFile += ".cmake"; - } - return true; } @@ -614,7 +487,7 @@ bool cmQtAutoGeneratorMocUic::SettingsFileWrite() bool success = true; // Only write if any setting changed if (this->SettingsChanged()) { - if (this->Verbose) { + if (this->GetVerbose()) { this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " + cmQtAutoGen::Quoted(this->SettingsFile)); } @@ -644,7 +517,7 @@ bool cmQtAutoGeneratorMocUic::SettingsFileWrite() return success; } -bool cmQtAutoGeneratorMocUic::Process() +bool cmQtAutoGeneratorMocUic::Process(cmMakefile* makefile) { // the program goes through all .cpp files to see which moc files are // included. It is not really interesting how the moc file is named, but @@ -654,6 +527,12 @@ bool cmQtAutoGeneratorMocUic::Process() // moc file is included anywhere a moc_.cpp file is created and // included in the mocs_compilation.cpp file. + if (!this->InitInfoFile(makefile)) { + return false; + } + // Read latest settings + this->SettingsFileRead(makefile); + // Create AUTOGEN include directory { std::string const incDirAbs = cmSystemTools::CollapseCombinedPath( @@ -690,6 +569,10 @@ bool cmQtAutoGeneratorMocUic::Process() return false; } + if (!this->SettingsFileWrite()) { + return false; + } + return true; } @@ -701,7 +584,7 @@ bool cmQtAutoGeneratorMocUic::ParseSourceFile(std::string const& absFilename, { std::string contentText; std::string error; - bool success = ReadFile(contentText, absFilename, &error); + bool success = this->FileRead(contentText, absFilename, &error); if (success) { if (!contentText.empty()) { if (job.Moc) { @@ -729,7 +612,7 @@ bool cmQtAutoGeneratorMocUic::ParseHeaderFile(std::string const& absFilename, { std::string contentText; std::string error; - bool success = ReadFile(contentText, absFilename, &error); + bool success = this->FileRead(contentText, absFilename, &error); if (success) { if (!contentText.empty()) { if (job.Moc) { @@ -760,7 +643,7 @@ bool cmQtAutoGeneratorMocUic::ParsePostprocess() if (!item->DependsValid) { std::string content; std::string error; - if (ReadFile(content, item->SourceFile, &error)) { + if (this->FileRead(content, item->SourceFile, &error)) { this->MocFindDepends(item->SourceFile, content, item->Depends); item->DependsValid = true; } else { @@ -968,7 +851,7 @@ void cmQtAutoGeneratorMocUic::MocFindDepends(std::string const& absFilename, std::string incFile; if (this->MocFindIncludedFile(incFile, sourcePath, match)) { depends.insert(incFile); - if (this->Verbose) { + if (this->GetVerbose()) { this->LogInfo(cmQtAutoGen::MOC, "Found dependency:\n " + cmQtAutoGen::Quoted(absFilename) + "\n " + cmQtAutoGen::Quoted(incFile)); @@ -988,7 +871,7 @@ void cmQtAutoGeneratorMocUic::MocFindDepends(std::string const& absFilename, bool cmQtAutoGeneratorMocUic::MocParseSourceContent( std::string const& absFilename, std::string const& contentText) { - if (this->Verbose) { + if (this->GetVerbose()) { this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); } @@ -1227,7 +1110,7 @@ bool cmQtAutoGeneratorMocUic::MocParseSourceContent( void cmQtAutoGeneratorMocUic::MocParseHeaderContent( std::string const& absFilename, std::string const& contentText) { - if (this->Verbose) { + if (this->GetVerbose()) { this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); } @@ -1312,7 +1195,7 @@ bool cmQtAutoGeneratorMocUic::MocGenerateAll() if (!this->MocPredefsCmd.empty()) { if (this->MocSettingsChanged || !cmSystemTools::FileExists(this->MocPredefsFileAbs)) { - if (this->Verbose) { + if (this->GetVerbose()) { this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel); } @@ -1347,7 +1230,7 @@ bool cmQtAutoGeneratorMocUic::MocGenerateAll() } } else { // Touch to update the time stamp - if (this->Verbose) { + if (this->GetVerbose()) { this->LogInfo(cmQtAutoGen::MOC, "Touching moc_predefs " + this->MocPredefsFileRel); } @@ -1398,7 +1281,7 @@ bool cmQtAutoGeneratorMocUic::MocGenerateAll() if (this->FileDiffers(this->MocCompFileAbs, mocs)) { // Actually write mocs compilation file - if (this->Verbose) { + if (this->GetVerbose()) { this->LogBold("Generating MOC compilation " + this->MocCompFileRel); } if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, mocs)) { @@ -1408,7 +1291,7 @@ bool cmQtAutoGeneratorMocUic::MocGenerateAll() } } else if (autoNameGenerated) { // Only touch mocs compilation file - if (this->Verbose) { + if (this->GetVerbose()) { this->LogInfo(cmQtAutoGen::MOC, "Touching mocs compilation " + this->MocCompFileRel); } @@ -1433,7 +1316,7 @@ bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob, bool generate = false; std::string generateReason; if (!generate && !cmSystemTools::FileExists(mocFileAbs.c_str())) { - if (this->Verbose) { + if (this->GetVerbose()) { generateReason = "Generating "; generateReason += cmQtAutoGen::Quoted(mocFileAbs); generateReason += " from its source file "; @@ -1443,7 +1326,7 @@ bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob, generate = true; } if (!generate && this->MocSettingsChanged) { - if (this->Verbose) { + if (this->GetVerbose()) { generateReason = "Generating "; generateReason += cmQtAutoGen::Quoted(mocFileAbs); generateReason += " from "; @@ -1453,7 +1336,7 @@ bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob, generate = true; } if (!generate && this->MocPredefsChanged) { - if (this->Verbose) { + if (this->GetVerbose()) { generateReason = "Generating "; generateReason += cmQtAutoGen::Quoted(mocFileAbs); generateReason += " from "; @@ -1465,7 +1348,7 @@ bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob, if (!generate) { std::string error; if (FileIsOlderThan(mocFileAbs, mocJob.SourceFile, &error)) { - if (this->Verbose) { + if (this->GetVerbose()) { generateReason = "Generating "; generateReason += cmQtAutoGen::Quoted(mocFileAbs); generateReason += " because it's older than its source file "; @@ -1484,7 +1367,7 @@ bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob, std::string error; for (std::string const& depFile : mocJob.Depends) { if (FileIsOlderThan(mocFileAbs, depFile, &error)) { - if (this->Verbose) { + if (this->GetVerbose()) { generateReason = "Generating "; generateReason += cmQtAutoGen::Quoted(mocFileAbs); generateReason += " from "; @@ -1505,7 +1388,7 @@ bool cmQtAutoGeneratorMocUic::MocGenerateFile(const MocJobAuto& mocJob, if (generate) { // Log - if (this->Verbose) { + if (this->GetVerbose()) { this->LogBold("Generating MOC source " + mocJob.BuildFileRel); this->LogInfo(cmQtAutoGen::MOC, generateReason); } @@ -1569,7 +1452,7 @@ bool cmQtAutoGeneratorMocUic::UicSkip(std::string const& absFilename) const bool cmQtAutoGeneratorMocUic::UicParseContent(std::string const& absFilename, std::string const& contentText) { - if (this->Verbose) { + if (this->GetVerbose()) { this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename); } @@ -1755,7 +1638,7 @@ bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob) bool generate = false; std::string generateReason; if (!generate && !cmSystemTools::FileExists(uicFileAbs.c_str())) { - if (this->Verbose) { + if (this->GetVerbose()) { generateReason = "Generating "; generateReason += cmQtAutoGen::Quoted(uicFileAbs); generateReason += " from its source file "; @@ -1765,7 +1648,7 @@ bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob) generate = true; } if (!generate && this->UicSettingsChanged) { - if (this->Verbose) { + if (this->GetVerbose()) { generateReason = "Generating "; generateReason += cmQtAutoGen::Quoted(uicFileAbs); generateReason += " from "; @@ -1777,7 +1660,7 @@ bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob) if (!generate) { std::string error; if (FileIsOlderThan(uicFileAbs, uicJob.SourceFile, &error)) { - if (this->Verbose) { + if (this->GetVerbose()) { generateReason = "Generating "; generateReason += cmQtAutoGen::Quoted(uicFileAbs); generateReason += " because it's older than its source file "; @@ -1793,7 +1676,7 @@ bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob) } if (generate) { // Log - if (this->Verbose) { + if (this->GetVerbose()) { this->LogBold("Generating UIC header " + uicJob.BuildFileRel); this->LogInfo(cmQtAutoGen::UIC, generateReason); } @@ -1839,191 +1722,6 @@ bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob) return success; } -void cmQtAutoGeneratorMocUic::LogBold(std::string const& message) const -{ - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, this->ColorOutput); -} - -void cmQtAutoGeneratorMocUic::LogInfo(cmQtAutoGen::Generator genType, - std::string const& message) const -{ - std::string msg = cmQtAutoGen::GeneratorName(genType); - msg += ": "; - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - cmSystemTools::Stdout(msg.c_str(), msg.size()); -} - -void cmQtAutoGeneratorMocUic::LogWarning(cmQtAutoGen::Generator genType, - std::string const& message) const -{ - std::string msg = cmQtAutoGen::GeneratorName(genType); - msg += " warning:"; - if (message.find('\n') == std::string::npos) { - // Single line message - msg.push_back(' '); - } else { - // Multi line message - msg.push_back('\n'); - } - // Message - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - cmSystemTools::Stdout(msg.c_str(), msg.size()); -} - -void cmQtAutoGeneratorMocUic::LogFileWarning(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const -{ - std::string msg = " "; - msg += cmQtAutoGen::Quoted(filename); - msg.push_back('\n'); - // Message - msg += message; - this->LogWarning(genType, msg); -} - -void cmQtAutoGeneratorMocUic::LogError(cmQtAutoGen::Generator genType, - std::string const& message) const -{ - std::string msg; - msg.push_back('\n'); - msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error"); - // Message - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - cmSystemTools::Stderr(msg.c_str(), msg.size()); -} - -void cmQtAutoGeneratorMocUic::LogFileError(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const -{ - std::string emsg = " "; - emsg += cmQtAutoGen::Quoted(filename); - emsg += '\n'; - // Message - emsg += message; - this->LogError(genType, emsg); -} - -void cmQtAutoGeneratorMocUic::LogCommandError( - cmQtAutoGen::Generator genType, std::string const& message, - std::vector const& command, std::string const& output) const -{ - std::string msg; - msg.push_back('\n'); - msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error"); - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - msg += HeadLine("Command"); - msg += QuotedCommand(command); - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - msg += HeadLine("Output"); - msg += output; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - cmSystemTools::Stderr(msg.c_str(), msg.size()); -} - -/** - * @brief Generates the parent directory of the given file on demand - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::MakeParentDirectory( - cmQtAutoGen::Generator genType, std::string const& filename) const -{ - bool success = true; - std::string const dirName = cmSystemTools::GetFilenamePath(filename); - if (!dirName.empty()) { - if (!cmSystemTools::MakeDirectory(dirName)) { - this->LogFileError(genType, filename, - "Could not create parent directory"); - success = false; - } - } - return success; -} - -bool cmQtAutoGeneratorMocUic::FileDiffers(std::string const& filename, - std::string const& content) -{ - bool differs = true; - { - std::string oldContents; - if (ReadFile(oldContents, filename)) { - differs = (oldContents != content); - } - } - return differs; -} - -bool cmQtAutoGeneratorMocUic::FileWrite(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& content) -{ - std::string error; - // Make sure the parent directory exists - if (this->MakeParentDirectory(genType, filename)) { - cmsys::ofstream outfile; - outfile.open(filename.c_str(), - (std::ios::out | std::ios::binary | std::ios::trunc)); - if (outfile) { - outfile << content; - // Check for write errors - if (!outfile.good()) { - error = "File writing failed"; - } - } else { - error = "Opening file for writing failed"; - } - } - if (!error.empty()) { - this->LogFileError(genType, filename, error); - return false; - } - return true; -} - -/** - * @brief Runs a command and returns true on success - * @return True on success - */ -bool cmQtAutoGeneratorMocUic::RunCommand( - std::vector const& command, std::string& output) const -{ - // Log command - if (this->Verbose) { - std::string qcmd = QuotedCommand(command); - qcmd.push_back('\n'); - cmSystemTools::Stdout(qcmd.c_str(), qcmd.size()); - } - // Execute command - int retVal = 0; - bool res = cmSystemTools::RunSingleCommand( - command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE); - return (res && (retVal == 0)); -} - /** * @brief Tries to find the header file to the given file base path by * appending different header extensions diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoGeneratorMocUic.h index 579b4f4..a03270b 100644 --- a/Source/cmQtAutoGeneratorMocUic.h +++ b/Source/cmQtAutoGeneratorMocUic.h @@ -7,6 +7,7 @@ #include "cmFilePathChecksum.h" #include "cmQtAutoGen.h" +#include "cmQtAutoGenerator.h" #include "cmsys/RegularExpression.hxx" #include @@ -17,12 +18,11 @@ class cmMakefile; -class cmQtAutoGeneratorMocUic +class cmQtAutoGeneratorMocUic : public cmQtAutoGenerator { CM_DISABLE_COPY(cmQtAutoGeneratorMocUic) public: cmQtAutoGeneratorMocUic(); - bool Run(std::string const& targetDirectory, std::string const& config); private: // -- Types @@ -81,8 +81,7 @@ private: }; // -- Initialization - bool InitInfoFile(cmMakefile* makefile, std::string const& targetDirectory, - std::string const& config); + bool InitInfoFile(cmMakefile* makefile); // -- Settings file void SettingsFileRead(cmMakefile* makefile); @@ -93,7 +92,7 @@ private: } // -- Central processing - bool Process(); + bool Process(cmMakefile* makefile) override; // -- Source parsing bool ParseSourceFile(std::string const& absFilename, const SourceJob& job); @@ -136,45 +135,14 @@ private: bool UicGenerateAll(); bool UicGenerateFile(const UicJob& uicJob); - // -- Log info - void LogBold(std::string const& message) const; - void LogInfo(cmQtAutoGen::Generator genType, - std::string const& message) const; - // -- Log warning - void LogWarning(cmQtAutoGen::Generator genType, - std::string const& message) const; - void LogFileWarning(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const; - // -- Log error - void LogError(cmQtAutoGen::Generator genType, - std::string const& message) const; - void LogFileError(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const; - void LogCommandError(cmQtAutoGen::Generator genType, - std::string const& message, - std::vector const& command, - std::string const& output) const; - // -- Utility - bool MakeParentDirectory(cmQtAutoGen::Generator genType, - std::string const& filename) const; - bool FileDiffers(std::string const& filename, std::string const& content); - bool FileWrite(cmQtAutoGen::Generator genType, std::string const& filename, - std::string const& content); bool FindHeader(std::string& header, std::string const& testBasePath) const; - bool RunCommand(std::vector const& command, - std::string& output) const; // -- Meta - std::string InfoFile; std::string ConfigSuffix; cmQtAutoGen::MultiConfig MultiConfig; // -- Settings bool IncludeProjectDirsBefore; - bool Verbose; - bool ColorOutput; std::string SettingsFile; std::string SettingsStringMoc; std::string SettingsStringUic; diff --git a/Source/cmQtAutoGeneratorRcc.cxx b/Source/cmQtAutoGeneratorRcc.cxx index e057937..4348f2b 100644 --- a/Source/cmQtAutoGeneratorRcc.cxx +++ b/Source/cmQtAutoGeneratorRcc.cxx @@ -3,146 +3,22 @@ #include "cmQtAutoGen.h" #include "cmQtAutoGeneratorRcc.h" -#include "cmsys/FStream.hxx" -#include "cmsys/Terminal.h" - #include "cmAlgorithms.h" #include "cmCryptoHash.h" -#include "cmFilePathChecksum.h" -#include "cmGlobalGenerator.h" #include "cmMakefile.h" #include "cmOutputConverter.h" -#include "cmStateDirectory.h" -#include "cmStateSnapshot.h" #include "cmSystemTools.h" -#include "cmake.h" - -#if defined(__APPLE__) -#include -#endif // -- Static variables static const char* SettingsKeyRcc = "ARCC_SETTINGS_HASH"; -// -- Static functions - -static std::string HeadLine(std::string const& title) -{ - std::string head = title; - head += '\n'; - head.append(head.size() - 1, '-'); - head += '\n'; - return head; -} - -static std::string QuotedCommand(std::vector const& command) -{ - std::string res; - for (std::string const& item : command) { - if (!res.empty()) { - res.push_back(' '); - } - std::string const cesc = cmQtAutoGen::Quoted(item); - if (item.empty() || (cesc.size() > (item.size() + 2)) || - (cesc.find(' ') != std::string::npos)) { - res += cesc; - } else { - res += item; - } - } - return res; -} - -static bool ReadFile(std::string& content, std::string const& filename, - std::string* error = nullptr) -{ - bool success = false; - if (cmSystemTools::FileExists(filename)) { - std::size_t const length = cmSystemTools::FileLength(filename); - cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); - if (ifs) { - content.resize(length); - ifs.read(&content.front(), content.size()); - if (ifs) { - success = true; - } else { - content.clear(); - if (error != nullptr) { - error->append("Reading from the file failed."); - } - } - } else if (error != nullptr) { - error->append("Opening the file for reading failed."); - } - } else if (error != nullptr) { - error->append("The file does not exist."); - } - return success; -} - -/** - * @brief Tests if buildFile is older than sourceFile - * @return True if buildFile is older than sourceFile. - * False may indicate an error. - */ -static bool FileIsOlderThan(std::string const& buildFile, - std::string const& sourceFile, - std::string* error = nullptr) -{ - int result = 0; - if (cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result)) { - return (result < 0); - } - if (error != nullptr) { - error->append( - "File modification time comparison failed for the files\n "); - error->append(cmQtAutoGen::Quoted(buildFile)); - error->append("\nand\n "); - error->append(cmQtAutoGen::Quoted(sourceFile)); - } - return false; -} - // -- Class methods cmQtAutoGeneratorRcc::cmQtAutoGeneratorRcc() : MultiConfig(cmQtAutoGen::WRAP) - , Verbose(cmSystemTools::HasEnv("VERBOSE")) - , ColorOutput(true) , SettingsChanged(false) { - { - std::string colorEnv; - cmSystemTools::GetEnv("COLOR", colorEnv); - if (!colorEnv.empty()) { - this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str()); - } - } -} - -bool cmQtAutoGeneratorRcc::Run(std::string const& infoFile, - std::string const& config) -{ - // Info settings - this->InfoFile = infoFile; - this->InfoDir = cmSystemTools::GetFilenamePath(infoFile); - this->InfoConfig = config; - - cmake cm(cmake::RoleScript); - cm.SetHomeOutputDirectory(this->InfoDir); - cm.SetHomeDirectory(this->InfoDir); - cm.GetCurrentSnapshot().SetDefaultDefinitions(); - cmGlobalGenerator gg(&cm); - - cmStateSnapshot snapshot = cm.GetCurrentSnapshot(); - snapshot.GetDirectory().SetCurrentBinary(this->InfoDir); - snapshot.GetDirectory().SetCurrentSource(this->InfoDir); - - auto makefile = cm::make_unique(&gg, snapshot); - gg.SetCurrentMakefile(makefile.get()); - - return this->Process(makefile.get()); } bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) @@ -161,7 +37,7 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) { std::string keyConf = key; keyConf += '_'; - keyConf += this->InfoConfig; + keyConf += this->GetInfoConfig(); valueConf = makefile->GetDefinition(keyConf); } if (valueConf == nullptr) { @@ -177,8 +53,8 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) }; // -- Read info file - if (!makefile->ReadListFile(this->InfoFile.c_str())) { - this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, + if (!makefile->ReadListFile(this->GetInfoFile().c_str())) { + this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), "File processing failed"); return false; } @@ -189,7 +65,7 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) this->ConfigSuffix = InfoGetConfig("ARCC_CONFIG_SUFFIX"); if (this->ConfigSuffix.empty()) { this->ConfigSuffix = "_"; - this->ConfigSuffix += this->InfoConfig; + this->ConfigSuffix += this->GetInfoConfig(); } this->SettingsFile = InfoGetConfig("ARCC_SETTINGS_FILE"); @@ -219,27 +95,27 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) // - Validity checks if (this->SettingsFile.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, + this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), "Settings file name missing"); return false; } if (this->AutogenBuildDir.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, + this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), "Autogen build directory missing"); return false; } if (this->RccExecutable.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, + this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), "rcc executable missing"); return false; } if (this->QrcFile.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, + this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), "rcc input file missing"); return false; } if (this->RccFile.empty()) { - this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, + this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(), "rcc output file missing"); return false; } @@ -304,7 +180,7 @@ bool cmQtAutoGeneratorRcc::SettingsFileWrite() bool success = true; // Only write if any setting changed if (this->SettingsChanged) { - if (this->Verbose) { + if (this->GetVerbose()) { this->LogInfo(cmQtAutoGen::RCC, "Writing settings file " + cmQtAutoGen::Quoted(this->SettingsFile)); } @@ -392,7 +268,7 @@ bool cmQtAutoGeneratorRcc::RccGenerate() success = false; } if (success && !generate && !cmSystemTools::FileExists(rccFileAbs.c_str())) { - if (this->Verbose) { + if (this->GetVerbose()) { generateReason = "Generating "; generateReason += cmQtAutoGen::Quoted(rccFileAbs); generateReason += " from its source file "; @@ -402,7 +278,7 @@ bool cmQtAutoGeneratorRcc::RccGenerate() generate = true; } if (success && !generate && this->SettingsChanged) { - if (this->Verbose) { + if (this->GetVerbose()) { generateReason = "Generating "; generateReason += cmQtAutoGen::Quoted(rccFileAbs); generateReason += " from "; @@ -414,7 +290,7 @@ bool cmQtAutoGeneratorRcc::RccGenerate() if (success && !generate) { std::string error; if (FileIsOlderThan(rccFileAbs, this->QrcFile, &error)) { - if (this->Verbose) { + if (this->GetVerbose()) { generateReason = "Generating "; generateReason += cmQtAutoGen::Quoted(rccFileAbs); generateReason += " because it is older than "; @@ -457,7 +333,7 @@ bool cmQtAutoGeneratorRcc::RccGenerate() break; } if (FileIsOlderThan(rccFileAbs, resFile, &error)) { - if (this->Verbose) { + if (this->GetVerbose()) { generateReason = "Generating "; generateReason += cmQtAutoGen::Quoted(rccFileAbs); generateReason += " from "; @@ -482,7 +358,7 @@ bool cmQtAutoGeneratorRcc::RccGenerate() // Regenerate on demand if (generate) { // Log - if (this->Verbose) { + if (this->GetVerbose()) { this->LogBold("Generating RCC source " + rccFileRel); this->LogInfo(cmQtAutoGen::RCC, generateReason); } @@ -531,7 +407,7 @@ bool cmQtAutoGeneratorRcc::RccGenerate() // Write content to file if (this->FileDiffers(wrapperFileAbs, content)) { // Write new wrapper file - if (this->Verbose) { + if (this->GetVerbose()) { this->LogBold("Generating RCC wrapper " + wrapperFileRel); } if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) { @@ -541,7 +417,7 @@ bool cmQtAutoGeneratorRcc::RccGenerate() } } else if (rccGenerated) { // Just touch the wrapper file - if (this->Verbose) { + if (this->GetVerbose()) { this->LogInfo(cmQtAutoGen::RCC, "Touching RCC wrapper " + wrapperFileRel); } @@ -551,188 +427,3 @@ bool cmQtAutoGeneratorRcc::RccGenerate() return success; } - -void cmQtAutoGeneratorRcc::LogBold(std::string const& message) const -{ - cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | - cmsysTerminal_Color_ForegroundBold, - message.c_str(), true, this->ColorOutput); -} - -void cmQtAutoGeneratorRcc::LogInfo(cmQtAutoGen::Generator genType, - std::string const& message) const -{ - std::string msg = cmQtAutoGen::GeneratorName(genType); - msg += ": "; - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - cmSystemTools::Stdout(msg.c_str(), msg.size()); -} - -void cmQtAutoGeneratorRcc::LogWarning(cmQtAutoGen::Generator genType, - std::string const& message) const -{ - std::string msg = cmQtAutoGen::GeneratorName(genType); - msg += " warning:"; - if (message.find('\n') == std::string::npos) { - // Single line message - msg.push_back(' '); - } else { - // Multi line message - msg.push_back('\n'); - } - // Message - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - cmSystemTools::Stdout(msg.c_str(), msg.size()); -} - -void cmQtAutoGeneratorRcc::LogFileWarning(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const -{ - std::string msg = " "; - msg += cmQtAutoGen::Quoted(filename); - msg.push_back('\n'); - // Message - msg += message; - this->LogWarning(genType, msg); -} - -void cmQtAutoGeneratorRcc::LogError(cmQtAutoGen::Generator genType, - std::string const& message) const -{ - std::string msg; - msg.push_back('\n'); - msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error"); - // Message - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - cmSystemTools::Stderr(msg.c_str(), msg.size()); -} - -void cmQtAutoGeneratorRcc::LogFileError(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const -{ - std::string emsg = " "; - emsg += cmQtAutoGen::Quoted(filename); - emsg += '\n'; - // Message - emsg += message; - this->LogError(genType, emsg); -} - -void cmQtAutoGeneratorRcc::LogCommandError( - cmQtAutoGen::Generator genType, std::string const& message, - std::vector const& command, std::string const& output) const -{ - std::string msg; - msg.push_back('\n'); - msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error"); - msg += message; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - msg += HeadLine("Command"); - msg += QuotedCommand(command); - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - msg += HeadLine("Output"); - msg += output; - if (msg.back() != '\n') { - msg.push_back('\n'); - } - msg.push_back('\n'); - cmSystemTools::Stderr(msg.c_str(), msg.size()); -} - -/** - * @brief Generates the parent directory of the given file on demand - * @return True on success - */ -bool cmQtAutoGeneratorRcc::MakeParentDirectory( - cmQtAutoGen::Generator genType, std::string const& filename) const -{ - bool success = true; - std::string const dirName = cmSystemTools::GetFilenamePath(filename); - if (!dirName.empty()) { - if (!cmSystemTools::MakeDirectory(dirName)) { - this->LogFileError(genType, filename, - "Could not create parent directory"); - success = false; - } - } - return success; -} - -bool cmQtAutoGeneratorRcc::FileDiffers(std::string const& filename, - std::string const& content) -{ - bool differs = true; - { - std::string oldContents; - if (ReadFile(oldContents, filename)) { - differs = (oldContents != content); - } - } - return differs; -} - -bool cmQtAutoGeneratorRcc::FileWrite(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& content) -{ - std::string error; - // Make sure the parent directory exists - if (this->MakeParentDirectory(genType, filename)) { - cmsys::ofstream outfile; - outfile.open(filename.c_str(), - (std::ios::out | std::ios::binary | std::ios::trunc)); - if (outfile) { - outfile << content; - // Check for write errors - if (!outfile.good()) { - error = "File writing failed"; - } - } else { - error = "Opening file for writing failed"; - } - } - if (!error.empty()) { - this->LogFileError(genType, filename, error); - return false; - } - return true; -} - -/** - * @brief Runs a command and returns true on success - * @return True on success - */ -bool cmQtAutoGeneratorRcc::RunCommand(std::vector const& command, - std::string& output) const -{ - // Log command - if (this->Verbose) { - std::string qcmd = QuotedCommand(command); - qcmd.push_back('\n'); - cmSystemTools::Stdout(qcmd.c_str(), qcmd.size()); - } - // Execute command - int retVal = 0; - bool res = cmSystemTools::RunSingleCommand( - command, &output, &output, &retVal, nullptr, cmSystemTools::OUTPUT_NONE); - return (res && (retVal == 0)); -} diff --git a/Source/cmQtAutoGeneratorRcc.h b/Source/cmQtAutoGeneratorRcc.h index 4539461..804d117 100644 --- a/Source/cmQtAutoGeneratorRcc.h +++ b/Source/cmQtAutoGeneratorRcc.h @@ -7,18 +7,18 @@ #include "cmFilePathChecksum.h" #include "cmQtAutoGen.h" +#include "cmQtAutoGenerator.h" #include #include class cmMakefile; -class cmQtAutoGeneratorRcc +class cmQtAutoGeneratorRcc : public cmQtAutoGenerator { CM_DISABLE_COPY(cmQtAutoGeneratorRcc) public: cmQtAutoGeneratorRcc(); - bool Run(std::string const& infoFile, std::string const& config); private: // -- Initialization & settings @@ -26,47 +26,13 @@ private: void SettingsFileRead(cmMakefile* makefile); bool SettingsFileWrite(); // -- Central processing - bool Process(cmMakefile* makefile); + bool Process(cmMakefile* makefile) override; bool RccGenerate(); - // -- Log info - void LogBold(std::string const& message) const; - void LogInfo(cmQtAutoGen::Generator genType, - std::string const& message) const; - // -- Log warning - void LogWarning(cmQtAutoGen::Generator genType, - std::string const& message) const; - void LogFileWarning(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const; - // -- Log error - void LogError(cmQtAutoGen::Generator genType, - std::string const& message) const; - void LogFileError(cmQtAutoGen::Generator genType, - std::string const& filename, - std::string const& message) const; - void LogCommandError(cmQtAutoGen::Generator genType, - std::string const& message, - std::vector const& command, - std::string const& output) const; - // -- Utility - bool MakeParentDirectory(cmQtAutoGen::Generator genType, - std::string const& filename) const; - bool FileDiffers(std::string const& filename, std::string const& content); - bool FileWrite(cmQtAutoGen::Generator genType, std::string const& filename, - std::string const& content); - bool RunCommand(std::vector const& command, - std::string& output) const; - // -- Info settings - std::string InfoFile; - std::string InfoDir; - std::string InfoConfig; // -- Config settings std::string ConfigSuffix; cmQtAutoGen::MultiConfig MultiConfig; // -- Settings - bool Verbose; - bool ColorOutput; bool SettingsChanged; std::string SettingsFile; std::string SettingsString; -- cgit v0.12 From 2a85b5ac768cde4a9bbe98551528c8bae1e268a8 Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Sat, 18 Nov 2017 15:08:08 +0100 Subject: Autogen: Make cmQtAutoGeneratorInitializer an instantiable class Remove the cmQtAutoGenDigest classes and make cmQtAutoGeneratorInitializer instantiable instead. --- Source/CMakeLists.txt | 1 - Source/cmGlobalGenerator.cxx | 36 +- Source/cmGlobalGenerator.h | 5 +- Source/cmQtAutoGenDigest.h | 66 -- Source/cmQtAutoGeneratorInitializer.cxx | 1351 +++++++++++++++---------------- Source/cmQtAutoGeneratorInitializer.h | 76 +- Source/cmQtAutoGeneratorMocUic.cxx | 9 +- Source/cmQtAutoGeneratorRcc.cxx | 6 - 8 files changed, 752 insertions(+), 798 deletions(-) delete mode 100644 Source/cmQtAutoGenDigest.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 6b086fa..6c60675 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -311,7 +311,6 @@ set(SRCS cmPropertyMap.h cmQtAutoGen.cxx cmQtAutoGen.h - cmQtAutoGenDigest.h cmQtAutoGenerator.cxx cmQtAutoGenerator.h cmQtAutoGeneratorInitializer.cxx diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index eba95f5..6e903fb 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -1314,7 +1314,10 @@ bool cmGlobalGenerator::Compute() #ifdef CMAKE_BUILD_WITH_CMAKE // Iterate through all targets and set up automoc for those which have // the AUTOMOC, AUTOUIC or AUTORCC property set - cmQtAutoGenDigestUPV autogenDigests = this->CreateQtAutoGeneratorsTargets(); + auto autogenInits = this->CreateQtAutoGenInitializers(); + for (auto& autoGen : autogenInits) { + autoGen->InitCustomTargets(); + } #endif // Add generator specific helper commands @@ -1335,10 +1338,11 @@ bool cmGlobalGenerator::Compute() } #ifdef CMAKE_BUILD_WITH_CMAKE - for (cmQtAutoGenDigestUP const& digest : autogenDigests) { - cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget(*digest); + for (auto& autoGen : autogenInits) { + autoGen->SetupCustomTargets(); + autoGen.reset(nullptr); } - autogenDigests.clear(); + autogenInits.clear(); #endif for (cmLocalGenerator* localGen : this->LocalGenerators) { @@ -1469,9 +1473,10 @@ bool cmGlobalGenerator::ComputeTargetDepends() return true; } -cmQtAutoGenDigestUPV cmGlobalGenerator::CreateQtAutoGeneratorsTargets() +std::vector> +cmGlobalGenerator::CreateQtAutoGenInitializers() { - cmQtAutoGenDigestUPV autogenDigests; + std::vector> autogenInits; #ifdef CMAKE_BUILD_WITH_CMAKE for (cmLocalGenerator* localGen : this->LocalGenerators) { @@ -1507,25 +1512,12 @@ cmQtAutoGenDigestUPV cmGlobalGenerator::CreateQtAutoGeneratorsTargets() continue; } - { - cmQtAutoGenDigestUP digest(new cmQtAutoGenDigest(target)); - digest->QtVersionMajor = std::move(qtVersionMajor); - digest->QtVersionMinor = - cmQtAutoGeneratorInitializer::GetQtMinorVersion( - target, digest->QtVersionMajor); - digest->MocEnabled = mocEnabled; - digest->UicEnabled = uicEnabled; - digest->RccEnabled = rccEnabled; - autogenDigests.emplace_back(std::move(digest)); - } + autogenInits.emplace_back(new cmQtAutoGeneratorInitializer( + target, mocEnabled, uicEnabled, rccEnabled, qtVersionMajor)); } } - // Initialize autogen targets - for (const cmQtAutoGenDigestUP& digest : autogenDigests) { - cmQtAutoGeneratorInitializer::InitializeAutogenTarget(*digest); - } #endif - return autogenDigests; + return autogenInits; } cmLinkLineComputer* cmGlobalGenerator::CreateLinkLineComputer( diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 8fcb533..99f33e5 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -15,7 +15,6 @@ #include "cmCustomCommandLines.h" #include "cmExportSetMap.h" -#include "cmQtAutoGenDigest.h" #include "cmStateSnapshot.h" #include "cmSystemTools.h" #include "cmTarget.h" @@ -33,6 +32,7 @@ class cmLinkLineComputer; class cmLocalGenerator; class cmMakefile; class cmOutputConverter; +class cmQtAutoGeneratorInitializer; class cmSourceFile; class cmStateDirectory; class cmake; @@ -433,7 +433,8 @@ protected: virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() const; // Qt auto generators - cmQtAutoGenDigestUPV CreateQtAutoGeneratorsTargets(); + std::vector> + CreateQtAutoGenInitializers(); std::string SelectMakeProgram(const std::string& makeProgram, const std::string& makeDefault = "") const; diff --git a/Source/cmQtAutoGenDigest.h b/Source/cmQtAutoGenDigest.h deleted file mode 100644 index 9ee1117..0000000 --- a/Source/cmQtAutoGenDigest.h +++ /dev/null @@ -1,66 +0,0 @@ -/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying - file Copyright.txt or https://cmake.org/licensing for details. */ -#ifndef cmQtAutoGenDigest_h -#define cmQtAutoGenDigest_h - -#include "cmConfigure.h" // IWYU pragma: keep - -#include -#include -#include - -class cmGeneratorTarget; - -class cmQtAutoGenDigestQrc -{ -public: - cmQtAutoGenDigestQrc() - : Generated(false) - , Unique(false) - { - } - -public: - std::string QrcFile; - std::string QrcName; - std::string PathChecksum; - std::string InfoFile; - std::string SettingsFile; - std::string RccFile; - bool Generated; - bool Unique; - std::vector Options; - std::vector Resources; -}; - -/** \class cmQtAutoGenDigest - * \brief Filtered set of QtAutogen variables for a specific target - */ -class cmQtAutoGenDigest -{ -public: - cmQtAutoGenDigest(cmGeneratorTarget* target) - : Target(target) - , MocEnabled(false) - , UicEnabled(false) - , RccEnabled(false) - { - } - -public: - cmGeneratorTarget* Target; - std::string QtVersionMajor; - std::string QtVersionMinor; - bool MocEnabled; - bool UicEnabled; - bool RccEnabled; - std::vector Headers; - std::vector Sources; - std::vector Qrcs; -}; - -// Utility types -typedef std::unique_ptr cmQtAutoGenDigestUP; -typedef std::vector cmQtAutoGenDigestUPV; - -#endif diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx index 1d18bad..5edddaa 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGeneratorInitializer.cxx @@ -51,118 +51,6 @@ inline static std::string GetSafeProperty(cmSourceFile const* sf, return std::string(SafeString(sf->GetProperty(key))); } -static cmQtAutoGen::MultiConfig AutogenMultiConfig( - cmGlobalGenerator* globalGen) -{ - if (!globalGen->IsMultiConfig()) { - return cmQtAutoGen::SINGLE; - } - - // FIXME: Xcode does not support per-config sources, yet. - // (EXCLUDED_SOURCE_FILE_NAMES) - // if (globalGen->GetName().find("Xcode") != std::string::npos) { - // return cmQtAutoGen::FULL; - //} - - // FIXME: Visual Studio does not support per-config sources, yet. - // (EXCLUDED_SOURCE_FILE_NAMES) - // if (globalGen->GetName().find("Visual Studio") != std::string::npos) { - // return cmQtAutoGen::FULL; - //} - - return cmQtAutoGen::WRAP; -} - -static std::string GetAutogenTargetName(cmGeneratorTarget const* target) -{ - std::string autogenTargetName = target->GetName(); - autogenTargetName += "_autogen"; - return autogenTargetName; -} - -static std::string GetAutogenTargetFilesDir(cmGeneratorTarget const* target) -{ - cmMakefile* makefile = target->Target->GetMakefile(); - std::string targetDir = makefile->GetCurrentBinaryDirectory(); - targetDir += makefile->GetCMakeInstance()->GetCMakeFilesDirectory(); - targetDir += "/"; - targetDir += GetAutogenTargetName(target); - targetDir += ".dir"; - return targetDir; -} - -static std::string GetAutogenTargetBuildDir(cmGeneratorTarget const* target) -{ - std::string targetDir = GetSafeProperty(target, "AUTOGEN_BUILD_DIR"); - if (targetDir.empty()) { - cmMakefile* makefile = target->Target->GetMakefile(); - targetDir = makefile->GetCurrentBinaryDirectory(); - targetDir += "/"; - targetDir += GetAutogenTargetName(target); - } - return targetDir; -} - -std::string cmQtAutoGeneratorInitializer::GetQtMajorVersion( - cmGeneratorTarget const* target) -{ - cmMakefile* makefile = target->Target->GetMakefile(); - std::string qtMajor = makefile->GetSafeDefinition("QT_VERSION_MAJOR"); - if (qtMajor.empty()) { - qtMajor = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR"); - } - const char* targetQtVersion = - target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", ""); - if (targetQtVersion != nullptr) { - qtMajor = targetQtVersion; - } - return qtMajor; -} - -std::string cmQtAutoGeneratorInitializer::GetQtMinorVersion( - cmGeneratorTarget const* target, std::string const& qtVersionMajor) -{ - cmMakefile* makefile = target->Target->GetMakefile(); - std::string qtMinor; - if (qtVersionMajor == "5") { - qtMinor = makefile->GetSafeDefinition("Qt5Core_VERSION_MINOR"); - } - if (qtMinor.empty()) { - qtMinor = makefile->GetSafeDefinition("QT_VERSION_MINOR"); - } - - const char* targetQtVersion = - target->GetLinkInterfaceDependentStringProperty("QT_MINOR_VERSION", ""); - if (targetQtVersion != nullptr) { - qtMinor = targetQtVersion; - } - return qtMinor; -} - -static bool QtVersionGreaterOrEqual(std::string const& major, - std::string const& minor, - unsigned long requestMajor, - unsigned long requestMinor) -{ - unsigned long majorUL(0); - unsigned long minorUL(0); - if (cmSystemTools::StringToULong(major.c_str(), &majorUL) && - cmSystemTools::StringToULong(minor.c_str(), &minorUL)) { - return (majorUL > requestMajor) || - (majorUL == requestMajor && minorUL >= requestMinor); - } - return false; -} - -static void GetConfigs(cmMakefile* makefile, std::string& configDefault, - std::vector& configsList) -{ - configDefault = makefile->GetConfigurations(configsList); - if (configsList.empty()) { - configsList.push_back(configDefault); - } -} - static void AddDefinitionEscaped(cmMakefile* makefile, const char* key, std::string const& value) { @@ -258,50 +146,6 @@ static void AddCleanFile(cmMakefile* makefile, std::string const& fileName) false); } -static std::vector AddGeneratedSource( - cmGeneratorTarget* target, std::string const& filename, - cmQtAutoGen::MultiConfig multiConfig, - const std::vector& configsList, cmQtAutoGen::Generator genType) -{ - std::vector genFiles; - // Register source file in makefile and source group - if (multiConfig != cmQtAutoGen::FULL) { - genFiles.push_back(filename); - } else { - for (std::string const& cfg : configsList) { - genFiles.push_back( - cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg)); - } - } - { - cmMakefile* makefile = target->Target->GetMakefile(); - for (std::string const& genFile : genFiles) { - { - cmSourceFile* gFile = makefile->GetOrCreateSource(genFile, true); - gFile->SetProperty("GENERATED", "1"); - gFile->SetProperty("SKIP_AUTOGEN", "On"); - } - AddToSourceGroup(makefile, genFile, genType); - } - } - - // Add source file to target - if (multiConfig != cmQtAutoGen::FULL) { - target->AddSource(filename); - } else { - for (std::string const& cfg : configsList) { - std::string src = "$<$AddSource(src); - } - } - - return genFiles; -} - /* @brief Tests if targetDepend is a STATIC_LIBRARY and if any of its * recursive STATIC_LIBRARY dependencies depends on targetOrigin * (STATIC_LIBRARY cycle). @@ -346,371 +190,182 @@ static bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin, return cycle; } -struct cmQtAutoGenSetup +static std::string RccGetExecutable(cmGeneratorTarget const* target, + std::string const& qtMajorVersion) { - std::set MocSkip; - std::set UicSkip; - - std::map ConfigMocIncludes; - std::map ConfigMocDefines; - std::map ConfigUicOptions; -}; + std::string rccExec; + std::string err; -static void SetupAcquireSkipFiles(cmQtAutoGenDigest const& digest, - cmQtAutoGenSetup& setup) -{ - // Read skip files from makefile sources - { - const std::vector& allSources = - digest.Target->Makefile->GetSourceFiles(); - for (cmSourceFile* sf : allSources) { - // sf->GetExtension() is only valid after sf->GetFullPath() ... - std::string const& fPath = sf->GetFullPath(); - cmSystemTools::FileFormat const fileType = - cmSystemTools::GetFileFormat(sf->GetExtension().c_str()); - if (!(fileType == cmSystemTools::CXX_FILE_FORMAT) && - !(fileType == cmSystemTools::HEADER_FILE_FORMAT)) { - continue; - } - const bool skipAll = sf->GetPropertyAsBool("SKIP_AUTOGEN"); - const bool mocSkip = digest.MocEnabled && - (skipAll || sf->GetPropertyAsBool("SKIP_AUTOMOC")); - const bool uicSkip = digest.UicEnabled && - (skipAll || sf->GetPropertyAsBool("SKIP_AUTOUIC")); - if (mocSkip || uicSkip) { - std::string const absFile = cmSystemTools::GetRealPath(fPath); - if (mocSkip) { - setup.MocSkip.insert(absFile); - } - if (uicSkip) { - setup.UicSkip.insert(absFile); - } - } + cmLocalGenerator* localGen = target->GetLocalGenerator(); + if (qtMajorVersion == "5") { + cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::rcc"); + if (tgt != nullptr) { + rccExec = SafeString(tgt->ImportedGetLocation("")); + } else { + err = "AUTORCC: Qt5::rcc target not found"; + } + } else if (qtMajorVersion == "4") { + cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::rcc"); + if (tgt != nullptr) { + rccExec = SafeString(tgt->ImportedGetLocation("")); + } else { + err = "AUTORCC: Qt4::rcc target not found"; } + } else { + err = "The AUTORCC feature supports only Qt 4 and Qt 5"; + } + + if (!err.empty()) { + err += " ("; + err += target->GetName(); + err += ")"; + cmSystemTools::Error(err.c_str()); } + return rccExec; } -static void SetupAutoTargetMoc(cmQtAutoGenDigest const& digest, - std::string const& configDefault, - std::vector const& configsList, - cmQtAutoGenSetup& setup) +cmQtAutoGeneratorInitializer::cmQtAutoGeneratorInitializer( + cmGeneratorTarget* target, bool mocEnabled, bool uicEnabled, bool rccEnabled, + std::string const& qtVersionMajor) + : Target(target) + , MocEnabled(mocEnabled) + , UicEnabled(uicEnabled) + , RccEnabled(rccEnabled) + , QtVersionMajor(qtVersionMajor) + , MultiConfig(cmQtAutoGen::WRAP) { - cmGeneratorTarget const* target = digest.Target; - cmLocalGenerator* localGen = target->GetLocalGenerator(); - cmMakefile* makefile = target->Target->GetMakefile(); + this->QtVersionMinor = cmQtAutoGeneratorInitializer::GetQtMinorVersion( + target, this->QtVersionMajor); +} - AddDefinitionEscaped(makefile, "_moc_skip", setup.MocSkip); - AddDefinitionEscaped(makefile, "_moc_options", - GetSafeProperty(target, "AUTOMOC_MOC_OPTIONS")); - AddDefinitionEscaped(makefile, "_moc_relaxed_mode", - makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE") ? "TRUE" - : "FALSE"); - AddDefinitionEscaped(makefile, "_moc_macro_names", - GetSafeProperty(target, "AUTOMOC_MACRO_NAMES")); - AddDefinitionEscaped(makefile, "_moc_depend_filters", - GetSafeProperty(target, "AUTOMOC_DEPEND_FILTERS")); +void cmQtAutoGeneratorInitializer::InitCustomTargets() +{ + cmMakefile* makefile = this->Target->Target->GetMakefile(); + cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); + cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator(); - // Compiler predefines - if (target->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES")) { - if (QtVersionGreaterOrEqual(digest.QtVersionMajor, digest.QtVersionMinor, - 5, 8)) { - AddDefinitionEscaped( - makefile, "_moc_predefs_cmd", - makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND")); - } + // Configurations + this->ConfigDefault = makefile->GetConfigurations(this->ConfigsList); + if (this->ConfigsList.empty()) { + this->ConfigsList.push_back(this->ConfigDefault); } - // Moc includes and compile definitions + + // Multi configuration { - auto GetIncludeDirs = [target, - localGen](std::string const& cfg) -> std::string { - // Get the include dirs for this target, without stripping the implicit - // include dirs off, see - // https://gitlab.kitware.com/cmake/cmake/issues/13667 - std::vector includeDirs; - localGen->GetIncludeDirectories(includeDirs, target, "CXX", cfg, false); - return cmJoin(includeDirs, ";"); - }; - auto GetCompileDefinitions = - [target, localGen](std::string const& cfg) -> std::string { - std::set defines; - localGen->AddCompileDefinitions(defines, target, cfg, "CXX"); - return cmJoin(defines, ";"); - }; + if (!globalGen->IsMultiConfig()) { + this->MultiConfig = cmQtAutoGen::SINGLE; + } - // Default configuration settings - std::string const includeDirs = GetIncludeDirs(configDefault); - std::string const compileDefs = GetCompileDefinitions(configDefault); - // Other configuration settings - for (std::string const& cfg : configsList) { - { - std::string const configIncludeDirs = GetIncludeDirs(cfg); - if (configIncludeDirs != includeDirs) { - setup.ConfigMocIncludes[cfg] = configIncludeDirs; - } - } - { - std::string const configCompileDefs = GetCompileDefinitions(cfg); - if (configCompileDefs != compileDefs) { - setup.ConfigMocDefines[cfg] = configCompileDefs; - } - } + // FIXME: Xcode does not support per-config sources, yet. + // (EXCLUDED_SOURCE_FILE_NAMES) + // if (globalGen->GetName().find("Xcode") != std::string::npos) { + // return cmQtAutoGen::FULL; + //} + + // FIXME: Visual Studio does not support per-config sources, yet. + // (EXCLUDED_SOURCE_FILE_NAMES) + // if (globalGen->GetName().find("Visual Studio") != std::string::npos) { + // return cmQtAutoGen::FULL; + //} + } + + // Autogen target name + this->AutogenTargetName = this->Target->GetName(); + this->AutogenTargetName += "_autogen"; + + // Autogen directories + { + // Collapsed current binary directory + std::string const cbd = cmSystemTools::CollapseFullPath( + "", makefile->GetCurrentBinaryDirectory()); + + // Autogen info dir + this->DirInfo = cbd; + this->DirInfo += makefile->GetCMakeInstance()->GetCMakeFilesDirectory(); + this->DirInfo += "/"; + this->DirInfo += this->AutogenTargetName; + this->DirInfo += ".dir"; + cmSystemTools::ConvertToUnixSlashes(this->DirInfo); + + // Autogen build dir + this->DirBuild = GetSafeProperty(this->Target, "AUTOGEN_BUILD_DIR"); + if (this->DirBuild.empty()) { + this->DirBuild = cbd; + this->DirBuild += "/"; + this->DirBuild += this->AutogenTargetName; } - AddDefinitionEscaped(makefile, "_moc_include_dirs", includeDirs); - AddDefinitionEscaped(makefile, "_moc_compile_defs", compileDefs); + cmSystemTools::ConvertToUnixSlashes(this->DirBuild); + + // Working directory + this->DirWork = cbd; + cmSystemTools::ConvertToUnixSlashes(this->DirWork); } - // Moc executable + // Autogen files { - std::string mocExec; - std::string err; + this->AutogenInfoFile = this->DirInfo; + this->AutogenInfoFile += "/AutogenInfo.cmake"; - if (digest.QtVersionMajor == "5") { - cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::moc"); - if (tgt != nullptr) { - mocExec = SafeString(tgt->ImportedGetLocation("")); - } else { - err = "AUTOMOC: Qt5::moc target not found"; - } - } else if (digest.QtVersionMajor == "4") { - cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::moc"); - if (tgt != nullptr) { - mocExec = SafeString(tgt->ImportedGetLocation("")); - } else { - err = "AUTOMOC: Qt4::moc target not found"; - } - } else { - err = "The AUTOMOC feature supports only Qt 4 and Qt 5"; - } + this->AutogenSettingsFile = this->DirInfo; + this->AutogenSettingsFile += "/AutogenOldSettings.cmake"; + } - if (err.empty()) { - AddDefinitionEscaped(makefile, "_qt_moc_executable", mocExec); - } else { - err += " (" + target->GetName() + ")"; - cmSystemTools::Error(err.c_str()); + // Autogen target FOLDER property + { + const char* folder = + makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER"); + if (folder == nullptr) { + folder = + makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER"); + } + // Inherit FOLDER property from target (#13688) + if (folder == nullptr) { + folder = SafeString(this->Target->Target->GetProperty("FOLDER")); + } + if (folder != nullptr) { + this->AutogenFolder = folder; } } -} -static void SetupAutoTargetUic(cmQtAutoGenDigest const& digest, - std::string const& config, - std::vector const& configs, - cmQtAutoGenSetup& setup) -{ - cmGeneratorTarget const* target = digest.Target; - cmMakefile* makefile = target->Target->GetMakefile(); + std::set autogenDependFiles; + std::set autogenDependTargets; + std::vector autogenProvides; - // Uic search paths + // Remove build directories on cleanup + AddCleanFile(makefile, this->DirBuild); + // Remove old settings on cleanup { - std::vector uicSearchPaths; - { - std::string const usp = GetSafeProperty(target, "AUTOUIC_SEARCH_PATHS"); - if (!usp.empty()) { - cmSystemTools::ExpandListArgument(usp, uicSearchPaths); - std::string const srcDir = makefile->GetCurrentSourceDirectory(); - for (std::string& path : uicSearchPaths) { - path = cmSystemTools::CollapseFullPath(path, srcDir); - } - } - } - AddDefinitionEscaped(makefile, "_uic_search_paths", uicSearchPaths); - } - // Uic target options - { - auto UicGetOpts = [target](std::string const& cfg) -> std::string { - std::vector opts; - target->GetAutoUicOptions(opts, cfg); - return cmJoin(opts, ";"); - }; - - // Default settings - std::string const uicOpts = UicGetOpts(config); - AddDefinitionEscaped(makefile, "_uic_target_options", uicOpts); - - // Configuration specific settings - for (std::string const& cfg : configs) { - std::string const configUicOpts = UicGetOpts(cfg); - if (configUicOpts != uicOpts) { - setup.ConfigUicOptions[cfg] = configUicOpts; - } - } - } - // .ui files skip and options - { - std::vector uiFileFiles; - std::vector> uiFileOptions; - { - std::string const uiExt = "ui"; - for (cmSourceFile* sf : makefile->GetSourceFiles()) { - // sf->GetExtension() is only valid after sf->GetFullPath() ... - std::string const& fPath = sf->GetFullPath(); - if (sf->GetExtension() == uiExt) { - std::string const absFile = cmSystemTools::GetRealPath(fPath); - // Check if the file should be skipped - if (sf->GetPropertyAsBool("SKIP_AUTOUIC") || - sf->GetPropertyAsBool("SKIP_AUTOGEN")) { - setup.UicSkip.insert(absFile); - } - // Check if the files has uic options - std::string const uicOpts = GetSafeProperty(sf, "AUTOUIC_OPTIONS"); - if (!uicOpts.empty()) { - // Check if file isn't skipped - if (setup.UicSkip.count(absFile) == 0) { - uiFileFiles.push_back(absFile); - std::vector optsVec; - cmSystemTools::ExpandListArgument(uicOpts, optsVec); - uiFileOptions.push_back(std::move(optsVec)); - } - } - } - } - } - AddDefinitionEscaped(makefile, "_qt_uic_options_files", uiFileFiles); - AddDefinitionEscaped(makefile, "_qt_uic_options_options", uiFileOptions); - } - - AddDefinitionEscaped(makefile, "_uic_skip", setup.UicSkip); - - // Uic executable - { - std::string err; - std::string uicExec; - - cmLocalGenerator* localGen = target->GetLocalGenerator(); - if (digest.QtVersionMajor == "5") { - cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::uic"); - if (tgt != nullptr) { - uicExec = SafeString(tgt->ImportedGetLocation("")); - } else { - // Project does not use Qt5Widgets, but has AUTOUIC ON anyway - } - } else if (digest.QtVersionMajor == "4") { - cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::uic"); - if (tgt != nullptr) { - uicExec = SafeString(tgt->ImportedGetLocation("")); - } else { - err = "AUTOUIC: Qt4::uic target not found"; - } - } else { - err = "The AUTOUIC feature supports only Qt 4 and Qt 5"; - } - - if (err.empty()) { - AddDefinitionEscaped(makefile, "_qt_uic_executable", uicExec); - } else { - err += " (" + target->GetName() + ")"; - cmSystemTools::Error(err.c_str()); - } - } -} - -static std::string RccGetExecutable(cmGeneratorTarget const* target, - std::string const& qtMajorVersion) -{ - std::string rccExec; - std::string err; - - cmLocalGenerator* localGen = target->GetLocalGenerator(); - if (qtMajorVersion == "5") { - cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::rcc"); - if (tgt != nullptr) { - rccExec = SafeString(tgt->ImportedGetLocation("")); - } else { - err = "AUTORCC: Qt5::rcc target not found"; - } - } else if (qtMajorVersion == "4") { - cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::rcc"); - if (tgt != nullptr) { - rccExec = SafeString(tgt->ImportedGetLocation("")); - } else { - err = "AUTORCC: Qt4::rcc target not found"; - } - } else { - err = "The AUTORCC feature supports only Qt 4 and Qt 5"; - } - - if (!err.empty()) { - err += " (" + target->GetName() + ")"; - cmSystemTools::Error(err.c_str()); - } - return rccExec; -} - -void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( - cmQtAutoGenDigest& digest) -{ - cmGeneratorTarget* target = digest.Target; - cmMakefile* makefile = target->Target->GetMakefile(); - cmLocalGenerator* localGen = target->GetLocalGenerator(); - cmGlobalGenerator* globalGen = localGen->GetGlobalGenerator(); - - std::string const autogenTargetName = GetAutogenTargetName(target); - std::string const autogenInfoDir = GetAutogenTargetFilesDir(target); - std::string const autogenBuildDir = GetAutogenTargetBuildDir(target); - std::string const workingDirectory = - cmSystemTools::CollapseFullPath("", makefile->GetCurrentBinaryDirectory()); - - cmQtAutoGen::MultiConfig const multiConfig = AutogenMultiConfig(globalGen); - std::string configDefault; - std::vector configsList; - GetConfigs(makefile, configDefault, configsList); - - std::set autogenDependFiles; - std::set autogenDependTargets; - std::vector autogenProvides; - - // Autogen target FOLDER property - std::string autogenFolder; - { - const char* folder = - makefile->GetState()->GetGlobalProperty("AUTOMOC_TARGETS_FOLDER"); - if (folder == nullptr) { - folder = - makefile->GetState()->GetGlobalProperty("AUTOGEN_TARGETS_FOLDER"); - } - // Inherit FOLDER property from target (#13688) - if (folder == nullptr) { - folder = SafeString(target->Target->GetProperty("FOLDER")); - } - if (folder != nullptr) { - autogenFolder = folder; - } - } - - // Remove build directories on cleanup - AddCleanFile(makefile, autogenBuildDir); - // Remove old settings on cleanup - { - std::string base = autogenInfoDir + "/AutogenOldSettings"; - if (multiConfig == cmQtAutoGen::SINGLE) { - AddCleanFile(makefile, base.append(".cmake")); - } else { - for (std::string const& cfg : configsList) { - std::string filename = base; - filename += "_"; - filename += cfg; - filename += ".cmake"; - AddCleanFile(makefile, filename); + std::string base = this->DirInfo; + base += "/AutogenOldSettings"; + if (this->MultiConfig == cmQtAutoGen::SINGLE) { + AddCleanFile(makefile, base.append(".cmake")); + } else { + for (std::string const& cfg : this->ConfigsList) { + std::string filename = base; + filename += "_"; + filename += cfg; + filename += ".cmake"; + AddCleanFile(makefile, filename); } } } // Add moc compilation to generated files list - if (digest.MocEnabled) { - std::string const mocsComp = autogenBuildDir + "/mocs_compilation.cpp"; - auto files = AddGeneratedSource(target, mocsComp, multiConfig, configsList, - cmQtAutoGen::MOC); + if (this->MocEnabled) { + std::string const mocsComp = this->DirBuild + "/mocs_compilation.cpp"; + auto files = this->AddGeneratedSource(mocsComp, cmQtAutoGen::MOC); for (std::string& file : files) { autogenProvides.push_back(std::move(file)); } } // Add autogen includes directory to the origin target INCLUDE_DIRECTORIES - if (digest.MocEnabled || digest.UicEnabled) { - std::string includeDir = autogenBuildDir + "/include"; - if (multiConfig != cmQtAutoGen::SINGLE) { + if (this->MocEnabled || this->UicEnabled) { + std::string includeDir = this->DirBuild + "/include"; + if (this->MultiConfig != cmQtAutoGen::SINGLE) { includeDir += "_$"; } - target->AddIncludeDirectory(includeDir, true); + this->Target->AddIncludeDirectory(includeDir, true); } // Extract relevant source files @@ -719,7 +374,7 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( { std::string const qrcExt = "qrc"; std::vector srcFiles; - target->GetConfigCommonSourceFiles(srcFiles); + this->Target->GetConfigCommonSourceFiles(srcFiles); for (cmSourceFile* sf : srcFiles) { if (sf->GetPropertyAsBool("SKIP_AUTOGEN")) { continue; @@ -728,50 +383,50 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( std::string const& fPath = sf->GetFullPath(); std::string const& ext = sf->GetExtension(); // Register generated files that will be scanned by moc or uic - if (digest.MocEnabled || digest.UicEnabled) { + if (this->MocEnabled || this->UicEnabled) { cmSystemTools::FileFormat const fileType = cmSystemTools::GetFileFormat(ext.c_str()); if ((fileType == cmSystemTools::CXX_FILE_FORMAT) || (fileType == cmSystemTools::HEADER_FILE_FORMAT)) { std::string const absPath = cmSystemTools::GetRealPath(fPath); - if ((digest.MocEnabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) || - (digest.UicEnabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) { + if ((this->MocEnabled && !sf->GetPropertyAsBool("SKIP_AUTOMOC")) || + (this->UicEnabled && !sf->GetPropertyAsBool("SKIP_AUTOUIC"))) { // Register source const bool generated = sf->GetPropertyAsBool("GENERATED"); if (fileType == cmSystemTools::HEADER_FILE_FORMAT) { if (generated) { generatedHeaders.push_back(absPath); } else { - digest.Headers.push_back(absPath); + this->Headers.push_back(absPath); } } else { if (generated) { generatedSources.push_back(absPath); } else { - digest.Sources.push_back(absPath); + this->Sources.push_back(absPath); } } } } } // Register rcc enabled files - if (digest.RccEnabled && (ext == qrcExt) && + if (this->RccEnabled && (ext == qrcExt) && !sf->GetPropertyAsBool("SKIP_AUTORCC")) { // Register qrc file { - cmQtAutoGenDigestQrc qrcDigest; - qrcDigest.QrcFile = cmSystemTools::GetRealPath(fPath); - qrcDigest.QrcName = - cmSystemTools::GetFilenameWithoutLastExtension(qrcDigest.QrcFile); - qrcDigest.Generated = sf->GetPropertyAsBool("GENERATED"); + Qrc qrc; + qrc.QrcFile = cmSystemTools::GetRealPath(fPath); + qrc.QrcName = + cmSystemTools::GetFilenameWithoutLastExtension(qrc.QrcFile); + qrc.Generated = sf->GetPropertyAsBool("GENERATED"); // RCC options { std::string const opts = GetSafeProperty(sf, "AUTORCC_OPTIONS"); if (!opts.empty()) { - cmSystemTools::ExpandListArgument(opts, qrcDigest.Options); + cmSystemTools::ExpandListArgument(opts, qrc.Options); } } - digest.Qrcs.push_back(std::move(qrcDigest)); + this->Qrcs.push_back(std::move(qrc)); } } } @@ -779,7 +434,35 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( // 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. - target->ClearSourcesCache(); + this->Target->ClearSourcesCache(); + } + // Read skip files from makefile sources + if (this->MocEnabled || this->UicEnabled) { + const std::vector& allSources = makefile->GetSourceFiles(); + for (cmSourceFile* sf : allSources) { + // sf->GetExtension() is only valid after sf->GetFullPath() ... + std::string const& fPath = sf->GetFullPath(); + cmSystemTools::FileFormat const fileType = + cmSystemTools::GetFileFormat(sf->GetExtension().c_str()); + if (!(fileType == cmSystemTools::CXX_FILE_FORMAT) && + !(fileType == cmSystemTools::HEADER_FILE_FORMAT)) { + continue; + } + const bool skipAll = sf->GetPropertyAsBool("SKIP_AUTOGEN"); + const bool mocSkip = + this->MocEnabled && (skipAll || sf->GetPropertyAsBool("SKIP_AUTOMOC")); + const bool uicSkip = + this->UicEnabled && (skipAll || sf->GetPropertyAsBool("SKIP_AUTOUIC")); + if (mocSkip || uicSkip) { + std::string const absFile = cmSystemTools::GetRealPath(fPath); + if (mocSkip) { + this->MocSkip.insert(absFile); + } + if (uicSkip) { + this->UicSkip.insert(absFile); + } + } + } } // Process GENERATED sources and headers @@ -788,7 +471,7 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( bool policyAccept = false; bool policyWarn = false; cmPolicies::PolicyStatus const CMP0071_status = - target->Makefile->GetPolicyStatus(cmPolicies::CMP0071); + makefile->GetPolicyStatus(cmPolicies::CMP0071); switch (CMP0071_status) { case cmPolicies::WARN: policyWarn = true; @@ -807,11 +490,11 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( if (policyAccept) { // Accept GENERATED sources for (std::string const& absFile : generatedHeaders) { - digest.Headers.push_back(absFile); + this->Headers.push_back(absFile); autogenDependFiles.insert(absFile); } for (std::string const& absFile : generatedSources) { - digest.Sources.push_back(absFile); + this->Sources.push_back(absFile); autogenDependFiles.insert(absFile); } } else { @@ -821,13 +504,13 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( msg += "\n"; std::string tools; std::string property; - if (digest.MocEnabled && digest.UicEnabled) { + if (this->MocEnabled && this->UicEnabled) { tools = "AUTOMOC and AUTOUIC"; property = "SKIP_AUTOGEN"; - } else if (digest.MocEnabled) { + } else if (this->MocEnabled) { tools = "AUTOMOC"; property = "SKIP_AUTOMOC"; - } else if (digest.UicEnabled) { + } else if (this->UicEnabled) { tools = "AUTOUIC"; property = "SKIP_AUTOUIC"; } @@ -852,27 +535,32 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( makefile->IssueMessage(cmake::AUTHOR_WARNING, msg); } } + // Clear lists + generatedSources.clear(); + generatedHeaders.clear(); } // Sort headers and sources - std::sort(digest.Headers.begin(), digest.Headers.end()); - std::sort(digest.Sources.begin(), digest.Sources.end()); + if (this->MocEnabled || this->UicEnabled) { + std::sort(this->Headers.begin(), this->Headers.end()); + std::sort(this->Sources.begin(), this->Sources.end()); + } // Process qrc files - if (!digest.Qrcs.empty()) { - const bool QtV5 = (digest.QtVersionMajor == "5"); - std::string const rcc = RccGetExecutable(target, digest.QtVersionMajor); + if (!this->Qrcs.empty()) { + const bool QtV5 = (this->QtVersionMajor == "5"); + std::string const rcc = + RccGetExecutable(this->Target, this->QtVersionMajor); // Target rcc options std::vector optionsTarget; cmSystemTools::ExpandListArgument( - GetSafeProperty(target, "AUTORCC_OPTIONS"), optionsTarget); + GetSafeProperty(this->Target, "AUTORCC_OPTIONS"), optionsTarget); // Check if file name is unique - for (cmQtAutoGenDigestQrc& qrcDigest : digest.Qrcs) { - qrcDigest.Unique = true; - for (cmQtAutoGenDigestQrc const& qrcDig2 : digest.Qrcs) { - if ((&qrcDigest != &qrcDig2) && - (qrcDigest.QrcName == qrcDig2.QrcName)) { - qrcDigest.Unique = false; + for (Qrc& qrc : this->Qrcs) { + qrc.Unique = true; + for (Qrc const& qrc2 : this->Qrcs) { + if ((&qrc != &qrc2) && (qrc.QrcName == qrc2.QrcName)) { + qrc.Unique = false; break; } } @@ -880,41 +568,43 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( // Path checksum and file names { cmFilePathChecksum const fpathCheckSum(makefile); - for (cmQtAutoGenDigestQrc& qrcDigest : digest.Qrcs) { - qrcDigest.PathChecksum = fpathCheckSum.getPart(qrcDigest.QrcFile); + for (Qrc& qrc : this->Qrcs) { + qrc.PathChecksum = fpathCheckSum.getPart(qrc.QrcFile); // RCC output file name { - std::string rccFile = autogenBuildDir + "/"; - rccFile += qrcDigest.PathChecksum; + std::string rccFile = this->DirBuild + "/"; + rccFile += qrc.PathChecksum; rccFile += "/qrc_"; - rccFile += qrcDigest.QrcName; + rccFile += qrc.QrcName; rccFile += ".cpp"; - qrcDigest.RccFile = std::move(rccFile); + qrc.RccFile = std::move(rccFile); } { - std::string base = autogenInfoDir; + std::string base = this->DirInfo; base += "/RCC"; - base += qrcDigest.QrcName; - if (!qrcDigest.Unique) { - base += qrcDigest.PathChecksum; + base += qrc.QrcName; + if (!qrc.Unique) { + base += qrc.PathChecksum; } - qrcDigest.InfoFile = base + "Info.cmake"; - qrcDigest.SettingsFile = base + "Settings.cmake"; + qrc.InfoFile = base; + qrc.InfoFile += "Info.cmake"; + qrc.SettingsFile = base; + qrc.SettingsFile += "Settings.cmake"; } } } // RCC options - for (cmQtAutoGenDigestQrc& qrcDigest : digest.Qrcs) { + for (Qrc& qrc : this->Qrcs) { // Target options std::vector opts = optionsTarget; // Merge computed "-name XYZ" option { - std::string name = qrcDigest.QrcName; + std::string name = qrc.QrcName; // Replace '-' with '_'. The former is not valid for symbol names. std::replace(name.begin(), name.end(), '-', '_'); - if (!qrcDigest.Unique) { + if (!qrc.Unique) { name += "_"; - name += qrcDigest.PathChecksum; + name += qrc.PathChecksum; } std::vector nameOpts; nameOpts.emplace_back("-name"); @@ -922,13 +612,13 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( cmQtAutoGen::RccMergeOptions(opts, nameOpts, QtV5); } // Merge file option - cmQtAutoGen::RccMergeOptions(opts, qrcDigest.Options, QtV5); - qrcDigest.Options = std::move(opts); + cmQtAutoGen::RccMergeOptions(opts, qrc.Options, QtV5); + qrc.Options = std::move(opts); } - for (cmQtAutoGenDigestQrc& qrcDigest : digest.Qrcs) { + for (Qrc& qrc : this->Qrcs) { // Register file at target - std::vector const ccOutput = AddGeneratedSource( - target, qrcDigest.RccFile, multiConfig, configsList, cmQtAutoGen::RCC); + std::vector const ccOutput = + this->AddGeneratedSource(qrc.RccFile, cmQtAutoGen::RCC); cmCustomCommandLines commandLines; { @@ -936,57 +626,57 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( currentLine.push_back(cmSystemTools::GetCMakeCommand()); currentLine.push_back("-E"); currentLine.push_back("cmake_autorcc"); - currentLine.push_back(qrcDigest.InfoFile); + currentLine.push_back(qrc.InfoFile); currentLine.push_back("$"); commandLines.push_back(std::move(currentLine)); } std::string ccComment = "Automatic RCC for "; - ccComment += qrcDigest.QrcFile; + ccComment += qrc.QrcFile; - if (qrcDigest.Generated) { + if (qrc.Generated) { // Create custom rcc target std::string ccName; { - ccName = target->GetName(); + ccName = this->Target->GetName(); ccName += "_arcc_"; - ccName += qrcDigest.QrcName; - if (!qrcDigest.Unique) { + ccName += qrc.QrcName; + if (!qrc.Unique) { ccName += "_"; - ccName += qrcDigest.PathChecksum; + ccName += qrc.PathChecksum; } std::vector ccDepends; // Add the .qrc file to the custom target dependencies - ccDepends.push_back(qrcDigest.QrcFile); + ccDepends.push_back(qrc.QrcFile); cmTarget* autoRccTarget = makefile->AddUtilityCommand( - ccName, true, workingDirectory.c_str(), ccOutput, ccDepends, + ccName, true, this->DirWork.c_str(), ccOutput, ccDepends, commandLines, false, ccComment.c_str()); // Create autogen generator target localGen->AddGeneratorTarget( new cmGeneratorTarget(autoRccTarget, localGen)); // Set FOLDER property in autogen target - if (!autogenFolder.empty()) { - autoRccTarget->SetProperty("FOLDER", autogenFolder.c_str()); + if (!this->AutogenFolder.empty()) { + autoRccTarget->SetProperty("FOLDER", this->AutogenFolder.c_str()); } } // Add autogen target to the origin target dependencies - target->Target->AddUtility(ccName, makefile); + this->Target->Target->AddUtility(ccName, makefile); } else { // Create custom rcc command { std::vector ccByproducts; std::vector ccDepends; // Add the .qrc file to the custom command dependencies - ccDepends.push_back(qrcDigest.QrcFile); + ccDepends.push_back(qrc.QrcFile); // Add the resource files to the dependencies { std::string error; - if (cmQtAutoGen::RccListInputs(digest.QtVersionMajor, rcc, - qrcDigest.QrcFile, - qrcDigest.Resources, &error)) { - for (std::string const& fileName : qrcDigest.Resources) { + if (cmQtAutoGen::RccListInputs(this->QtVersionMajor, rcc, + qrc.QrcFile, qrc.Resources, + &error)) { + for (std::string const& fileName : qrc.Resources) { // Add resource file to the custom command dependencies ccDepends.push_back(fileName); } @@ -997,20 +687,20 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( makefile->AddCustomCommandToOutput(ccOutput, ccByproducts, ccDepends, /*main_dependency*/ std::string(), commandLines, ccComment.c_str(), - workingDirectory.c_str()); + this->DirWork.c_str()); } // Reconfigure when .qrc file changes - makefile->AddCMakeDependFile(qrcDigest.QrcFile); + makefile->AddCMakeDependFile(qrc.QrcFile); } } } // Create _autogen target - if (digest.MocEnabled || digest.UicEnabled) { + if (this->MocEnabled || this->UicEnabled) { // Add user defined autogen target dependencies { std::string const deps = - GetSafeProperty(target, "AUTOGEN_TARGET_DEPENDS"); + GetSafeProperty(this->Target, "AUTOGEN_TARGET_DEPENDS"); if (!deps.empty()) { std::vector extraDeps; cmSystemTools::ExpandListArgument(deps, extraDeps); @@ -1029,29 +719,20 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( // Compose target comment std::string autogenComment; { - std::vector toolNames; - if (digest.MocEnabled) { - toolNames.emplace_back("MOC"); - } - if (digest.UicEnabled) { - toolNames.emplace_back("UIC"); + std::string tools; + if (this->MocEnabled) { + tools += "MOC"; } - if (digest.RccEnabled) { - toolNames.emplace_back("RCC"); - } - - std::string tools = toolNames.front(); - toolNames.erase(toolNames.begin()); - if (!toolNames.empty()) { - while (toolNames.size() > 1) { - tools += ", "; - tools += toolNames.front(); - toolNames.erase(toolNames.begin()); + if (this->UicEnabled) { + if (!tools.empty()) { + tools += " and "; } - tools += " and " + toolNames.front(); + tools += "UIC"; } - autogenComment = - "Automatic " + tools + " for target " + target->GetName(); + autogenComment = "Automatic "; + autogenComment += tools; + autogenComment += " for target "; + autogenComment += this->Target->GetName(); } // Compose command lines @@ -1061,7 +742,7 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( currentLine.push_back(cmSystemTools::GetCMakeCommand()); currentLine.push_back("-E"); currentLine.push_back("cmake_autogen"); - currentLine.push_back(autogenInfoDir + "/AutogenInfo.cmake"); + currentLine.push_back(this->AutogenInfoFile); currentLine.push_back("$"); commandLines.push_back(std::move(currentLine)); } @@ -1086,7 +767,7 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( if (usePRE_BUILD) { // Add additional autogen target dependencies to origin target for (cmTarget* depTarget : autogenDependTargets) { - target->Target->AddUtility(depTarget->GetName(), makefile); + this->Target->Target->AddUtility(depTarget->GetName(), makefile); } // Add the pre-build command directly to bypass the OBJECT_LIBRARY @@ -1098,10 +779,10 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( const std::vector no_deps; cmCustomCommand cc(makefile, no_output, autogenProvides, no_deps, commandLines, autogenComment.c_str(), - workingDirectory.c_str()); + this->DirWork.c_str()); cc.SetEscapeOldStyle(false); cc.SetEscapeAllowMakeVars(true); - target->Target->AddPreBuildCommand(cc); + this->Target->Target->AddPreBuildCommand(cc); } else { // Convert file dependencies std::set to std::vector @@ -1110,22 +791,22 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( // Add link library target dependencies to the autogen target // dependencies - for (std::string const& config : configsList) { + for (std::string const& config : this->ConfigsList) { cmLinkImplementationLibraries const* libs = - target->GetLinkImplementationLibraries(config); + this->Target->GetLinkImplementationLibraries(config); if (libs != nullptr) { for (cmLinkItem const& item : libs->Libraries) { cmGeneratorTarget const* libTarget = item.Target; if ((libTarget != nullptr) && - !StaticLibraryCycle(target, libTarget, config)) { + !StaticLibraryCycle(this->Target, libTarget, config)) { std::string util; - if (configsList.size() > 1) { + if (this->ConfigsList.size() > 1) { util += "$<$GetName(); - if (configsList.size() > 1) { + if (this->ConfigsList.size() > 1) { util += ">"; } autogenDepends.push_back(util); @@ -1136,7 +817,7 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( // Create autogen target cmTarget* autogenTarget = makefile->AddUtilityCommand( - autogenTargetName, true, workingDirectory.c_str(), + this->AutogenTargetName, true, this->DirWork.c_str(), /*byproducts=*/autogenProvides, autogenDepends, commandLines, false, autogenComment.c_str()); // Create autogen generator target @@ -1144,7 +825,7 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( new cmGeneratorTarget(autogenTarget, localGen)); // Forward origin utilities to autogen target - for (std::string const& depName : target->Target->GetUtilities()) { + for (std::string const& depName : this->Target->Target->GetUtilities()) { autogenTarget->AddUtility(depName, makefile); } // Add additional autogen target dependencies to autogen target @@ -1153,87 +834,66 @@ void cmQtAutoGeneratorInitializer::InitializeAutogenTarget( } // Set FOLDER property in autogen target - if (!autogenFolder.empty()) { - autogenTarget->SetProperty("FOLDER", autogenFolder.c_str()); + if (!this->AutogenFolder.empty()) { + autogenTarget->SetProperty("FOLDER", this->AutogenFolder.c_str()); } // Add autogen target to the origin target dependencies - target->Target->AddUtility(autogenTargetName, makefile); + this->Target->Target->AddUtility(this->AutogenTargetName, makefile); } } } -void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( - cmQtAutoGenDigest const& digest) +void cmQtAutoGeneratorInitializer::SetupCustomTargets() { - cmGeneratorTarget const* target = digest.Target; - cmMakefile* makefile = target->Target->GetMakefile(); - cmQtAutoGen::MultiConfig const multiConfig = - AutogenMultiConfig(target->GetGlobalGenerator()); + cmMakefile* makefile = this->Target->Target->GetMakefile(); // forget the variables added here afterwards again: cmMakefile::ScopePushPop varScope(makefile); static_cast(varScope); - // Configurations - std::string configDefault; - std::vector configsList; + // Configuration suffixes std::map configSuffixes; - { - configDefault = makefile->GetConfigurations(configsList); - if (configsList.empty()) { - configsList.push_back(""); - } - } - for (std::string const& cfg : configsList) { - configSuffixes[cfg] = "_" + cfg; + for (std::string const& cfg : this->ConfigsList) { + std::string& suffix = configSuffixes[cfg]; + suffix = "_"; + suffix += cfg; } - // Configurations settings buffers - cmQtAutoGenSetup setup; - // Basic setup AddDefinitionEscaped(makefile, "_multi_config", - cmQtAutoGen::MultiConfigName(multiConfig)); - AddDefinitionEscaped(makefile, "_build_dir", - GetAutogenTargetBuildDir(target)); - AddDefinitionEscaped(makefile, "_qt_version_major", digest.QtVersionMajor); - AddDefinitionEscaped(makefile, "_qt_version_minor", digest.QtVersionMinor); - { - if (digest.MocEnabled || digest.UicEnabled) { - { - std::string settingsFile = GetAutogenTargetFilesDir(target); - cmSystemTools::ConvertToUnixSlashes(settingsFile); - settingsFile += "/AutogenOldSettings.cmake"; - AddDefinitionEscaped(makefile, "_settings_file", settingsFile); - } - AddDefinitionEscaped(makefile, "_sources", digest.Sources); - AddDefinitionEscaped(makefile, "_headers", digest.Headers); - - SetupAcquireSkipFiles(digest, setup); - if (digest.MocEnabled) { - SetupAutoTargetMoc(digest, configDefault, configsList, setup); - } - if (digest.UicEnabled) { - SetupAutoTargetUic(digest, configDefault, configsList, setup); - } + cmQtAutoGen::MultiConfigName(this->MultiConfig)); + AddDefinitionEscaped(makefile, "_build_dir", this->DirBuild); + AddDefinitionEscaped(makefile, "_qt_version_major", this->QtVersionMajor); + AddDefinitionEscaped(makefile, "_qt_version_minor", this->QtVersionMinor); + + if (this->MocEnabled || this->UicEnabled) { + AddDefinitionEscaped(makefile, "_settings_file", + this->AutogenSettingsFile); + AddDefinitionEscaped(makefile, "_sources", this->Sources); + AddDefinitionEscaped(makefile, "_headers", this->Headers); + + if (this->MocEnabled) { + this->SetupCustomTargetsMoc(); } - if (digest.RccEnabled) { - AddDefinitionEscaped( - makefile, "_qt_rcc_executable", - RccGetExecutable(digest.Target, digest.QtVersionMajor)); + if (this->UicEnabled) { + this->SetupCustomTargetsUic(); } } + if (this->RccEnabled) { + AddDefinitionEscaped(makefile, "_qt_rcc_executable", + RccGetExecutable(this->Target, this->QtVersionMajor)); + } // Create info directory on demand - std::string const infoDir = GetAutogenTargetFilesDir(target); - if (!cmSystemTools::MakeDirectory(infoDir)) { + if (!cmSystemTools::MakeDirectory(this->DirInfo)) { std::string emsg = ("Could not create directory: "); - emsg += cmQtAutoGen::Quoted(infoDir); + emsg += cmQtAutoGen::Quoted(this->DirInfo); cmSystemTools::Error(emsg.c_str()); } - auto AdjustFilePermissions = [](std::string const& fileName) { + auto ReOpenInfoFile = [](cmsys::ofstream& ofs, + std::string const& fileName) -> bool { // Ensure we have write permission mode_t perm = 0; #if defined(_WIN32) && !defined(__CYGWIN__) @@ -1245,27 +905,31 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( if (!(perm & mode_write)) { cmSystemTools::SetPermissions(fileName, perm | mode_write); } + + ofs.open(fileName.c_str(), std::ios::app); + if (!ofs) { + // File open error + std::string error = "Internal CMake error when trying to open file: "; + error += cmQtAutoGen::Quoted(fileName); + error += " for writing."; + cmSystemTools::Error(error.c_str()); + } + return static_cast(ofs); }; // Generate autogen target info file - { - std::string const infoFile = infoDir + "/AutogenInfo.cmake"; + if (this->MocEnabled || this->UicEnabled) { { std::string infoFileIn = cmSystemTools::GetCMakeRoot(); infoFileIn += "/Modules/AutogenInfo.cmake.in"; - makefile->ConfigureFile(infoFileIn.c_str(), infoFile.c_str(), false, - true, false); + makefile->ConfigureFile( + infoFileIn.c_str(), this->AutogenInfoFile.c_str(), false, true, false); } // Append custom definitions to info file // -------------------------------------- - - // Ensure we have write permission in case .in was read-only. - AdjustFilePermissions(infoFile); - - // Open and write file - cmsys::ofstream ofs(infoFile.c_str(), std::ios::app); - if (ofs) { + cmsys::ofstream ofs; + if (ReOpenInfoFile(ofs, this->AutogenInfoFile)) { auto OfsWriteMap = [&ofs]( const char* key, std::map const& map) { for (auto const& item : map) { @@ -1275,36 +939,47 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( }; ofs << "# Configurations options\n"; OfsWriteMap("AM_CONFIG_SUFFIX", configSuffixes); - OfsWriteMap("AM_MOC_DEFINITIONS", setup.ConfigMocDefines); - OfsWriteMap("AM_MOC_INCLUDES", setup.ConfigMocIncludes); - OfsWriteMap("AM_UIC_TARGET_OPTIONS", setup.ConfigUicOptions); - } else { - // File open error - std::string error = "Internal CMake error when trying to open file: "; - error += cmQtAutoGen::Quoted(infoFile); - error += " for writing."; - cmSystemTools::Error(error.c_str()); + OfsWriteMap("AM_MOC_DEFINITIONS", this->ConfigMocDefines); + OfsWriteMap("AM_MOC_INCLUDES", this->ConfigMocIncludes); + OfsWriteMap("AM_UIC_TARGET_OPTIONS", this->ConfigUicOptions); + // Settings files (only require for multi configuration generators) + if (this->MultiConfig != cmQtAutoGen::SINGLE) { + std::map settingsFiles; + for (std::string const& cfg : this->ConfigsList) { + settingsFiles[cfg] = cmQtAutoGen::AppendFilenameSuffix( + this->AutogenSettingsFile, "_" + cfg); + } + OfsWriteMap("AM_SETTINGS_FILE", settingsFiles); + } } } // Generate auto RCC info files - { + if (this->RccEnabled) { std::string infoFileIn = cmSystemTools::GetCMakeRoot(); infoFileIn += "/Modules/AutoRccInfo.cmake.in"; - for (cmQtAutoGenDigestQrc const& qrcDigest : digest.Qrcs) { + for (Qrc const& qrc : this->Qrcs) { // Configure info file - makefile->ConfigureFile(infoFileIn.c_str(), qrcDigest.InfoFile.c_str(), - false, true, false); + makefile->ConfigureFile(infoFileIn.c_str(), qrc.InfoFile.c_str(), false, + true, false); // Append custom definitions to info file // -------------------------------------- + cmsys::ofstream ofs; + if (ReOpenInfoFile(ofs, qrc.InfoFile)) { + { + ofs << "# Job\n"; + auto OfsWrite = [&ofs](const char* key, std::string const& value) { + ofs << "set(" << key << " " + << cmOutputConverter::EscapeForCMake(value) << ")\n"; - // Ensure we have write permission in case .in was read-only. - AdjustFilePermissions(qrcDigest.InfoFile); - - // Open and write file - cmsys::ofstream ofs(qrcDigest.InfoFile.c_str(), std::ios::app); - if (ofs) { + }; + OfsWrite("ARCC_SETTINGS_FILE", qrc.SettingsFile); + OfsWrite("ARCC_SOURCE", qrc.QrcFile); + OfsWrite("ARCC_OUTPUT", qrc.RccFile); + OfsWrite("ARCC_OPTIONS", cmJoin(qrc.Options, ";")); + OfsWrite("ARCC_INPUTS", cmJoin(qrc.Resources, ";")); + } { ofs << "# Configurations options\n"; auto OfsWriteMap = [&ofs]( @@ -1315,27 +990,321 @@ void cmQtAutoGeneratorInitializer::SetupAutoGenerateTarget( } }; OfsWriteMap("ARCC_CONFIG_SUFFIX", configSuffixes); + + // Settings files (only require for multi configuration generators) + if (this->MultiConfig != cmQtAutoGen::SINGLE) { + std::map settingsFiles; + for (std::string const& cfg : this->ConfigsList) { + settingsFiles[cfg] = + cmQtAutoGen::AppendFilenameSuffix(qrc.SettingsFile, "_" + cfg); + } + OfsWriteMap("ARCC_SETTINGS_FILE", settingsFiles); + } } - { - ofs << "# Job\n"; - auto OfsWrite = [&ofs](const char* key, std::string const& value) { - ofs << "set(" << key << " " - << cmOutputConverter::EscapeForCMake(value) << ")\n"; + } else { + break; + } + } + } +} - }; - OfsWrite("ARCC_SETTINGS_FILE", qrcDigest.SettingsFile); - OfsWrite("ARCC_SOURCE", qrcDigest.QrcFile); - OfsWrite("ARCC_OUTPUT", qrcDigest.RccFile); - OfsWrite("ARCC_OPTIONS", cmJoin(qrcDigest.Options, ";")); - OfsWrite("ARCC_INPUTS", cmJoin(qrcDigest.Resources, ";")); +void cmQtAutoGeneratorInitializer::SetupCustomTargetsMoc() +{ + cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); + cmMakefile* makefile = this->Target->Target->GetMakefile(); + + AddDefinitionEscaped(makefile, "_moc_skip", this->MocSkip); + AddDefinitionEscaped(makefile, "_moc_options", + GetSafeProperty(this->Target, "AUTOMOC_MOC_OPTIONS")); + AddDefinitionEscaped(makefile, "_moc_relaxed_mode", + makefile->IsOn("CMAKE_AUTOMOC_RELAXED_MODE") ? "TRUE" + : "FALSE"); + AddDefinitionEscaped(makefile, "_moc_macro_names", + GetSafeProperty(this->Target, "AUTOMOC_MACRO_NAMES")); + AddDefinitionEscaped( + makefile, "_moc_depend_filters", + GetSafeProperty(this->Target, "AUTOMOC_DEPEND_FILTERS")); + + // Compiler predefines + if (this->Target->GetPropertyAsBool("AUTOMOC_COMPILER_PREDEFINES") && + this->QtVersionGreaterOrEqual(5, 8)) { + AddDefinitionEscaped( + makefile, "_moc_predefs_cmd", + makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_PREDEFINES_COMMAND")); + } + // Moc includes and compile definitions + { + auto GetIncludeDirs = [this, + localGen](std::string const& cfg) -> std::string { + // Get the include dirs for this target, without stripping the implicit + // include dirs off, see + // https://gitlab.kitware.com/cmake/cmake/issues/13667 + std::vector includeDirs; + localGen->GetIncludeDirectories(includeDirs, this->Target, "CXX", cfg, + false); + return cmJoin(includeDirs, ";"); + }; + auto GetCompileDefinitions = + [this, localGen](std::string const& cfg) -> std::string { + std::set defines; + localGen->AddCompileDefinitions(defines, this->Target, cfg, "CXX"); + return cmJoin(defines, ";"); + }; + + // Default configuration settings + std::string const includeDirs = GetIncludeDirs(this->ConfigDefault); + std::string const compileDefs = GetCompileDefinitions(this->ConfigDefault); + // Other configuration settings + for (std::string const& cfg : this->ConfigsList) { + { + std::string const configIncludeDirs = GetIncludeDirs(cfg); + if (configIncludeDirs != includeDirs) { + this->ConfigMocIncludes[cfg] = configIncludeDirs; + } + } + { + std::string const configCompileDefs = GetCompileDefinitions(cfg); + if (configCompileDefs != compileDefs) { + this->ConfigMocDefines[cfg] = configCompileDefs; + } + } + } + AddDefinitionEscaped(makefile, "_moc_include_dirs", includeDirs); + AddDefinitionEscaped(makefile, "_moc_compile_defs", compileDefs); + } + + // Moc executable + { + std::string mocExec; + std::string err; + + if (this->QtVersionMajor == "5") { + cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::moc"); + if (tgt != nullptr) { + mocExec = SafeString(tgt->ImportedGetLocation("")); + } else { + err = "AUTOMOC: Qt5::moc target not found"; + } + } else if (this->QtVersionMajor == "4") { + cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::moc"); + if (tgt != nullptr) { + mocExec = SafeString(tgt->ImportedGetLocation("")); + } else { + err = "AUTOMOC: Qt4::moc target not found"; + } + } else { + err = "The AUTOMOC feature supports only Qt 4 and Qt 5"; + } + + if (err.empty()) { + AddDefinitionEscaped(makefile, "_qt_moc_executable", mocExec); + } else { + err += " ("; + err += this->Target->GetName(); + err += ")"; + cmSystemTools::Error(err.c_str()); + } + } +} + +void cmQtAutoGeneratorInitializer::SetupCustomTargetsUic() +{ + cmMakefile* makefile = this->Target->Target->GetMakefile(); + + // Uic search paths + { + std::vector uicSearchPaths; + { + std::string const usp = + GetSafeProperty(this->Target, "AUTOUIC_SEARCH_PATHS"); + if (!usp.empty()) { + cmSystemTools::ExpandListArgument(usp, uicSearchPaths); + std::string const srcDir = makefile->GetCurrentSourceDirectory(); + for (std::string& path : uicSearchPaths) { + path = cmSystemTools::CollapseFullPath(path, srcDir); + } + } + } + AddDefinitionEscaped(makefile, "_uic_search_paths", uicSearchPaths); + } + // Uic target options + { + auto UicGetOpts = [this](std::string const& cfg) -> std::string { + std::vector opts; + this->Target->GetAutoUicOptions(opts, cfg); + return cmJoin(opts, ";"); + }; + + // Default settings + std::string const uicOpts = UicGetOpts(this->ConfigDefault); + AddDefinitionEscaped(makefile, "_uic_target_options", uicOpts); + + // Configuration specific settings + for (std::string const& cfg : this->ConfigsList) { + std::string const configUicOpts = UicGetOpts(cfg); + if (configUicOpts != uicOpts) { + this->ConfigUicOptions[cfg] = configUicOpts; + } + } + } + // .ui files skip and options + { + std::vector uiFileFiles; + std::vector> uiFileOptions; + { + std::string const uiExt = "ui"; + for (cmSourceFile* sf : makefile->GetSourceFiles()) { + // sf->GetExtension() is only valid after sf->GetFullPath() ... + std::string const& fPath = sf->GetFullPath(); + if (sf->GetExtension() == uiExt) { + std::string const absFile = cmSystemTools::GetRealPath(fPath); + // Check if the .ui file should be skipped + if (sf->GetPropertyAsBool("SKIP_AUTOUIC") || + sf->GetPropertyAsBool("SKIP_AUTOGEN")) { + this->UicSkip.insert(absFile); + } + // Check if the .ui file has uic options + std::string const uicOpts = GetSafeProperty(sf, "AUTOUIC_OPTIONS"); + if (!uicOpts.empty()) { + // Check if file isn't skipped + if (this->UicSkip.count(absFile) == 0) { + uiFileFiles.push_back(absFile); + std::vector optsVec; + cmSystemTools::ExpandListArgument(uicOpts, optsVec); + uiFileOptions.push_back(std::move(optsVec)); + } + } } + } + } + AddDefinitionEscaped(makefile, "_qt_uic_options_files", uiFileFiles); + AddDefinitionEscaped(makefile, "_qt_uic_options_options", uiFileOptions); + } + + AddDefinitionEscaped(makefile, "_uic_skip", this->UicSkip); + + // Uic executable + { + std::string err; + std::string uicExec; + + cmLocalGenerator* localGen = this->Target->GetLocalGenerator(); + if (this->QtVersionMajor == "5") { + cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::uic"); + if (tgt != nullptr) { + uicExec = SafeString(tgt->ImportedGetLocation("")); + } else { + // Project does not use Qt5Widgets, but has AUTOUIC ON anyway + } + } else if (this->QtVersionMajor == "4") { + cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::uic"); + if (tgt != nullptr) { + uicExec = SafeString(tgt->ImportedGetLocation("")); } else { - // File open error - std::string error = "Internal CMake error when trying to open file: "; - error += cmQtAutoGen::Quoted(qrcDigest.InfoFile); - error += " for writing."; - cmSystemTools::Error(error.c_str()); + err = "AUTOUIC: Qt4::uic target not found"; } + } else { + err = "The AUTOUIC feature supports only Qt 4 and Qt 5"; + } + + if (err.empty()) { + AddDefinitionEscaped(makefile, "_qt_uic_executable", uicExec); + } else { + err += " ("; + err += this->Target->GetName(); + err += ")"; + cmSystemTools::Error(err.c_str()); + } + } +} + +std::vector cmQtAutoGeneratorInitializer::AddGeneratedSource( + std::string const& filename, cmQtAutoGen::Generator genType) +{ + std::vector genFiles; + // Register source file in makefile and source group + if (this->MultiConfig != cmQtAutoGen::FULL) { + genFiles.push_back(filename); + } else { + for (std::string const& cfg : this->ConfigsList) { + genFiles.push_back( + cmQtAutoGen::AppendFilenameSuffix(filename, "_" + cfg)); + } + } + { + cmMakefile* makefile = this->Target->Target->GetMakefile(); + for (std::string const& genFile : genFiles) { + { + cmSourceFile* gFile = makefile->GetOrCreateSource(genFile, true); + gFile->SetProperty("GENERATED", "1"); + gFile->SetProperty("SKIP_AUTOGEN", "On"); + } + AddToSourceGroup(makefile, genFile, genType); } } + + // Add source file to target + if (this->MultiConfig != cmQtAutoGen::FULL) { + this->Target->AddSource(filename); + } else { + for (std::string const& cfg : this->ConfigsList) { + std::string src = "$<$Target->AddSource(src); + } + } + + return genFiles; +} + +std::string cmQtAutoGeneratorInitializer::GetQtMajorVersion( + cmGeneratorTarget const* target) +{ + cmMakefile* makefile = target->Target->GetMakefile(); + std::string qtMajor = makefile->GetSafeDefinition("QT_VERSION_MAJOR"); + if (qtMajor.empty()) { + qtMajor = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR"); + } + const char* targetQtVersion = + target->GetLinkInterfaceDependentStringProperty("QT_MAJOR_VERSION", ""); + if (targetQtVersion != nullptr) { + qtMajor = targetQtVersion; + } + return qtMajor; +} + +std::string cmQtAutoGeneratorInitializer::GetQtMinorVersion( + cmGeneratorTarget const* target, std::string const& qtVersionMajor) +{ + cmMakefile* makefile = target->Target->GetMakefile(); + std::string qtMinor; + if (qtVersionMajor == "5") { + qtMinor = makefile->GetSafeDefinition("Qt5Core_VERSION_MINOR"); + } + if (qtMinor.empty()) { + qtMinor = makefile->GetSafeDefinition("QT_VERSION_MINOR"); + } + + const char* targetQtVersion = + target->GetLinkInterfaceDependentStringProperty("QT_MINOR_VERSION", ""); + if (targetQtVersion != nullptr) { + qtMinor = targetQtVersion; + } + return qtMinor; +} + +bool cmQtAutoGeneratorInitializer::QtVersionGreaterOrEqual( + unsigned long requestMajor, unsigned long requestMinor) const +{ + unsigned long majorUL(0); + unsigned long minorUL(0); + if (cmSystemTools::StringToULong(this->QtVersionMajor.c_str(), &majorUL) && + cmSystemTools::StringToULong(this->QtVersionMinor.c_str(), &minorUL)) { + return (majorUL > requestMajor) || + (majorUL == requestMajor && minorUL >= requestMinor); + } + return false; } diff --git a/Source/cmQtAutoGeneratorInitializer.h b/Source/cmQtAutoGeneratorInitializer.h index b8a5ae4..9eebbd6 100644 --- a/Source/cmQtAutoGeneratorInitializer.h +++ b/Source/cmQtAutoGeneratorInitializer.h @@ -4,9 +4,12 @@ #define cmQtAutoGeneratorInitializer_h #include "cmConfigure.h" // IWYU pragma: keep -#include "cmQtAutoGenDigest.h" +#include "cmQtAutoGen.h" +#include +#include #include +#include class cmGeneratorTarget; @@ -17,8 +20,75 @@ public: static std::string GetQtMinorVersion(cmGeneratorTarget const* target, std::string const& qtVersionMajor); - static void InitializeAutogenTarget(cmQtAutoGenDigest& digest); - static void SetupAutoGenerateTarget(cmQtAutoGenDigest const& digest); + class Qrc + { + public: + Qrc() + : Generated(false) + , Unique(false) + { + } + + public: + std::string QrcFile; + std::string QrcName; + std::string PathChecksum; + std::string InfoFile; + std::string SettingsFile; + std::string RccFile; + bool Generated; + bool Unique; + std::vector Options; + std::vector Resources; + }; + +public: + cmQtAutoGeneratorInitializer(cmGeneratorTarget* target, bool mocEnabled, + bool uicEnabled, bool rccEnabled, + std::string const& qtVersionMajor); + + void InitCustomTargets(); + void SetupCustomTargets(); + +private: + void SetupCustomTargetsMoc(); + void SetupCustomTargetsUic(); + + std::vector AddGeneratedSource(std::string const& filename, + cmQtAutoGen::Generator genType); + + bool QtVersionGreaterOrEqual(unsigned long requestMajor, + unsigned long requestMinor) const; + +private: + cmGeneratorTarget* Target; + bool MocEnabled; + bool UicEnabled; + bool RccEnabled; + std::string QtVersionMajor; + std::string QtVersionMinor; + // Configurations + std::string ConfigDefault; + std::vector ConfigsList; + cmQtAutoGen::MultiConfig MultiConfig; + // Names + std::string AutogenTargetName; + std::string AutogenFolder; + std::string AutogenInfoFile; + std::string AutogenSettingsFile; + // Directories + std::string DirInfo; + std::string DirBuild; + std::string DirWork; + // Sources + std::vector Headers; + std::vector Sources; + std::set MocSkip; + std::set UicSkip; + std::map ConfigMocIncludes; + std::map ConfigMocDefines; + std::map ConfigUicOptions; + std::vector Qrcs; }; #endif diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx index ea99b1f..1956a89 100644 --- a/Source/cmQtAutoGeneratorMocUic.cxx +++ b/Source/cmQtAutoGeneratorMocUic.cxx @@ -142,14 +142,9 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) } this->SettingsFile = InfoGetConfig("AM_SETTINGS_FILE"); - if (!this->SettingsFile.empty()) { - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - this->SettingsFile = cmQtAutoGen::AppendFilenameSuffix( - this->SettingsFile, this->ConfigSuffix); - } - } else { + if (this->SettingsFile.empty()) { this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(), - "Settings file is missing"); + "Settings file name missing"); return false; } diff --git a/Source/cmQtAutoGeneratorRcc.cxx b/Source/cmQtAutoGeneratorRcc.cxx index 4348f2b..573f04c 100644 --- a/Source/cmQtAutoGeneratorRcc.cxx +++ b/Source/cmQtAutoGeneratorRcc.cxx @@ -69,12 +69,6 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) } this->SettingsFile = InfoGetConfig("ARCC_SETTINGS_FILE"); - if (!this->SettingsFile.empty()) { - if (this->MultiConfig != cmQtAutoGen::SINGLE) { - this->SettingsFile = cmQtAutoGen::AppendFilenameSuffix( - this->SettingsFile, this->ConfigSuffix); - } - } // - Files and directories this->ProjectSourceDir = InfoGet("ARCC_CMAKE_SOURCE_DIR"); -- cgit v0.12 From ab9d5896ae43789872fca2e83d556912abb60254 Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Sun, 19 Nov 2017 12:22:49 +0100 Subject: Autogen: Detect rcc feature once during configuration We used to detect the `rcc` features before every `rcc` list invocation wich resulted in `rcc` be called twice for every listing operation. Now we detect the `rcc` list capabilities once during configuration and pass it to the cmake_autorcc target in the info file. --- Modules/AutoRccInfo.cmake.in | 4 +- Source/cmQtAutoGen.cxx | 91 +++++++++++++----------------- Source/cmQtAutoGen.h | 6 +- Source/cmQtAutoGeneratorInitializer.cxx | 98 +++++++++++++++++++-------------- Source/cmQtAutoGeneratorInitializer.h | 3 + Source/cmQtAutoGeneratorRcc.cxx | 8 ++- Source/cmQtAutoGeneratorRcc.h | 2 +- 7 files changed, 109 insertions(+), 103 deletions(-) diff --git a/Modules/AutoRccInfo.cmake.in b/Modules/AutoRccInfo.cmake.in index 7b13b9e..5457a6f 100644 --- a/Modules/AutoRccInfo.cmake.in +++ b/Modules/AutoRccInfo.cmake.in @@ -7,5 +7,5 @@ set(ARCC_CMAKE_CURRENT_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@/") set(ARCC_CMAKE_CURRENT_BINARY_DIR "@CMAKE_CURRENT_BINARY_DIR@/") set(ARCC_BUILD_DIR @_build_dir@) # Qt environment -set(ARCC_QT_VERSION_MAJOR @_qt_version_major@) -set(ARCC_QT_RCC_EXECUTABLE @_qt_rcc_executable@) +set(ARCC_RCC_EXECUTABLE @_qt_rcc_executable@) +set(ARCC_RCC_LIST_OPTIONS @_qt_rcc_list_options@) diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index 9dc77ac..b9dd392 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx @@ -80,16 +80,6 @@ void MergeOptions(std::vector& baseOpts, baseOpts.insert(baseOpts.end(), extraOpts.begin(), extraOpts.end()); } -static std::string utilStripCR(std::string const& line) -{ - // Strip CR characters rcc may have printed (possibly more than one!). - std::string::size_type cr = line.find('\r'); - if (cr != std::string::npos) { - return line.substr(0, cr); - } - return line; -} - /// @brief Reads the resource files list from from a .qrc file - Qt4 version /// @return True if the .qrc file was successfully parsed static bool RccListInputsQt4(std::string const& fileName, @@ -107,10 +97,10 @@ static bool RccListInputsQt4(std::string const& fileName, qrcContents = osst.str(); } else { if (errorMessage != nullptr) { - std::ostringstream ost; - ost << "rcc file not readable:\n" - << " " << cmQtAutoGen::Quoted(fileName) << "\n"; - *errorMessage = ost.str(); + std::string& err = *errorMessage; + err = "rcc file not readable:\n "; + err += cmQtAutoGen::Quoted(fileName); + err += "\n"; } allGood = false; } @@ -146,6 +136,7 @@ static bool RccListInputsQt4(std::string const& fileName, /// @brief Reads the resource files list from from a .qrc file - Qt5 version /// @return True if the .qrc file was successfully parsed static bool RccListInputsQt5(std::string const& rccCommand, + std::vector const& rccListOptions, std::string const& fileName, std::vector& files, std::string* errorMessage) @@ -155,24 +146,6 @@ static bool RccListInputsQt5(std::string const& rccCommand, return false; } - // Read rcc features - bool hasDashDashList = false; - { - std::vector command; - command.push_back(rccCommand); - command.push_back("--help"); - std::string rccStdOut; - std::string rccStdErr; - int retVal = 0; - bool result = cmSystemTools::RunSingleCommand( - command, &rccStdOut, &rccStdErr, &retVal, nullptr, - cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto); - if (result && retVal == 0 && - rccStdOut.find("--list") != std::string::npos) { - hasDashDashList = true; - } - } - std::string const fileDir = cmSystemTools::GetFilenamePath(fileName); std::string const fileNameName = cmSystemTools::GetFilenameName(fileName); @@ -184,7 +157,8 @@ static bool RccListInputsQt5(std::string const& rccCommand, { std::vector command; command.push_back(rccCommand); - command.push_back(hasDashDashList ? "--list" : "-list"); + command.insert(command.end(), rccListOptions.begin(), + rccListOptions.end()); command.push_back(fileNameName); result = cmSystemTools::RunSingleCommand( command, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(), @@ -192,22 +166,32 @@ static bool RccListInputsQt5(std::string const& rccCommand, } if (!result || retVal) { if (errorMessage != nullptr) { - std::ostringstream ost; - ost << "rcc list process failed for\n " << cmQtAutoGen::Quoted(fileName) - << "\n" - << rccStdOut << "\n" - << rccStdErr << "\n"; - *errorMessage = ost.str(); + std::string& err = *errorMessage; + err = "rcc list process failed for:\n "; + err += cmQtAutoGen::Quoted(fileName); + err += "\n"; + err += rccStdOut; + err += "\n"; + err += rccStdErr; + err += "\n"; } return false; } + // Lambda to strip CR characters + auto StripCR = [](std::string& line) { + std::string::size_type cr = line.find('\r'); + if (cr != std::string::npos) { + line = line.substr(0, cr); + } + }; + // Parse rcc std output { std::istringstream ostr(rccStdOut); std::string oline; while (std::getline(ostr, oline)) { - oline = utilStripCR(oline); + StripCR(oline); if (!oline.empty()) { files.push_back(oline); } @@ -218,17 +202,17 @@ static bool RccListInputsQt5(std::string const& rccCommand, std::istringstream estr(rccStdErr); std::string eline; while (std::getline(estr, eline)) { - eline = utilStripCR(eline); + StripCR(eline); if (cmHasLiteralPrefix(eline, "RCC: Error in")) { static std::string searchString = "Cannot find file '"; std::string::size_type pos = eline.find(searchString); if (pos == std::string::npos) { if (errorMessage != nullptr) { - std::ostringstream ost; - ost << "rcc lists unparsable output:\n" - << cmQtAutoGen::Quoted(eline) << "\n"; - *errorMessage = ost.str(); + std::string& err = *errorMessage; + err = "rcc lists unparsable output:\n"; + err += cmQtAutoGen::Quoted(eline); + err += "\n"; } return false; } @@ -349,25 +333,26 @@ void cmQtAutoGen::RccMergeOptions(std::vector& baseOpts, MergeOptions(baseOpts, newOpts, valueOpts, isQt5); } -bool cmQtAutoGen::RccListInputs(std::string const& qtMajorVersion, - std::string const& rccCommand, +bool cmQtAutoGen::RccListInputs(std::string const& rccCommand, + std::vector const& rccListOptions, std::string const& fileName, std::vector& files, std::string* errorMessage) { bool allGood = false; if (cmSystemTools::FileExists(fileName.c_str())) { - if (qtMajorVersion == "4") { + if (rccListOptions.empty()) { allGood = RccListInputsQt4(fileName, files, errorMessage); } else { - allGood = RccListInputsQt5(rccCommand, fileName, files, errorMessage); + allGood = RccListInputsQt5(rccCommand, rccListOptions, fileName, files, + errorMessage); } } else { if (errorMessage != nullptr) { - std::ostringstream ost; - ost << "rcc file does not exist:\n" - << " " << cmQtAutoGen::Quoted(fileName) << "\n"; - *errorMessage = ost.str(); + std::string& err = *errorMessage; + err = "rcc resource file does not exist:\n "; + err += cmQtAutoGen::Quoted(fileName); + err += "\n"; } } return allGood; diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index acc092f..e769e93 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -61,9 +61,9 @@ public: /// @brief Reads the resource files list from from a .qrc file /// @arg fileName Must be the absolute path of the .qrc file - /// @return True if the rcc file was successfully parsed - static bool RccListInputs(std::string const& qtMajorVersion, - std::string const& rccCommand, + /// @return True if the rcc file was successfully read + static bool RccListInputs(std::string const& rccCommand, + std::vector const& rccListOptions, std::string const& fileName, std::vector& files, std::string* errorMessage = nullptr); diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx index 5edddaa..b265f28 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGeneratorInitializer.cxx @@ -14,6 +14,7 @@ #include "cmMakefile.h" #include "cmOutputConverter.h" #include "cmPolicies.h" +#include "cmProcessOutput.h" #include "cmSourceFile.h" #include "cmSourceGroup.h" #include "cmState.h" @@ -190,40 +191,6 @@ static bool StaticLibraryCycle(cmGeneratorTarget const* targetOrigin, return cycle; } -static std::string RccGetExecutable(cmGeneratorTarget const* target, - std::string const& qtMajorVersion) -{ - std::string rccExec; - std::string err; - - cmLocalGenerator* localGen = target->GetLocalGenerator(); - if (qtMajorVersion == "5") { - cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt5::rcc"); - if (tgt != nullptr) { - rccExec = SafeString(tgt->ImportedGetLocation("")); - } else { - err = "AUTORCC: Qt5::rcc target not found"; - } - } else if (qtMajorVersion == "4") { - cmGeneratorTarget* tgt = localGen->FindGeneratorTargetToUse("Qt4::rcc"); - if (tgt != nullptr) { - rccExec = SafeString(tgt->ImportedGetLocation("")); - } else { - err = "AUTORCC: Qt4::rcc target not found"; - } - } else { - err = "The AUTORCC feature supports only Qt 4 and Qt 5"; - } - - if (!err.empty()) { - err += " ("; - err += target->GetName(); - err += ")"; - cmSystemTools::Error(err.c_str()); - } - return rccExec; -} - cmQtAutoGeneratorInitializer::cmQtAutoGeneratorInitializer( cmGeneratorTarget* target, bool mocEnabled, bool uicEnabled, bool rccEnabled, std::string const& qtVersionMajor) @@ -368,6 +335,56 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() this->Target->AddIncludeDirectory(includeDir, true); } + // Acquire rcc executable and features + if (this->RccEnabled) { + { + std::string err; + if (this->QtVersionMajor == "5") { + cmGeneratorTarget* tgt = + localGen->FindGeneratorTargetToUse("Qt5::rcc"); + if (tgt != nullptr) { + this->RccExecutable = SafeString(tgt->ImportedGetLocation("")); + } else { + err = "AUTORCC: Qt5::rcc target not found"; + } + } else if (QtVersionMajor == "4") { + cmGeneratorTarget* tgt = + localGen->FindGeneratorTargetToUse("Qt4::rcc"); + if (tgt != nullptr) { + this->RccExecutable = SafeString(tgt->ImportedGetLocation("")); + } else { + err = "AUTORCC: Qt4::rcc target not found"; + } + } else { + err = "The AUTORCC feature supports only Qt 4 and Qt 5"; + } + if (!err.empty()) { + err += " ("; + err += this->Target->GetName(); + err += ")"; + cmSystemTools::Error(err.c_str()); + } + } + // Detect if rcc supports (-)-list + if (!this->RccExecutable.empty() && (this->QtVersionMajor == "5")) { + std::vector command; + command.push_back(this->RccExecutable); + command.push_back("--help"); + std::string rccStdOut; + std::string rccStdErr; + int retVal = 0; + bool result = cmSystemTools::RunSingleCommand( + command, &rccStdOut, &rccStdErr, &retVal, nullptr, + cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto); + if (result && retVal == 0 && + rccStdOut.find("--list") != std::string::npos) { + this->RccListOptions.push_back("--list"); + } else { + this->RccListOptions.push_back("-list"); + } + } + } + // Extract relevant source files std::vector generatedSources; std::vector generatedHeaders; @@ -548,8 +565,6 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() // Process qrc files if (!this->Qrcs.empty()) { const bool QtV5 = (this->QtVersionMajor == "5"); - std::string const rcc = - RccGetExecutable(this->Target, this->QtVersionMajor); // Target rcc options std::vector optionsTarget; cmSystemTools::ExpandListArgument( @@ -673,9 +688,9 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() // Add the resource files to the dependencies { std::string error; - if (cmQtAutoGen::RccListInputs(this->QtVersionMajor, rcc, - qrc.QrcFile, qrc.Resources, - &error)) { + if (cmQtAutoGen::RccListInputs(this->RccExecutable, + this->RccListOptions, qrc.QrcFile, + qrc.Resources, &error)) { for (std::string const& fileName : qrc.Resources) { // Add resource file to the custom command dependencies ccDepends.push_back(fileName); @@ -881,8 +896,9 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() } } if (this->RccEnabled) { - AddDefinitionEscaped(makefile, "_qt_rcc_executable", - RccGetExecutable(this->Target, this->QtVersionMajor)); + AddDefinitionEscaped(makefile, "_qt_rcc_executable", this->RccExecutable); + AddDefinitionEscaped(makefile, "_qt_rcc_list_options", + this->RccListOptions); } // Create info directory on demand diff --git a/Source/cmQtAutoGeneratorInitializer.h b/Source/cmQtAutoGeneratorInitializer.h index 9eebbd6..e06e1c4 100644 --- a/Source/cmQtAutoGeneratorInitializer.h +++ b/Source/cmQtAutoGeneratorInitializer.h @@ -65,8 +65,11 @@ private: bool MocEnabled; bool UicEnabled; bool RccEnabled; + // Qt std::string QtVersionMajor; std::string QtVersionMinor; + std::string RccExecutable; + std::vector RccListOptions; // Configurations std::string ConfigDefault; std::vector ConfigsList; diff --git a/Source/cmQtAutoGeneratorRcc.cxx b/Source/cmQtAutoGeneratorRcc.cxx index 573f04c..3c9f1a8 100644 --- a/Source/cmQtAutoGeneratorRcc.cxx +++ b/Source/cmQtAutoGeneratorRcc.cxx @@ -78,8 +78,8 @@ bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile) this->AutogenBuildDir = InfoGet("ARCC_BUILD_DIR"); // - Qt environment - this->QtMajorVersion = InfoGet("ARCC_QT_VERSION_MAJOR"); - this->RccExecutable = InfoGet("ARCC_QT_RCC_EXECUTABLE"); + this->RccExecutable = InfoGet("ARCC_RCC_EXECUTABLE"); + this->RccListOptions = InfoGetList("ARCC_RCC_LIST_OPTIONS"); // - Job this->QrcFile = InfoGet("ARCC_SOURCE"); @@ -135,6 +135,8 @@ void cmQtAutoGeneratorRcc::SettingsFileRead(cmMakefile* makefile) std::string str; str += this->RccExecutable; str += sep; + str += cmJoin(this->RccListOptions, ";"); + str += sep; str += this->QrcFile; str += sep; str += this->RccFile; @@ -307,7 +309,7 @@ bool cmQtAutoGeneratorRcc::RccGenerate() } else { // Read input file list from qrc file std::string error; - if (cmQtAutoGen::RccListInputs(this->QtMajorVersion, this->RccExecutable, + if (cmQtAutoGen::RccListInputs(this->RccExecutable, this->RccListOptions, this->QrcFile, readFiles, &error)) { files = &readFiles; } else { diff --git a/Source/cmQtAutoGeneratorRcc.h b/Source/cmQtAutoGeneratorRcc.h index 804d117..0e3f690 100644 --- a/Source/cmQtAutoGeneratorRcc.h +++ b/Source/cmQtAutoGeneratorRcc.h @@ -44,8 +44,8 @@ private: std::string AutogenBuildDir; cmFilePathChecksum FilePathChecksum; // -- Qt environment - std::string QtMajorVersion; std::string RccExecutable; + std::vector RccListOptions; // -- Job std::string QrcFile; std::string RccFile; -- cgit v0.12 From be11a85286ad538654dacce63a6be0af0c3c5bec Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Sun, 19 Nov 2017 13:34:43 +0100 Subject: Autogen: Use project relative paths in rcc custom command comment --- Source/cmQtAutoGeneratorInitializer.cxx | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx index b265f28..a2c9b42 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGeneratorInitializer.cxx @@ -147,6 +147,26 @@ static void AddCleanFile(cmMakefile* makefile, std::string const& fileName) false); } +static std::string FileProjectRelativePath(cmMakefile* makefile, + std::string const& fileName) +{ + std::string res; + { + std::string pSource = cmSystemTools::RelativePath( + makefile->GetCurrentSourceDirectory(), fileName.c_str()); + std::string pBinary = cmSystemTools::RelativePath( + makefile->GetCurrentBinaryDirectory(), fileName.c_str()); + if (pSource.size() < pBinary.size()) { + res = std::move(pSource); + } else if (pBinary.size() < fileName.size()) { + res = std::move(pBinary); + } else { + res = fileName; + } + } + return res; +} + /* @brief Tests if targetDepend is a STATIC_LIBRARY and if any of its * recursive STATIC_LIBRARY dependencies depends on targetOrigin * (STATIC_LIBRARY cycle). @@ -646,7 +666,7 @@ void cmQtAutoGeneratorInitializer::InitCustomTargets() commandLines.push_back(std::move(currentLine)); } std::string ccComment = "Automatic RCC for "; - ccComment += qrc.QrcFile; + ccComment += FileProjectRelativePath(makefile, qrc.QrcFile); if (qrc.Generated) { // Create custom rcc target -- cgit v0.12 From 4043463179d5e238cd1505f68fe0a4e75f4feba4 Mon Sep 17 00:00:00 2001 From: Sebastian Holtermann Date: Sun, 19 Nov 2017 12:44:21 +0100 Subject: Autogen: Use integers instead of strings for the Qt version --- Modules/AutogenInfo.cmake.in | 1 - Source/cmQtAutoGeneratorInitializer.cxx | 3 +-- Source/cmQtAutoGeneratorMocUic.cxx | 19 +++++++------------ Source/cmQtAutoGeneratorMocUic.h | 3 +-- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/Modules/AutogenInfo.cmake.in b/Modules/AutogenInfo.cmake.in index 8fa3684..9a4a06d 100644 --- a/Modules/AutogenInfo.cmake.in +++ b/Modules/AutogenInfo.cmake.in @@ -12,7 +12,6 @@ set(AM_HEADERS @_headers@) set(AM_SETTINGS_FILE @_settings_file@) # Qt environment set(AM_QT_VERSION_MAJOR @_qt_version_major@) -set(AM_QT_VERSION_MINOR @_qt_version_minor@) set(AM_QT_MOC_EXECUTABLE @_qt_moc_executable@) set(AM_QT_UIC_EXECUTABLE @_qt_uic_executable@) # MOC settings diff --git a/Source/cmQtAutoGeneratorInitializer.cxx b/Source/cmQtAutoGeneratorInitializer.cxx index a2c9b42..d9a5a9a 100644 --- a/Source/cmQtAutoGeneratorInitializer.cxx +++ b/Source/cmQtAutoGeneratorInitializer.cxx @@ -899,10 +899,9 @@ void cmQtAutoGeneratorInitializer::SetupCustomTargets() AddDefinitionEscaped(makefile, "_multi_config", cmQtAutoGen::MultiConfigName(this->MultiConfig)); AddDefinitionEscaped(makefile, "_build_dir", this->DirBuild); - AddDefinitionEscaped(makefile, "_qt_version_major", this->QtVersionMajor); - AddDefinitionEscaped(makefile, "_qt_version_minor", this->QtVersionMinor); if (this->MocEnabled || this->UicEnabled) { + AddDefinitionEscaped(makefile, "_qt_version_major", this->QtVersionMajor); AddDefinitionEscaped(makefile, "_settings_file", this->AutogenSettingsFile); AddDefinitionEscaped(makefile, "_sources", this->Sources); diff --git a/Source/cmQtAutoGeneratorMocUic.cxx b/Source/cmQtAutoGeneratorMocUic.cxx index 1956a89..0de02b5 100644 --- a/Source/cmQtAutoGeneratorMocUic.cxx +++ b/Source/cmQtAutoGeneratorMocUic.cxx @@ -49,6 +49,7 @@ static bool ListContains(std::vector const& list, cmQtAutoGeneratorMocUic::cmQtAutoGeneratorMocUic() : MultiConfig(cmQtAutoGen::WRAP) , IncludeProjectDirsBefore(false) + , QtVersionMajor(4) , MocSettingsChanged(false) , MocPredefsChanged(false) , MocRelaxedMode(false) @@ -163,19 +164,13 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) } // - Qt environment - this->QtMajorVersion = InfoGet("AM_QT_VERSION_MAJOR"); - this->QtMinorVersion = InfoGet("AM_QT_VERSION_MINOR"); + if (!cmSystemTools::StringToULong(InfoGet("AM_QT_VERSION_MAJOR"), + &this->QtVersionMajor)) { + this->QtVersionMajor = 4; + } this->MocExecutable = InfoGet("AM_QT_MOC_EXECUTABLE"); this->UicExecutable = InfoGet("AM_QT_UIC_EXECUTABLE"); - // Check Qt version - if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) { - this->LogFileError(cmQtAutoGen::GEN, this->GetInfoFile(), - "Unsupported Qt version: " + - cmQtAutoGen::Quoted(this->QtMajorVersion)); - return false; - } - // - Moc if (this->MocEnabled()) { this->MocSkipList = InfoGetList("AM_MOC_SKIP"); @@ -203,7 +198,7 @@ bool cmQtAutoGeneratorMocUic::InitInfoFile(cmMakefile* makefile) std::vector const mocDependFilters = InfoGetList("AM_MOC_DEPEND_FILTERS"); // Insert Q_PLUGIN_METADATA dependency filter - if (this->QtMajorVersion != "4") { + if (this->QtVersionMajor != 4) { this->MocDependFilterPush("Q_PLUGIN_METADATA", "[\n][ \t]*Q_PLUGIN_METADATA[ \t]*\\(" "[^\\)]*FILE[ \t]*\"([^\"]+)\""); @@ -1686,7 +1681,7 @@ bool cmQtAutoGeneratorMocUic::UicGenerateFile(const UicJob& uicJob) auto optionIt = this->UicOptions.find(uicJob.SourceFile); if (optionIt != this->UicOptions.end()) { cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second, - (this->QtMajorVersion == "5")); + (this->QtVersionMajor == 5)); } cmd.insert(cmd.end(), allOpts.begin(), allOpts.end()); } diff --git a/Source/cmQtAutoGeneratorMocUic.h b/Source/cmQtAutoGeneratorMocUic.h index a03270b..d510939 100644 --- a/Source/cmQtAutoGeneratorMocUic.h +++ b/Source/cmQtAutoGeneratorMocUic.h @@ -154,8 +154,7 @@ private: std::string AutogenBuildDir; std::string AutogenIncludeDir; // -- Qt environment - std::string QtMajorVersion; - std::string QtMinorVersion; + unsigned long QtVersionMajor; std::string MocExecutable; std::string UicExecutable; // -- File lists -- cgit v0.12