diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 9a18184fd3bd16a68a5acb4392de9557724b0f23..0142c07ecb33ded1b187862370032c108c9a05c1 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -432,6 +432,8 @@ set(SRCS cmQtAutoMocUic.h cmQtAutoRcc.cxx cmQtAutoRcc.h + cmQtAutoUicHelpers.cxx + cmQtAutoUicHelpers.h cmRST.cxx cmRST.h cmRuntimeDependencyArchive.cxx diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index 57fcd2da7688bc430cca22982e864c32f6dfcd2f..898d862221f6f6862caa303c566ced99abf10f90 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx @@ -384,3 +384,39 @@ bool cmQtAutoGen::RccLister::list(std::string const& qrcFile, } return true; } + +bool cmQtAutoGen::FileRead(std::string& content, std::string const& filename, + std::string* error) +{ + content.clear(); + if (!cmSystemTools::FileExists(filename, true)) { + if (error != nullptr) { + *error = "Not a file."; + } + return false; + } + + unsigned long const length = cmSystemTools::FileLength(filename); + cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); + + // Use lambda to save destructor calls of ifs + return [&ifs, length, &content, error]() -> bool { + if (!ifs) { + if (error != nullptr) { + *error = "Opening the file for reading failed."; + } + return false; + } + content.reserve(length); + using IsIt = std::istreambuf_iterator<char>; + content.assign(IsIt{ ifs }, IsIt{}); + if (!ifs) { + content.clear(); + if (error != nullptr) { + *error = "Reading from the file failed."; + } + return false; + } + return true; + }(); +} diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index 466a954a981c59285af8f2fcf12cdc097ecbc3b3..b9ae360291eb10cfbe32b7d9a0b171e947e36fe0 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -100,6 +100,9 @@ public: std::vector<std::string> const& newOpts, bool isQt5); + static bool FileRead(std::string& content, std::string const& filename, + std::string* error = nullptr); + /** @class RccLister * @brief Lists files in qrc resource files */ diff --git a/Source/cmQtAutoGenInitializer.cxx b/Source/cmQtAutoGenInitializer.cxx index 2894201c9b35513a7342c1319fe11ad03c3bc88e..9d8086397ca17d75de4deee4635b6573a2472c7a 100644 --- a/Source/cmQtAutoGenInitializer.cxx +++ b/Source/cmQtAutoGenInitializer.cxx @@ -902,6 +902,13 @@ bool cmQtAutoGenInitializer::InitScanFiles() // The reason is that their file names might be discovered from source files // at generation time. if (this->MocOrUicEnabled()) { + std::set<std::string> uicIncludes; + auto collectUicIncludes = [&](std::unique_ptr<cmSourceFile> const& sf) { + std::string content; + FileRead(content, sf->GetFullPath()); + this->AutoUicHelpers.CollectUicIncludes(uicIncludes, content); + }; + for (const auto& sf : this->Makefile->GetSourceFiles()) { // sf->GetExtension() is only valid after sf->ResolveFullPath() ... // Since we're iterating over source files that might be not in the @@ -914,6 +921,10 @@ bool cmQtAutoGenInitializer::InitScanFiles() std::string const& extLower = cmSystemTools::LowerCase(sf->GetExtension()); + bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN); + bool const skipUic = + (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC) || + !this->Uic.Enabled); if (cm->IsAHeaderExtension(extLower)) { if (!cm::contains(this->AutogenTarget.Headers, sf.get())) { auto muf = makeMUFile(sf.get(), fullPath, {}, false); @@ -921,6 +932,9 @@ bool cmQtAutoGenInitializer::InitScanFiles() addMUHeader(std::move(muf), extLower); } } + if (!skipUic && !sf->GetIsGenerated()) { + collectUicIncludes(sf); + } } else if (cm->IsACLikeSourceExtension(extLower)) { if (!cm::contains(this->AutogenTarget.Sources, sf.get())) { auto muf = makeMUFile(sf.get(), fullPath, {}, false); @@ -928,11 +942,11 @@ bool cmQtAutoGenInitializer::InitScanFiles() addMUSource(std::move(muf)); } } + if (!skipUic && !sf->GetIsGenerated()) { + collectUicIncludes(sf); + } } else if (this->Uic.Enabled && (extLower == kw.ui)) { // .ui file - bool const skipAutogen = sf->GetPropertyAsBool(kw.SKIP_AUTOGEN); - bool const skipUic = - (skipAutogen || sf->GetPropertyAsBool(kw.SKIP_AUTOUIC)); if (!skipUic) { // Check if the .ui file has uic options std::string const uicOpts = sf->GetSafeProperty(kw.AUTOUIC_OPTIONS); @@ -942,35 +956,22 @@ bool cmQtAutoGenInitializer::InitScanFiles() this->Uic.UiFilesWithOptions.emplace_back(fullPath, cmExpandedList(uicOpts)); } - - auto uiHeaderRelativePath = cmSystemTools::RelativePath( - this->LocalGen->GetCurrentSourceDirectory(), - cmSystemTools::GetFilenamePath(fullPath)); - - // Avoid creating a path containing adjacent slashes - if (!uiHeaderRelativePath.empty() && - uiHeaderRelativePath.back() != '/') { - uiHeaderRelativePath += '/'; - } - - auto uiHeaderFilePath = cmStrCat( - '/', uiHeaderRelativePath, "ui_"_s, - cmSystemTools::GetFilenameWithoutLastExtension(fullPath), ".h"_s); - - ConfigString uiHeader; - std::string uiHeaderGenex; - this->ConfigFileNamesAndGenex( - uiHeader, uiHeaderGenex, cmStrCat(this->Dir.Build, "/include"_s), - uiHeaderFilePath); - - this->Uic.UiHeaders.emplace_back( - std::make_pair(uiHeader, uiHeaderGenex)); } else { // Register skipped .ui file this->Uic.SkipUi.insert(fullPath); } } } + + for (const auto& include : uicIncludes) { + ConfigString uiHeader; + std::string uiHeaderGenex; + this->ConfigFileNamesAndGenex(uiHeader, uiHeaderGenex, + cmStrCat(this->Dir.Build, "/include"_s), + cmStrCat("/"_s, include)); + this->Uic.UiHeaders.emplace_back( + std::make_pair(uiHeader, uiHeaderGenex)); + } } // Process GENERATED sources and headers diff --git a/Source/cmQtAutoGenInitializer.h b/Source/cmQtAutoGenInitializer.h index e76817b9c430082e8abb699c2143f60c497cdc81..3ec87d212d4c73e9eb5c3fa7abf223d8b46b9275 100644 --- a/Source/cmQtAutoGenInitializer.h +++ b/Source/cmQtAutoGenInitializer.h @@ -17,6 +17,7 @@ #include "cmFilePathChecksum.h" #include "cmQtAutoGen.h" +#include "cmQtAutoUicHelpers.h" class cmGeneratorTarget; class cmGlobalGenerator; @@ -170,6 +171,7 @@ private: std::string ConfigDefault; std::vector<std::string> ConfigsList; std::string TargetsFolder; + cmQtAutoUicHelpers AutoUicHelpers; /** Common directories. */ struct diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx index 568926e3c5d861eb0b947337b94f1b560006e2c8..0c6b5e6dc53c52a73c0e32afba36e82dc833ea67 100644 --- a/Source/cmQtAutoGenerator.cxx +++ b/Source/cmQtAutoGenerator.cxx @@ -121,43 +121,6 @@ bool cmQtAutoGenerator::MakeParentDirectory(std::string const& filename) return success; } -bool cmQtAutoGenerator::FileRead(std::string& content, - std::string const& filename, - std::string* error) -{ - content.clear(); - if (!cmSystemTools::FileExists(filename, true)) { - if (error != nullptr) { - *error = "Not a file."; - } - return false; - } - - unsigned long const length = cmSystemTools::FileLength(filename); - cmsys::ifstream ifs(filename.c_str(), (std::ios::in | std::ios::binary)); - - // Use lambda to save destructor calls of ifs - return [&ifs, length, &content, error]() -> bool { - if (!ifs) { - if (error != nullptr) { - *error = "Opening the file for reading failed."; - } - return false; - } - content.reserve(length); - using IsIt = std::istreambuf_iterator<char>; - content.assign(IsIt{ ifs }, IsIt{}); - if (!ifs) { - content.clear(); - if (error != nullptr) { - *error = "Reading from the file failed."; - } - return false; - } - return true; - }(); -} - bool cmQtAutoGenerator::FileWrite(std::string const& filename, std::string const& content, std::string* error) diff --git a/Source/cmQtAutoGenerator.h b/Source/cmQtAutoGenerator.h index 5c3a8adccb4980ce162ec3845900555cefc7acf7..66399d7e8aeabce047e11d08029a448c0894f682 100644 --- a/Source/cmQtAutoGenerator.h +++ b/Source/cmQtAutoGenerator.h @@ -70,8 +70,6 @@ public: // -- File system methods static bool MakeParentDirectory(std::string const& filename); - static bool FileRead(std::string& content, std::string const& filename, - std::string* error = nullptr); static bool FileWrite(std::string const& filename, std::string const& content, std::string* error = nullptr); diff --git a/Source/cmQtAutoMocUic.cxx b/Source/cmQtAutoMocUic.cxx index 2753fd57c021cf1a38355ed8c9b58f3e987e0537..86d54f9e1a7cebee2c6e3234f6a1c26e4f1f27bd 100644 --- a/Source/cmQtAutoMocUic.cxx +++ b/Source/cmQtAutoMocUic.cxx @@ -30,6 +30,7 @@ #include "cmGeneratedFileStream.h" #include "cmQtAutoGen.h" #include "cmQtAutoGenerator.h" +#include "cmQtAutoUicHelpers.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmWorkerPool.h" @@ -281,7 +282,7 @@ public: std::vector<std::string> Options; std::unordered_map<std::string, UiFile> UiFiles; std::vector<std::string> SearchPaths; - cmsys::RegularExpression RegExpInclude; + cmQtAutoUicHelpers AutoUicHelpers; }; /** Uic shared variables. */ @@ -761,11 +762,7 @@ std::string cmQtAutoMocUicT::MocSettingsT::MacrosString() const return res; } -cmQtAutoMocUicT::UicSettingsT::UicSettingsT() -{ - this->RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+" - "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); -} +cmQtAutoMocUicT::UicSettingsT::UicSettingsT() = default; cmQtAutoMocUicT::UicSettingsT::~UicSettingsT() = default; @@ -1056,16 +1053,7 @@ void cmQtAutoMocUicT::JobParseT::UicIncludes() } std::set<std::string> includes; - { - const char* contentChars = this->Content.c_str(); - cmsys::RegularExpression const& regExp = this->UicConst().RegExpInclude; - cmsys::RegularExpressionMatch match; - while (regExp.find(contentChars, match)) { - includes.emplace(match.match(2)); - // Forward content pointer - contentChars += match.end(); - } - } + this->UicConst().AutoUicHelpers.CollectUicIncludes(includes, this->Content); this->CreateKeys(this->FileHandle->ParseData->Uic.Include, includes, UiUnderscoreLength); } diff --git a/Source/cmQtAutoUicHelpers.cxx b/Source/cmQtAutoUicHelpers.cxx new file mode 100644 index 0000000000000000000000000000000000000000..751ae08575a807b8bec169d5896437001d9cae33 --- /dev/null +++ b/Source/cmQtAutoUicHelpers.cxx @@ -0,0 +1,25 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmQtAutoUicHelpers.h" + +cmQtAutoUicHelpers::cmQtAutoUicHelpers() +{ + RegExpInclude.compile("(^|\n)[ \t]*#[ \t]*include[ \t]+" + "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); +} + +void cmQtAutoUicHelpers::CollectUicIncludes(std::set<std::string>& includes, + const std::string& content) const +{ + if (content.find("ui_") == std::string::npos) { + return; + } + + const char* contentChars = content.c_str(); + cmsys::RegularExpressionMatch match; + while (this->RegExpInclude.find(contentChars, match)) { + includes.emplace(match.match(2)); + // Forward content pointer + contentChars += match.end(); + } +} diff --git a/Source/cmQtAutoUicHelpers.h b/Source/cmQtAutoUicHelpers.h new file mode 100644 index 0000000000000000000000000000000000000000..6b09a31daaadbf1c32da782cfbe4d69343ebe845 --- /dev/null +++ b/Source/cmQtAutoUicHelpers.h @@ -0,0 +1,20 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <set> +#include <string> + +#include "cmsys/RegularExpression.hxx" + +class cmQtAutoUicHelpers +{ +public: + cmQtAutoUicHelpers(); + virtual ~cmQtAutoUicHelpers() = default; + void CollectUicIncludes(std::set<std::string>& includes, + const std::string& content) const; + +private: + cmsys::RegularExpression RegExpInclude; +}; diff --git a/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt b/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt index 1f636af451fb0e5f9429478f67fbcf7ba2ba529c..a9ccece5d5c7de280e2e3cde613fe312f66f156c 100644 --- a/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt +++ b/Tests/QtAutogen/RerunUicOnFileChange/CMakeLists.txt @@ -27,10 +27,12 @@ endmacro() configure_file("${testProjectTemplateDir}/mocwidget.h" "${testProjectSrc}/mocwidget.h" COPYONLY) configure_file("${testProjectTemplateDir}/main.cpp" "${testProjectSrc}/main.cpp" COPYONLY) +configure_file("${testProjectTemplateDir}/subdir/subdircheck.cpp" "${testProjectSrc}/subdir/subdircheck.cpp" COPYONLY) configure_file("${testProjectTemplateDir}/CMakeLists.txt.in" "${testProjectSrc}/CMakeLists.txt" @ONLY) set(Num 1) configure_file("${testProjectTemplateDir}/mainwindow.ui.in" "${testProjectSrc}/mainwindow.ui" @ONLY) +configure_file("${testProjectTemplateDir}/subdir/mainwindowsubdir.ui.in" "${testProjectSrc}/subdir/mainwindowsubdir.ui" @ONLY) if(CMAKE_GENERATOR_INSTANCE) set(_D_CMAKE_GENERATOR_INSTANCE "-DCMAKE_GENERATOR_INSTANCE=${CMAKE_GENERATOR_INSTANCE}") @@ -94,6 +96,7 @@ sleep() set(Num 2) configure_file("${testProjectTemplateDir}/mainwindow.ui.in" "${testProjectSrc}/mainwindow.ui" @ONLY) +configure_file("${testProjectTemplateDir}/subdir/mainwindowsubdir.ui.in" "${testProjectSrc}/subdir/mainwindowsubdir.ui" @ONLY) rebuild(2) execute_process(COMMAND "${testProjectBinDir}/${extra_bin_path}UicOnFileChange" RESULT_VARIABLE result) diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in index fa9dd6bee3239b01ea10450191c6bfe00500e294..2a1998d10fee4377d5965ad6e12564e1fab6575e 100644 --- a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in +++ b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/CMakeLists.txt.in @@ -6,6 +6,8 @@ include("@CMAKE_CURRENT_LIST_DIR@/../AutogenGuiTest.cmake") # Enable CMAKE_AUTOUIC for all targets set(CMAKE_AUTOUIC ON) -add_executable(UicOnFileChange main.cpp mainwindow.ui) +add_executable(UicOnFileChange main.cpp mainwindow.ui + subdir/subdircheck.cpp subdir/mainwindowsubdir.ui +) target_include_directories(UicOnFileChange PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(UicOnFileChange ${QT_QTCORE_TARGET} ${QT_LIBRARIES}) diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp index fd810fa24727e4f2880d0bb9c589a4c3f1621667..39812688463ae0c5b9079b20b05a161e559c41cf 100644 --- a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp +++ b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/main.cpp @@ -1,9 +1,11 @@ #include "ui_mainwindow.h" +extern bool subdircheck(); + int main(int argc, char* argv[]) { MocWidget mw; Ui::Widget mwUi; mwUi.setupUi(&mw); - return mw.objectName() == "Widget2" ? 0 : 1; + return mw.objectName() == "Widget2" && subdircheck() ? 0 : 1; } diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/mainwindowsubdir.ui.in b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/mainwindowsubdir.ui.in new file mode 100644 index 0000000000000000000000000000000000000000..a6a31f6d4280a799bc97df4f46d54fc236da9483 --- /dev/null +++ b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/mainwindowsubdir.ui.in @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>WidgetSubdir</class> + <widget class="MocWidget" name="WidgetSubdir@Num@"/> + <resources/> + <connections/> +</ui> diff --git a/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/subdircheck.cpp b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/subdircheck.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b36a10df8a5ffef57245344961163a06b4a46cc --- /dev/null +++ b/Tests/QtAutogen/RerunUicOnFileChange/UicOnFileChange/subdir/subdircheck.cpp @@ -0,0 +1,9 @@ +#include "ui_mainwindowsubdir.h" + +bool subdircheck() +{ + MocWidget mw; + Ui::WidgetSubdir mwUi; + mwUi.setupUi(&mw); + return mw.objectName() == "WidgetSubdir2"; +}