diff options
111 files changed, 2365 insertions, 1531 deletions
diff --git a/Help/release/dev/cpack-rpm-deb-version.rst b/Help/release/dev/cpack-rpm-deb-version.rst new file mode 100644 index 0000000..a64e8bd --- /dev/null +++ b/Help/release/dev/cpack-rpm-deb-version.rst @@ -0,0 +1,14 @@ +cpack-rpm-deb-version +--------------------- + +* Modules :module:`CPackRPM` and :module:`CPackDeb` learned to set package epoch + version. + See :variable:`CPACK_RPM_PACKAGE_EPOCH` and + :variable:`CPACK_DEBIAN_PACKAGE_EPOCH` variables. + +* The :module:`CPackDeb` module learned to set package release version in + `Version` info property. + See :variable:`CPACK_DEBIAN_PACKAGE_RELEASE` variable. + +* The :module:`CPackDeb` module learned more strict package version checking + that complies with Debian rules. diff --git a/Help/release/dev/graphviz-line-styles.rst b/Help/release/dev/graphviz-line-styles.rst new file mode 100644 index 0000000..d24f236 --- /dev/null +++ b/Help/release/dev/graphviz-line-styles.rst @@ -0,0 +1,6 @@ +graphviz-line-styles +------------------------- + +* The graphviz output now distinguishes between the different dependency types + ``PUBLIC``, ``PRIVATE`` and ``INTERFACE`` and represents them in the output graph + as solid, dashed and dotted edges. diff --git a/Modules/CMakeDetermineCompilerId.cmake b/Modules/CMakeDetermineCompilerId.cmake index 0a93e5f..cddc966 100644 --- a/Modules/CMakeDetermineCompilerId.cmake +++ b/Modules/CMakeDetermineCompilerId.cmake @@ -321,12 +321,19 @@ Id flags: ${testflags} ${CMAKE_${lang}_COMPILER_ID_FLAGS_ALWAYS} set(id_product_type "com.apple.product-type.tool") if(CMAKE_OSX_SYSROOT) set(id_sdkroot "SDKROOT = \"${CMAKE_OSX_SYSROOT}\";") - if(CMAKE_OSX_SYSROOT MATCHES "(^|/)[Ii][Pp][Hh][Oo][Nn][Ee]") + if(CMAKE_OSX_SYSROOT MATCHES "(^|/)[Ii][Pp][Hh][Oo][Nn][Ee]" OR + CMAKE_OSX_SYSROOT MATCHES "(^|/)[Aa][Pp][Pp][Ll][Ee][Tt][Vv]") set(id_product_type "com.apple.product-type.bundle.unit-test") endif() else() set(id_sdkroot "") endif() + if(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM) + set(id_development_team + "DEVELOPMENT_TEAM = \"${CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM}\";") + else() + set(id_development_team "") + endif() configure_file(${CMAKE_ROOT}/Modules/CompilerId/Xcode-3.pbxproj.in ${id_dir}/CompilerId${lang}.xcodeproj/project.pbxproj @ONLY) unset(_ENV_MACOSX_DEPLOYMENT_TARGET) diff --git a/Modules/CMakeGraphVizOptions.cmake b/Modules/CMakeGraphVizOptions.cmake index 3976581..0d7f1d9 100644 --- a/Modules/CMakeGraphVizOptions.cmake +++ b/Modules/CMakeGraphVizOptions.cmake @@ -20,6 +20,9 @@ # * a ``foo.dot.<target>`` file for each target, file showing on which other targets the respective target depends # * a ``foo.dot.<target>.dependers`` file, showing which other targets depend on the respective target # +# The different dependency types ``PUBLIC``, ``PRIVATE`` and ``INTERFACE`` +# are represented as solid, dashed and dotted edges. +# # This can result in huge graphs. Using the file # ``CMakeGraphVizOptions.cmake`` the look and content of the generated # graphs can be influenced. This file is searched first in diff --git a/Modules/CPackDeb.cmake b/Modules/CPackDeb.cmake index 85d564e..337bec8 100644 --- a/Modules/CPackDeb.cmake +++ b/Modules/CPackDeb.cmake @@ -88,6 +88,16 @@ # get overwritten and it is up to the packager to set the variables in a # manner that will prevent such errors. # +# .. variable:: CPACK_DEBIAN_PACKAGE_EPOCH +# +# The Debian package epoch +# +# * Mandatory : No +# * Default : - +# +# Optional number that should be incremented when changing versioning schemas +# or fixing mistakes in the version numbers of older packages. +# # .. variable:: CPACK_DEBIAN_PACKAGE_VERSION # # The Debian package version @@ -95,12 +105,17 @@ # * Mandatory : YES # * Default : :variable:`CPACK_PACKAGE_VERSION` # +# This variable may contain only alphanumerics (A-Za-z0-9) and the characters +# . + - ~ (full stop, plus, hyphen, tilde) and should start with a digit. If +# :variable:`CPACK_DEBIAN_PACKAGE_RELEASE` is not set then hyphens are not +# allowed. +# # .. variable:: CPACK_DEBIAN_PACKAGE_RELEASE # # The Debian package release - Debian revision number. # -# * Mandatory : YES -# * Default : 1 +# * Mandatory : No +# * Default : - # # This is the numbering of the DEB package itself, i.e. the version of the # packaging and not the version of the content (see @@ -738,6 +753,32 @@ function(cpack_deb_prepare_package_vars) set(CPACK_DEBIAN_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}) endif() + if(NOT CPACK_DEBIAN_PACKAGE_VERSION MATCHES "^[0-9][A-Za-z0-9.+-~]*$") + message(FATAL_ERROR + "CPackDeb: Debian package version must confirm to \"^[0-9][A-Za-z0-9.+-~]*$\" regex!") + endif() + + if(CPACK_DEBIAN_PACKAGE_RELEASE) + if(NOT CPACK_DEBIAN_PACKAGE_RELEASE MATCHES "^[A-Za-z0-9.+~]+$") + message(FATAL_ERROR + "CPackDeb: Debian package release must confirm to \"^[A-Za-z0-9.+~]+$\" regex!") + endif() + string(APPEND CPACK_DEBIAN_PACKAGE_VERSION + "-${CPACK_DEBIAN_PACKAGE_RELEASE}") + elseif(CPACK_DEBIAN_PACKAGE_VERSION MATCHES ".*-.*") + message(FATAL_ERROR + "CPackDeb: Debian package version must not contain hyphens when CPACK_DEBIAN_PACKAGE_RELEASE is not provided!") + endif() + + if(CPACK_DEBIAN_PACKAGE_EPOCH) + if(NOT CPACK_DEBIAN_PACKAGE_EPOCH MATCHES "^[0-9]+$") + message(FATAL_ERROR + "CPackDeb: Debian package epoch must confirm to \"^[0-9]+$\" regex!") + endif() + set(CPACK_DEBIAN_PACKAGE_VERSION + "${CPACK_DEBIAN_PACKAGE_EPOCH}:${CPACK_DEBIAN_PACKAGE_VERSION}") + endif() + # Architecture: (mandatory) if(CPACK_DEB_PACKAGE_COMPONENT AND CPACK_DEBIAN_${_local_component_name}_PACKAGE_ARCHITECTURE) set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "${CPACK_DEBIAN_${_local_component_name}_PACKAGE_ARCHITECTURE}") @@ -961,11 +1002,6 @@ function(cpack_deb_prepare_package_vars) set(CPACK_DEBIAN_GENERATE_POSTRM 0) endif() - if(NOT CPACK_DEBIAN_PACKAGE_RELEASE) - set(CPACK_DEBIAN_PACKAGE_RELEASE 1) - endif() - - cpack_deb_variable_fallback("CPACK_DEBIAN_FILE_NAME" "CPACK_DEBIAN_${_local_component_name}_FILE_NAME" "CPACK_DEBIAN_FILE_NAME") @@ -974,7 +1010,7 @@ function(cpack_deb_prepare_package_vars) # Patch package file name to be in corrent debian format: # <foo>_<VersionNumber>-<DebianRevisionNumber>_<DebianArchitecture>.deb set(CPACK_OUTPUT_FILE_NAME - "${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_DEBIAN_PACKAGE_VERSION}-${CPACK_DEBIAN_PACKAGE_RELEASE}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb") + "${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}.deb") else() cmake_policy(PUSH) cmake_policy(SET CMP0010 NEW) diff --git a/Modules/CPackRPM.cmake b/Modules/CPackRPM.cmake index 3913494..9f77ec3 100644 --- a/Modules/CPackRPM.cmake +++ b/Modules/CPackRPM.cmake @@ -106,6 +106,16 @@ # group rpm package is generated without component suffix in filename and # package name. # +# .. variable:: CPACK_RPM_PACKAGE_EPOCH +# +# The RPM package epoch +# +# * Mandatory : No +# * Default : - +# +# Optional number that should be incremented when changing versioning schemas +# or fixing mistakes in the version numbers of older packages. +# # .. variable:: CPACK_RPM_PACKAGE_VERSION # # The RPM package version. @@ -530,7 +540,9 @@ # list of path to be excluded. # # * Mandatory : NO -# * Default : /etc /etc/init.d /usr /usr/share /usr/share/doc /usr/bin /usr/lib /usr/lib64 /usr/include +# * Default : /etc /etc/init.d /usr /usr/bin /usr/include /usr/lib +# /usr/libx32 /usr/lib64 /usr/share /usr/share/aclocal +# /usr/share/doc # # May be used to exclude path (directories or files) from the auto-generated # list of paths discovered by CPack RPM. The defaut value contains a @@ -1083,7 +1095,9 @@ function(cpack_rpm_prepare_content_list) endif() if(NOT DEFINED CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST) - set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST /etc /etc/init.d /usr /usr/share /usr/share/doc /usr/bin /usr/lib /usr/lib64 /usr/libx32 /usr/include) + set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST /etc /etc/init.d /usr /usr/bin + /usr/include /usr/lib /usr/libx32 /usr/lib64 + /usr/share /usr/share/aclocal /usr/share/doc ) if(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION) if(CPACK_RPM_PACKAGE_DEBUG) message("CPackRPM:Debug: Adding ${CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION} to builtin omit list.") @@ -1891,11 +1905,16 @@ function(cpack_rpm_generate_package) OUTPUT_STRIP_TRAILING_WHITESPACE) string(REPLACE "\n" ";" RPMBUILD_TAG_LIST "${RPMBUILD_TAG_LIST}") + if(CPACK_RPM_PACKAGE_EPOCH) + set(TMP_RPM_EPOCH "Epoch: ${CPACK_RPM_PACKAGE_EPOCH}") + endif() + # Check if additional fields for RPM spec header are given # There may be some COMPONENT specific variables as well # If component specific var is not provided we use the global one # for each component foreach(_RPM_SPEC_HEADER URL REQUIRES SUGGESTS PROVIDES OBSOLETES PREFIX CONFLICTS AUTOPROV AUTOREQ AUTOREQPROV REQUIRES_PRE REQUIRES_POST REQUIRES_PREUN REQUIRES_POSTUN) + if(CPACK_RPM_PACKAGE_DEBUG) message("CPackRPM:Debug: processing ${_RPM_SPEC_HEADER}") endif() @@ -2501,6 +2520,7 @@ Vendor: \@CPACK_RPM_PACKAGE_VENDOR\@ \@TMP_RPM_AUTOREQPROV\@ \@TMP_RPM_BUILDARCH\@ \@TMP_RPM_PREFIXES\@ +\@TMP_RPM_EPOCH\@ %description -n \@CPACK_RPM_PACKAGE_NAME\@ \@CPACK_RPM_PACKAGE_DESCRIPTION\@ @@ -2560,6 +2580,7 @@ Vendor: \@CPACK_RPM_PACKAGE_VENDOR\@ \@TMP_RPM_AUTOREQPROV\@ \@TMP_RPM_BUILDARCH\@ \@TMP_RPM_PREFIXES\@ +\@TMP_RPM_EPOCH\@ \@TMP_RPM_DEBUGINFO\@ diff --git a/Modules/CompilerId/Xcode-3.pbxproj.in b/Modules/CompilerId/Xcode-3.pbxproj.in index 84f48ae..1cbaa57 100644 --- a/Modules/CompilerId/Xcode-3.pbxproj.in +++ b/Modules/CompilerId/Xcode-3.pbxproj.in @@ -72,6 +72,7 @@ 1DEB928608733DD80010E9CD = { isa = XCBuildConfiguration; buildSettings = { + @id_development_team@ PRODUCT_NAME = CompilerId@id_lang@; }; name = Debug; diff --git a/Modules/FindOpenMP.cmake b/Modules/FindOpenMP.cmake index 4a3edf9..595b0b3 100644 --- a/Modules/FindOpenMP.cmake +++ b/Modules/FindOpenMP.cmake @@ -75,6 +75,7 @@ # the OpenMP specification implemented by the ``<lang>`` compiler. cmake_policy(PUSH) +cmake_policy(SET CMP0054 NEW) # if() quoted variables not dereferenced cmake_policy(SET CMP0057 NEW) # if IN_LIST function(_OPENMP_FLAG_CANDIDATES LANG) diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index b2916b4..10e0e85 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,5 +1,5 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 9) -set(CMake_VERSION_PATCH 20170921) +set(CMake_VERSION_PATCH 20170925) #set(CMake_VERSION_RC 1) diff --git a/Source/CPack/IFW/cmCPackIFWRepository.cxx b/Source/CPack/IFW/cmCPackIFWRepository.cxx index 87e7089..a01fc4e 100644 --- a/Source/CPack/IFW/cmCPackIFWRepository.cxx +++ b/Source/CPack/IFW/cmCPackIFWRepository.cxx @@ -162,7 +162,7 @@ protected: void CharacterDataHandler(const char* data, int length) override { std::string content(data, data + length); - if (content == "" || content == " " || content == " " || + if (content.empty() || content == " " || content == " " || content == "\n") { return; } diff --git a/Source/CTest/cmCTestBuildHandler.cxx b/Source/CTest/cmCTestBuildHandler.cxx index 28fc113..1da42d4 100644 --- a/Source/CTest/cmCTestBuildHandler.cxx +++ b/Source/CTest/cmCTestBuildHandler.cxx @@ -260,11 +260,11 @@ std::string cmCTestBuildHandler::GetMakeCommand() "MakeCommand:" << makeCommand << "\n", this->Quiet); std::string configType = this->CTest->GetConfigType(); - if (configType == "") { + if (configType.empty()) { configType = this->CTest->GetCTestConfiguration("DefaultCTestConfigurationType"); } - if (configType == "") { + if (configType.empty()) { configType = "Release"; } diff --git a/Source/CTest/cmCTestMultiProcessHandler.cxx b/Source/CTest/cmCTestMultiProcessHandler.cxx index c853373..6a7bdc0 100644 --- a/Source/CTest/cmCTestMultiProcessHandler.cxx +++ b/Source/CTest/cmCTestMultiProcessHandler.cxx @@ -491,7 +491,7 @@ void cmCTestMultiProcessHandler::ReadCostData() } // Next part of the file is the failed tests while (std::getline(fin, line)) { - if (line != "") { + if (!line.empty()) { this->LastTestsFailed.push_back(line); } } diff --git a/Source/CTest/cmCTestRunTest.cxx b/Source/CTest/cmCTestRunTest.cxx index 56a9cb8..abdb643 100644 --- a/Source/CTest/cmCTestRunTest.cxx +++ b/Source/CTest/cmCTestRunTest.cxx @@ -494,7 +494,7 @@ bool cmCTestRunTest::StartTest(size_t total) } } // log and return if we did not find the executable - if (this->ActualCommand == "") { + if (this->ActualCommand.empty()) { // if the command was not found create a TestResult object // that has that information this->TestProcess = new cmProcess; @@ -595,7 +595,7 @@ double cmCTestRunTest::ResolveTimeout() { double timeout = this->TestProperties->Timeout; - if (this->CTest->GetStopTime() == "") { + if (this->CTest->GetStopTime().empty()) { return timeout; } struct tm* lctime; diff --git a/Source/CTest/cmCTestSVN.cxx b/Source/CTest/cmCTestSVN.cxx index 087eb38..ce96224 100644 --- a/Source/CTest/cmCTestSVN.cxx +++ b/Source/CTest/cmCTestSVN.cxx @@ -410,7 +410,7 @@ void cmCTestSVN::DoRevisionSVN(Revision const& revision, // Ignore changes in the old revision for external repositories if (revision.Rev == revision.SVNInfo->OldRevision && - revision.SVNInfo->LocalPath != "") { + !revision.SVNInfo->LocalPath.empty()) { return; } diff --git a/Source/CTest/cmCTestSubmitHandler.cxx b/Source/CTest/cmCTestSubmitHandler.cxx index 137cea9..e51e168 100644 --- a/Source/CTest/cmCTestSubmitHandler.cxx +++ b/Source/CTest/cmCTestSubmitHandler.cxx @@ -496,11 +496,11 @@ bool cmCTestSubmitHandler::SubmitUsingHTTP(const std::string& localprefix, ? "" : this->GetOption("RetryCount"); - int delay = retryDelay == "" + int delay = retryDelay.empty() ? atoi(this->CTest->GetCTestConfiguration("CTestSubmitRetryDelay") .c_str()) : atoi(retryDelay.c_str()); - int count = retryCount == "" + int count = retryCount.empty() ? atoi(this->CTest->GetCTestConfiguration("CTestSubmitRetryCount") .c_str()) : atoi(retryCount.c_str()); @@ -1032,14 +1032,14 @@ int cmCTestSubmitHandler::HandleCDashUploadFile(std::string const& file, ? "" : this->GetOption("RetryCount"); unsigned long retryDelay = 0; - if (retryDelayString != "") { + if (!retryDelayString.empty()) { if (!cmSystemTools::StringToULong(retryDelayString.c_str(), &retryDelay)) { cmCTestLog(this->CTest, WARNING, "Invalid value for 'RETRY_DELAY' : " << retryDelayString << std::endl); } } unsigned long retryCount = 0; - if (retryCountString != "") { + if (!retryCountString.empty()) { if (!cmSystemTools::StringToULong(retryCountString.c_str(), &retryCount)) { cmCTestLog(this->CTest, WARNING, "Invalid value for 'RETRY_DELAY' : " << retryCountString << std::endl); @@ -1361,7 +1361,7 @@ int cmCTestSubmitHandler::ProcessHandler() std::string dropMethod(this->CTest->GetCTestConfiguration("DropMethod")); - if (dropMethod == "" || dropMethod == "ftp") { + if (dropMethod.empty() || dropMethod == "ftp") { ofs << "Using drop method: FTP" << std::endl; cmCTestOptionalLog(this->CTest, HANDLER_OUTPUT, " Using FTP submit method" << std::endl diff --git a/Source/CTest/cmCTestTestHandler.cxx b/Source/CTest/cmCTestTestHandler.cxx index f3404a5..5896014 100644 --- a/Source/CTest/cmCTestTestHandler.cxx +++ b/Source/CTest/cmCTestTestHandler.cxx @@ -1596,7 +1596,7 @@ std::string cmCTestTestHandler::FindExecutable( // wasn't specified if (fullPath.empty() && filepath.empty()) { std::string const path = cmSystemTools::FindProgram(filename.c_str()); - if (path != "") { + if (!path.empty()) { resultingConfig.clear(); return path; } @@ -1802,7 +1802,7 @@ void cmCTestTestHandler::ExpandTestsToRunInformationForRerunFailed() if (fileNameSubstring != pattern) { continue; } - if (logName == "") { + if (logName.empty()) { logName = fileName; } else { // if multiple matching logs were found we use the most recently diff --git a/Source/CTest/cmParseCoberturaCoverage.cxx b/Source/CTest/cmParseCoberturaCoverage.cxx index 3e85339..61ce7d4 100644 --- a/Source/CTest/cmParseCoberturaCoverage.cxx +++ b/Source/CTest/cmParseCoberturaCoverage.cxx @@ -78,7 +78,7 @@ protected: } } - if (this->CurFileName == "") { + if (this->CurFileName.empty()) { // Check if this is a path that is relative to our source or // binary directories. for (std::string const& filePath : FilePaths) { @@ -91,7 +91,7 @@ protected: } cmsys::ifstream fin(this->CurFileName.c_str()); - if (this->CurFileName == "" || !fin) { + if (this->CurFileName.empty() || !fin) { this->CurFileName = this->Coverage.BinaryDir + "/" + atts[tagCount + 1]; fin.open(this->CurFileName.c_str()); diff --git a/Source/CTest/cmParseDelphiCoverage.cxx b/Source/CTest/cmParseDelphiCoverage.cxx index 4b781a6..6d82cb2 100644 --- a/Source/CTest/cmParseDelphiCoverage.cxx +++ b/Source/CTest/cmParseDelphiCoverage.cxx @@ -71,7 +71,8 @@ public: } } // Based up what was found, add a line to the coverageVector - if (!beginSet.empty() && line != "" && !blockComFlag && !lineComFlag) { + if (!beginSet.empty() && !line.empty() && !blockComFlag && + !lineComFlag) { coverageVector.push_back(0); } else { coverageVector.push_back(-1); diff --git a/Source/CTest/cmParseJacocoCoverage.cxx b/Source/CTest/cmParseJacocoCoverage.cxx index 56d07e7..7acb5ca 100644 --- a/Source/CTest/cmParseJacocoCoverage.cxx +++ b/Source/CTest/cmParseJacocoCoverage.cxx @@ -36,7 +36,7 @@ protected: } else if (name == "sourcefile") { std::string fileName = atts[1]; - if (this->PackagePath == "") { + if (this->PackagePath.empty()) { if (!this->FindPackagePath(fileName)) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find file: " << this->PackageName << "/" << fileName << std::endl); diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h index b6ea75a..69d0ed6 100644 --- a/Source/cmAlgorithms.h +++ b/Source/cmAlgorithms.h @@ -370,8 +370,7 @@ std::string cmWrap(char prefix, Range const& r, char suffix, template <typename Range, typename T> typename Range::const_iterator cmFindNot(Range const& r, T const& t) { - return std::find_if(r.begin(), r.end(), - std::bind1st(std::not_equal_to<T>(), t)); + return std::find_if(r.begin(), r.end(), [&t](T const& i) { return i != t; }); } template <typename Range> diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index d60ce55..ba50986 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -147,7 +147,7 @@ std::string cmCTest::CurrentTime() std::string cmCTest::GetCostDataFile() { std::string fname = this->GetCTestConfiguration("CostDataFile"); - if (fname == "") { + if (fname.empty()) { fname = this->GetBinaryDir() + "/Testing/Temporary/CTestCostData.txt"; } return fname; @@ -1235,7 +1235,7 @@ std::string cmCTest::SafeBuildIdField(const std::string& value) { std::string safevalue(value); - if (safevalue != "") { + if (!safevalue.empty()) { // Disallow non-filename and non-space whitespace characters. // If they occur, replace them with "" // @@ -1254,7 +1254,7 @@ std::string cmCTest::SafeBuildIdField(const std::string& value) } } - if (safevalue == "") { + if (safevalue.empty()) { safevalue = "(empty)"; } diff --git a/Source/cmDependsJavaParserHelper.cxx b/Source/cmDependsJavaParserHelper.cxx index 85dfc7d..f227cf2 100644 --- a/Source/cmDependsJavaParserHelper.cxx +++ b/Source/cmDependsJavaParserHelper.cxx @@ -310,7 +310,7 @@ void cmDependsJavaParserHelper::Error(const char* str) void cmDependsJavaParserHelper::UpdateCombine(const char* str1, const char* str2) { - if (this->CurrentCombine == "" && str1 != nullptr) { + if (this->CurrentCombine.empty() && str1 != nullptr) { this->CurrentCombine = str1; } this->CurrentCombine += "."; diff --git a/Source/cmFindLibraryCommand.cxx b/Source/cmFindLibraryCommand.cxx index 09a9648..b8b51ba 100644 --- a/Source/cmFindLibraryCommand.cxx +++ b/Source/cmFindLibraryCommand.cxx @@ -68,8 +68,8 @@ bool cmFindLibraryCommand::InitialPass(std::vector<std::string> const& argsIn, this->AddArchitecturePaths("x32"); } - std::string library = this->FindLibrary(); - if (library != "") { + std::string const library = this->FindLibrary(); + if (!library.empty()) { // Save the value in the cache this->Makefile->AddCacheDefinition(this->VariableName, library.c_str(), this->VariableDocumentation.c_str(), diff --git a/Source/cmFindProgramCommand.cxx b/Source/cmFindProgramCommand.cxx index a5dc1c6..2059b3d 100644 --- a/Source/cmFindProgramCommand.cxx +++ b/Source/cmFindProgramCommand.cxx @@ -97,8 +97,8 @@ bool cmFindProgramCommand::InitialPass(std::vector<std::string> const& argsIn, return true; } - std::string result = FindProgram(); - if (result != "") { + std::string const result = FindProgram(); + if (!result.empty()) { // Save the value in the cache this->Makefile->AddCacheDefinition(this->VariableName, result.c_str(), this->VariableDocumentation.c_str(), diff --git a/Source/cmGlobalUnixMakefileGenerator3.cxx b/Source/cmGlobalUnixMakefileGenerator3.cxx index 15ddeff..be40126 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.cxx +++ b/Source/cmGlobalUnixMakefileGenerator3.cxx @@ -230,7 +230,7 @@ void cmGlobalUnixMakefileGenerator3::WriteMainMakefile2() // The all and preinstall rules might never have any dependencies // added to them. - if (this->EmptyRuleHackDepends != "") { + if (!this->EmptyRuleHackDepends.empty()) { depends.push_back(this->EmptyRuleHackDepends); } @@ -438,7 +438,7 @@ void cmGlobalUnixMakefileGenerator3::WriteDirectoryRule2( // Work-around for makes that drop rules that have no dependencies // or commands. - if (depends.empty() && this->EmptyRuleHackDepends != "") { + if (depends.empty() && !this->EmptyRuleHackDepends.empty()) { depends.push_back(this->EmptyRuleHackDepends); } diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index ece2a77..0651536 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -150,7 +150,7 @@ void cmGlobalVisualStudioGenerator::ConfigureCMakeVisualStudioMacros() { std::string dir = this->GetUserMacrosDirectory(); - if (dir != "") { + if (!dir.empty()) { std::string src = cmSystemTools::GetCMakeRoot(); src += "/Templates/" CMAKE_VSMACROS_FILENAME; @@ -190,7 +190,7 @@ void cmGlobalVisualStudioGenerator::CallVisualStudioMacro( // - the CMake vsmacros file is registered // - there were .sln/.vcproj files changed during generation // - if (dir != "") { + if (!dir.empty()) { std::string macrosFile = dir + "/CMakeMacros/" CMAKE_VSMACROS_FILENAME; std::string nextSubkeyName; if (cmSystemTools::FileExists(macrosFile.c_str()) && diff --git a/Source/cmGraphVizWriter.cxx b/Source/cmGraphVizWriter.cxx index 967fd2e..ca87cbf 100644 --- a/Source/cmGraphVizWriter.cxx +++ b/Source/cmGraphVizWriter.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmGraphVizWriter.h" +#include <cstddef> #include <iostream> #include <sstream> #include <utility> @@ -17,7 +18,18 @@ #include "cm_auto_ptr.hxx" #include "cmake.h" -static const char* getShapeForTarget(const cmGeneratorTarget* target) +namespace { +enum LinkLibraryScopeType +{ + LLT_SCOPE_PUBLIC, + LLT_SCOPE_PRIVATE, + LLT_SCOPE_INTERFACE +}; + +const char* const GRAPHVIZ_PRIVATE_EDEGE_STYLE = "dashed"; +const char* const GRAPHVIZ_INTERFACE_EDEGE_STYLE = "dotted"; + +const char* getShapeForTarget(const cmGeneratorTarget* target) { if (!target) { return "ellipse"; @@ -39,6 +51,76 @@ static const char* getShapeForTarget(const cmGeneratorTarget* target) return "box"; } +std::map<std::string, LinkLibraryScopeType> getScopedLinkLibrariesFromTarget( + cmTarget* Target) +{ + char sep = ';'; + std::map<std::string, LinkLibraryScopeType> tokens; + size_t start = 0, end = 0; + + const char* pInterfaceLinkLibraries = + Target->GetProperty("INTERFACE_LINK_LIBRARIES"); + const char* pLinkLibraries = Target->GetProperty("LINK_LIBRARIES"); + + if (!pInterfaceLinkLibraries && !pLinkLibraries) { + return tokens; // target is not linked against any other libraries + } + + // make sure we don't touch a null-ptr + auto interfaceLinkLibraries = + std::string(pInterfaceLinkLibraries ? pInterfaceLinkLibraries : ""); + auto linkLibraries = std::string(pLinkLibraries ? pLinkLibraries : ""); + + // first extract interfaceLinkLibraries + while (start < interfaceLinkLibraries.length()) { + + if ((end = interfaceLinkLibraries.find(sep, start)) == std::string::npos) { + end = interfaceLinkLibraries.length(); + } + + std::string element = interfaceLinkLibraries.substr(start, end - start); + if (std::string::npos == element.find("$<LINK_ONLY:", 0)) { + // we assume first, that this library is an interface library. + // if we find it again in the linklibraries property, we promote it to an + // public library. + tokens[element] = LLT_SCOPE_INTERFACE; + } else { + // this is an private linked static library. + // we take care of this case in the second iterator. + } + start = end + 1; + } + + // second extract linkLibraries + start = 0; + while (start < linkLibraries.length()) { + + if ((end = linkLibraries.find(sep, start)) == std::string::npos) { + end = linkLibraries.length(); + } + + std::string element = linkLibraries.substr(start, end - start); + + if (tokens.find(element) == tokens.end()) { + // this library is not found in interfaceLinkLibraries but in + // linkLibraries. + // this results in a private linked library. + tokens[element] = LLT_SCOPE_PRIVATE; + } else if (LLT_SCOPE_INTERFACE == tokens[element]) { + // this library is found in interfaceLinkLibraries and linkLibraries. + // this results in a public linked library. + tokens[element] = LLT_SCOPE_PUBLIC; + } else { + // private and public linked libraries should not be changed anymore. + } + + start = end + 1; + } + + return tokens; +} +} + cmGraphVizWriter::cmGraphVizWriter( const std::vector<cmLocalGenerator*>& localGenerators) : GraphType("digraph") @@ -273,11 +355,10 @@ void cmGraphVizWriter::WriteConnections( } std::string myNodeName = this->TargetNamesNodes.find(targetName)->second; + std::map<std::string, LinkLibraryScopeType> ll = + getScopedLinkLibrariesFromTarget(targetPtrIt->second->Target); - const cmTarget::LinkLibraryVectorType* ll = - &(targetPtrIt->second->Target->GetOriginalLinkLibraries()); - - for (auto const& llit : *ll) { + for (auto const& llit : ll) { const char* libName = llit.first.c_str(); std::map<std::string, std::string>::const_iterator libNameIt = this->TargetNamesNodes.find(libName); @@ -297,6 +378,18 @@ void cmGraphVizWriter::WriteConnections( insertedNodes, str); str << " \"" << myNodeName << "\" -> \"" << libNameIt->second << "\""; + + switch (llit.second) { + case LLT_SCOPE_PRIVATE: + str << "[style = " << GRAPHVIZ_PRIVATE_EDEGE_STYLE << "]"; + break; + case LLT_SCOPE_INTERFACE: + str << "[style = " << GRAPHVIZ_INTERFACE_EDEGE_STYLE << "]"; + break; + default: + break; + } + str << " // " << targetName << " -> " << libName << std::endl; this->WriteConnections(libName, insertedNodes, insertedConnections, str); } diff --git a/Source/cmLoadCommandCommand.cxx b/Source/cmLoadCommandCommand.cxx index 2850032..6bfac17 100644 --- a/Source/cmLoadCommandCommand.cxx +++ b/Source/cmLoadCommandCommand.cxx @@ -197,7 +197,7 @@ bool cmLoadCommandCommand::InitialPass(std::vector<std::string> const& args, // Try to find the program. std::string fullPath = cmSystemTools::FindFile(moduleName, path); - if (fullPath == "") { + if (fullPath.empty()) { std::ostringstream e; e << "Attempt to load command failed from file \"" << moduleName << "\""; this->SetError(e.str()); diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index e1662ac..1a088ea 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -1410,7 +1410,7 @@ bool cmLocalGenerator::GetRealDependency(const std::string& inName, // If the input name is the empty string, there is no real // dependency. Short-circuit the other checks: - if (name == "") { + if (name.empty()) { return false; } @@ -2096,7 +2096,7 @@ void cmLocalGenerator::GenerateTargetInstallRules( } // Install this target if a destination is given. - if (l->Target->GetInstallPath() != "") { + if (!l->Target->GetInstallPath().empty()) { // Compute the full install destination. Note that converting // to unix slashes also removes any trailing slash. // We also skip over the leading slash given by the user. diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index df02f84..50ed0ed 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -5,7 +5,6 @@ #include "cmsys/FStream.hxx" #include "cmsys/Terminal.h" #include <algorithm> -#include <functional> #include <sstream> #include <stdio.h> #include <utility> @@ -2073,7 +2072,7 @@ void cmLocalUnixMakefileGenerator3::CreateCDCommand( std::string outputForExisting = this->ConvertToOutputForExisting(tgtDir); std::string prefix = cd_cmd + outputForExisting + " && "; std::transform(commands.begin(), commands.end(), commands.begin(), - std::bind1st(std::plus<std::string>(), prefix)); + [&prefix](std::string const& s) { return prefix + s; }); } } diff --git a/Source/cmLocalVisualStudio7Generator.cxx b/Source/cmLocalVisualStudio7Generator.cxx index c978936..a1771ee 100644 --- a/Source/cmLocalVisualStudio7Generator.cxx +++ b/Source/cmLocalVisualStudio7Generator.cxx @@ -1591,7 +1591,7 @@ bool cmLocalVisualStudio7Generator::WriteGroup( // If the group has a name, write the header. std::string name = sg->GetName(); - if (name != "") { + if (!name.empty()) { this->WriteVCProjBeginGroup(fout, name.c_str(), ""); } @@ -1709,7 +1709,7 @@ bool cmLocalVisualStudio7Generator::WriteGroup( } // If the group has a name, write the footer. - if (name != "") { + if (!name.empty()) { this->WriteVCProjEndGroup(fout); } @@ -2006,7 +2006,7 @@ void cmLocalVisualStudio7Generator::WriteVCProjFooter( i != props.end(); ++i) { if (i->find("VS_GLOBAL_") == 0) { std::string name = i->substr(10); - if (name != "") { + if (!name.empty()) { /* clang-format off */ fout << "\t\t<Global\n" << "\t\t\tName=\"" << name << "\"\n" diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index e51cfcc..c96b892 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -3082,9 +3082,18 @@ void cmMakefile::SetArgcArgv(const std::vector<std::string>& args) cmSourceFile* cmMakefile::GetSource(const std::string& sourceName) const { cmSourceFileLocation sfl(this, sourceName); - for (cmSourceFile* sf : this->SourceFiles) { - if (sf->Matches(sfl)) { - return sf; + +#if defined(_WIN32) || defined(__APPLE__) + const auto& name = cmSystemTools::LowerCase(sfl.GetName()); +#else + const auto& name = sfl.GetName(); +#endif + auto sfsi = this->SourceFileSearchIndex.find(name); + if (sfsi != this->SourceFileSearchIndex.end()) { + for (auto sf : sfsi->second) { + if (sf->Matches(sfl)) { + return sf; + } } } return nullptr; @@ -3098,6 +3107,41 @@ cmSourceFile* cmMakefile::CreateSource(const std::string& sourceName, sf->SetProperty("GENERATED", "1"); } this->SourceFiles.push_back(sf); + + auto name = sf->GetLocation().GetName(); +#if defined(_WIN32) || defined(__APPLE__) + name = cmSystemTools::LowerCase(name); +#endif + + // For a file in the form "a.b.c" add the cmSourceFile to the index + // at "a.b.c", "a.b" and "a". + auto partial = name; + while (true) { + this->SourceFileSearchIndex[partial].insert(sf); + auto i = partial.rfind('.'); + if (i == std::string::npos) { + break; + } + partial = partial.substr(0, i); + } + + if (sf->GetLocation().ExtensionIsAmbiguous()) { + // For an ambiguous extension also add the various "known" + // extensions to the original filename. + + const auto& srcExts = this->GetCMakeInstance()->GetSourceExtensions(); + for (const auto& ext : srcExts) { + auto name_ext = name + "." + cmSystemTools::LowerCase(ext); + this->SourceFileSearchIndex[name_ext].insert(sf); + } + + const auto& hdrExts = this->GetCMakeInstance()->GetHeaderExtensions(); + for (const auto& ext : hdrExts) { + auto name_ext = name + "." + cmSystemTools::LowerCase(ext); + this->SourceFileSearchIndex[name_ext].insert(sf); + } + } + return sf; } diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index 398604d..2cae659 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -13,6 +13,7 @@ #include <stddef.h> #include <string> #include <unordered_map> +#include <unordered_set> #include <vector> #include "cmAlgorithms.h" @@ -623,7 +624,6 @@ public: { return this->SourceFiles; } - std::vector<cmSourceFile*>& GetSourceFiles() { return this->SourceFiles; } /** * Is there a source file that has the provided source file as an output? @@ -809,7 +809,17 @@ protected: // libraries, classes, and executables mutable cmTargets Targets; std::map<std::string, std::string> AliasTargets; + std::vector<cmSourceFile*> SourceFiles; + // Because cmSourceFile names are compared in a fuzzy way (see + // cmSourceFileLocation::Match()) we can't have a straight mapping from + // filename to cmSourceFile. To make lookups more efficient we store the + // Name portion of the cmSourceFileLocation and then compare on the list of + // cmSourceFiles that might match that name. Note that on platforms which + // have a case-insensitive filesystem we store the key in all lowercase. + typedef std::unordered_set<cmSourceFile*> SourceFileSet; + typedef std::unordered_map<std::string, SourceFileSet> SourceFileMap; + SourceFileMap SourceFileSearchIndex; // Tests std::map<std::string, cmTest*> Tests; diff --git a/Source/cmOutputRequiredFilesCommand.cxx b/Source/cmOutputRequiredFilesCommand.cxx index c430298..cde9037 100644 --- a/Source/cmOutputRequiredFilesCommand.cxx +++ b/Source/cmOutputRequiredFilesCommand.cxx @@ -510,7 +510,7 @@ void cmOutputRequiredFilesCommand::ListDependencies( // now recurse with info's dependencies for (cmDependInformation* d : info->DependencySet) { if (visited->find(d) == visited->end()) { - if (info->FullPath != "") { + if (!info->FullPath.empty()) { std::string tmp = d->FullPath; std::string::size_type pos = tmp.rfind('.'); if (pos != std::string::npos && (tmp.substr(pos) != ".h")) { diff --git a/Source/cmPolicies.cxx b/Source/cmPolicies.cxx index a1f346a..e7d1b72 100644 --- a/Source/cmPolicies.cxx +++ b/Source/cmPolicies.cxx @@ -140,7 +140,7 @@ static bool GetPolicyDefault(cmMakefile* mf, std::string const& policy, *defaultSetting = cmPolicies::NEW; } else if (defaultValue == "OLD") { *defaultSetting = cmPolicies::OLD; - } else if (defaultValue == "") { + } else if (defaultValue.empty()) { *defaultSetting = cmPolicies::WARN; } else { std::ostringstream e; diff --git a/Source/cmQtAutoGen.cxx b/Source/cmQtAutoGen.cxx index 95cd122..d9b1fd3 100644 --- a/Source/cmQtAutoGen.cxx +++ b/Source/cmQtAutoGen.cxx @@ -2,6 +2,7 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmQtAutoGen.h" #include "cmAlgorithms.h" +#include "cmProcessOutput.h" #include "cmSystemTools.h" #include "cmsys/FStream.hxx" @@ -158,15 +159,18 @@ static bool RccListInputsQt5(const std::string& rccCommand, std::string rccStdOut; std::string rccStdErr; int retVal = 0; - bool result = - cmSystemTools::RunSingleCommand(command, &rccStdOut, &rccStdErr, &retVal, - nullptr, cmSystemTools::OUTPUT_NONE); + 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); + // Run rcc list command bool result = false; int retVal = 0; @@ -176,16 +180,16 @@ static bool RccListInputsQt5(const std::string& rccCommand, std::vector<std::string> command; command.push_back(rccCommand); command.push_back(hasDashDashList ? "--list" : "-list"); - command.push_back(fileName); - result = - cmSystemTools::RunSingleCommand(command, &rccStdOut, &rccStdErr, &retVal, - nullptr, cmSystemTools::OUTPUT_NONE); + command.push_back(fileNameName); + result = cmSystemTools::RunSingleCommand( + command, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(), + cmSystemTools::OUTPUT_NONE, 0.0, cmProcessOutput::Auto); } if (!result || retVal) { if (errorMessage != nullptr) { std::ostringstream ost; - ost << "rcc list process for " << cmQtAutoGen::Quoted(fileName) - << " failed:\n" + ost << "rcc list process failed for\n " << cmQtAutoGen::Quoted(fileName) + << "\n" << rccStdOut << "\n" << rccStdErr << "\n"; *errorMessage = ost.str(); @@ -230,6 +234,11 @@ static bool RccListInputsQt5(const std::string& rccCommand, } } + // Convert relative paths to absolute paths + for (std::string& resFile : files) { + resFile = cmSystemTools::CollapseCombinedPath(fileDir, resFile); + } + return true; } diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx index 8c03045..e8a9006 100644 --- a/Source/cmQtAutoGenerators.cxx +++ b/Source/cmQtAutoGenerators.cxx @@ -8,6 +8,7 @@ #include <algorithm> #include <array> #include <list> +#include <memory> #include <sstream> #include <string.h> #include <utility> @@ -35,14 +36,23 @@ static const char* SettingsKeyRcc = "AM_RCC_SETTINGS_HASH"; // -- Static functions -static std::string QuotedCommand(const std::vector<std::string>& command) +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<std::string> const& command) { std::string res; for (std::string const& item : command) { if (!res.empty()) { res.push_back(' '); } - const std::string cesc = cmQtAutoGen::Quoted(item); + std::string const cesc = cmQtAutoGen::Quoted(item); if (item.empty() || (cesc.size() > (item.size() + 2)) || (cesc.find(' ') != std::string::npos)) { res += cesc; @@ -53,7 +63,7 @@ static std::string QuotedCommand(const std::vector<std::string>& command) return res; } -static std::string SubDirPrefix(const std::string& fileName) +static std::string SubDirPrefix(std::string const& fileName) { std::string res(cmSystemTools::GetFilenamePath(fileName)); if (!res.empty()) { @@ -62,36 +72,58 @@ static std::string SubDirPrefix(const std::string& fileName) return res; } -static bool ReadAll(std::string& content, const std::string& filename) +static bool ReadFile(std::string& content, std::string const& filename, + std::string* error = nullptr) { bool success = false; - { - cmsys::ifstream ifs(filename.c_str()); + 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) { - std::ostringstream osst; - osst << ifs.rdbuf(); - content = osst.str(); - success = true; + 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 doesn't exist or is older than sourceFile - * @return True if buildFile doesn't exist or is older than sourceFile + * @brief Tests if buildFile is older than sourceFile + * @return True if buildFile is older than sourceFile. + * False may indicate an error. */ -static bool FileAbsentOrOlder(const std::string& buildFile, - const std::string& sourceFile) +static bool FileIsOlderThan(std::string const& buildFile, + std::string const& sourceFile, + std::string* error = nullptr) { int result = 0; - bool success = - cmSystemTools::FileTimeCompare(buildFile, sourceFile, &result); - return (!success || (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(const std::vector<std::string>& list, - const std::string& entry) +static bool ListContains(std::vector<std::string> const& list, + std::string const& entry) { return (std::find(list.begin(), list.end(), entry) != list.end()); } @@ -99,24 +131,20 @@ static bool ListContains(const std::vector<std::string>& list, // -- Class methods cmQtAutoGenerators::cmQtAutoGenerators() - : Verbose(cmSystemTools::HasEnv("VERBOSE")) + : IncludeProjectDirsBefore(false) + , Verbose(cmSystemTools::HasEnv("VERBOSE")) , ColorOutput(true) , MocSettingsChanged(false) , MocPredefsChanged(false) - , MocRunFailed(false) + , MocRelaxedMode(false) , UicSettingsChanged(false) - , UicRunFailed(false) , RccSettingsChanged(false) - , RccRunFailed(false) { - - std::string colorEnv; - cmSystemTools::GetEnv("COLOR", colorEnv); - if (!colorEnv.empty()) { - if (cmSystemTools::IsOn(colorEnv.c_str())) { - this->ColorOutput = true; - } else { - this->ColorOutput = false; + { + std::string colorEnv; + cmSystemTools::GetEnv("COLOR", colorEnv); + if (!colorEnv.empty()) { + this->ColorOutput = cmSystemTools::IsOn(colorEnv.c_str()); } } @@ -134,8 +162,8 @@ cmQtAutoGenerators::cmQtAutoGenerators() "[\"<](([^ \">]+/)?ui_[^ \">/]+\\.h)[\">]"); } -bool cmQtAutoGenerators::Run(const std::string& targetDirectory, - const std::string& config) +bool cmQtAutoGenerators::Run(std::string const& targetDirectory, + std::string const& config) { cmake cm(cmake::RoleScript); cm.SetHomeOutputDirectory(targetDirectory); @@ -147,16 +175,14 @@ bool cmQtAutoGenerators::Run(const std::string& targetDirectory, snapshot.GetDirectory().SetCurrentBinary(targetDirectory); snapshot.GetDirectory().SetCurrentSource(targetDirectory); - std::unique_ptr<cmMakefile> makefile(new cmMakefile(&gg, snapshot)); + auto makefile = cm::make_unique<cmMakefile>(&gg, snapshot); gg.SetCurrentMakefile(makefile.get()); bool success = false; - if (this->ReadAutogenInfoFile(makefile.get(), targetDirectory, config)) { - // Read old settings + if (this->InitInfoFile(makefile.get(), targetDirectory, config)) { + // Read latest settings this->SettingsFileRead(makefile.get()); - // Init and run - this->Init(makefile.get()); - if (this->RunAutogen()) { + if (this->Process()) { // Write current settings if (this->SettingsFileWrite()) { success = true; @@ -166,46 +192,11 @@ bool cmQtAutoGenerators::Run(const std::string& targetDirectory, return success; } -bool cmQtAutoGenerators::MocDependFilterPush(const std::string& key, - const std::string& regExp) +bool cmQtAutoGenerators::InitInfoFile(cmMakefile* makefile, + std::string const& targetDirectory, + std::string const& config) { - 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; -} - -bool cmQtAutoGenerators::ReadAutogenInfoFile( - cmMakefile* makefile, const std::string& targetDirectory, - const std::string& config) -{ - // Lambdas + // Utility lambdas auto InfoGet = [makefile](const char* key) { return makefile->GetSafeDefinition(key); }; @@ -221,7 +212,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( [makefile](const char* key) -> std::vector<std::vector<std::string>> { std::vector<std::vector<std::string>> lists; { - const std::string value = makefile->GetSafeDefinition(key); + 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); @@ -266,16 +257,18 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( return list; }; - std::string filename(cmSystemTools::CollapseFullPath(targetDirectory)); - cmSystemTools::ConvertToUnixSlashes(filename); - filename += "/AutogenInfo.cmake"; + this->InfoFile = cmSystemTools::CollapseFullPath(targetDirectory); + cmSystemTools::ConvertToUnixSlashes(this->InfoFile); + this->InfoFile += "/AutogenInfo.cmake"; - if (!makefile->ReadListFile(filename.c_str())) { - this->LogFileError(cmQtAutoGen::GEN, filename, "File processing failed"); + if (!makefile->ReadListFile(this->InfoFile.c_str())) { + this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + "File processing failed"); return false; } // -- Meta + this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); this->ConfigSuffix = InfoGetConfig("AM_CONFIG_SUFFIX"); // - Old settings file @@ -296,12 +289,10 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( InfoGetBool("AM_CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE"); this->AutogenBuildDir = InfoGet("AM_BUILD_DIR"); if (this->AutogenBuildDir.empty()) { - this->LogFileError(cmQtAutoGen::GEN, filename, + this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, "Autogen build directory missing"); return false; } - this->Sources = InfoGetList("AM_SOURCES"); - this->Headers = InfoGetList("AM_HEADERS"); // - Qt environment this->QtMajorVersion = InfoGet("AM_QT_VERSION_MAJOR"); @@ -312,7 +303,8 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( // Check Qt version if ((this->QtMajorVersion != "4") && (this->QtMajorVersion != "5")) { - this->LogFileError(cmQtAutoGen::GEN, filename, "Unsupported Qt version: " + + this->LogFileError(cmQtAutoGen::GEN, this->InfoFile, + "Unsupported Qt version: " + cmQtAutoGen::Quoted(this->QtMajorVersion)); return false; } @@ -323,7 +315,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( this->MocDefinitions = InfoGetConfigList("AM_MOC_DEFINITIONS"); #ifdef _WIN32 { - const std::string win32("WIN32"); + std::string const win32("WIN32"); if (!ListContains(this->MocDefinitions, win32)) { this->MocDefinitions.push_back(win32); } @@ -333,7 +325,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( this->MocOptions = InfoGetList("AM_MOC_OPTIONS"); this->MocRelaxedMode = InfoGetBool("AM_MOC_RELAXED_MODE"); { - const std::vector<std::string> MocMacroNames = + std::vector<std::string> const MocMacroNames = InfoGetList("AM_MOC_MACRO_NAMES"); for (std::string const& item : MocMacroNames) { this->MocMacroFilters.emplace_back( @@ -341,7 +333,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( } } { - const std::vector<std::string> mocDependFilters = + std::vector<std::string> const mocDependFilters = InfoGetList("AM_MOC_DEPEND_FILTERS"); // Insert Q_PLUGIN_METADATA dependency filter if (this->QtMajorVersion != "4") { @@ -361,7 +353,7 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( } } else { this->LogFileError( - cmQtAutoGen::MOC, filename, + cmQtAutoGen::MOC, this->InfoFile, "AUTOMOC_DEPEND_FILTERS list size is not a multiple of 2"); return false; } @@ -378,22 +370,21 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( auto sources = InfoGetList("AM_UIC_OPTIONS_FILES"); auto options = InfoGetLists("AM_UIC_OPTIONS_OPTIONS"); // Compare list sizes - if (sources.size() == options.size()) { - auto fitEnd = sources.cend(); - auto fit = sources.begin(); - auto oit = options.begin(); - while (fit != fitEnd) { - this->UicOptions[*fit] = std::move(*oit); - ++fit; - ++oit; - } - } else { + if (sources.size() != options.size()) { std::ostringstream ost; ost << "files/options lists sizes missmatch (" << sources.size() << "/" << options.size() << ")"; - this->LogFileError(cmQtAutoGen::UIC, filename, ost.str()); + 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; + } } } @@ -409,24 +400,23 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( std::ostringstream ost; ost << "sources, builds lists sizes missmatch (" << sources.size() << "/" << builds.size() << ")"; - this->LogFileError(cmQtAutoGen::RCC, filename, ost.str()); + 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, filename, ost.str()); + 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, filename, ost.str()); + this->LogFileError(cmQtAutoGen::RCC, this->InfoFile, ost.str()); return false; } - { auto srcItEnd = sources.end(); auto srcIt = sources.begin(); @@ -445,6 +435,156 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile( } } + // Initialize source file jobs + { + // Utility lambdas + auto AddJob = [this](std::map<std::string, SourceJob>& 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<std::string> 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<std::string, 2> 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"; + this->AutogenIncludeDir += this->ConfigSuffix; + this->AutogenIncludeDir += "/"; + + if (this->MocEnabled()) { + // Mocs compilation file + this->MocCompFileRel = "mocs_compilation.cpp"; + this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, this->MocCompFileRel); + + // Moc predefs file + if (!this->MocPredefsCmd.empty()) { + this->MocPredefsFileRel = "moc_predefs"; + 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<std::string> 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<std::string const*, 2> const movePaths = { + { &this->ProjectBinaryDir, &this->ProjectSourceDir } + }; + for (std::string const* ppath : movePaths) { + std::list<std::string>::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<std::string> 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<std::string> 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()); + } + } + return true; } @@ -453,16 +593,12 @@ void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile) // Compose current settings strings { cmCryptoHash crypt(cmCryptoHash::AlgoSHA256); - const std::string sep(" ~~~ "); + std::string const sep(" ~~~ "); if (this->MocEnabled()) { std::string str; str += this->MocExecutable; str += sep; - str += cmJoin(this->MocDefinitions, ";"); - str += sep; - str += cmJoin(this->MocIncludePaths, ";"); - str += sep; - str += cmJoin(this->MocOptions, ";"); + str += cmJoin(this->MocAllOptions, ";"); str += sep; str += this->IncludeProjectDirsBefore ? "TRUE" : "FALSE"; str += sep; @@ -503,7 +639,7 @@ void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile) // Read old settings if (makefile->ReadListFile(this->SettingsFile.c_str())) { { - auto SMatch = [makefile](const char* key, const std::string& value) { + auto SMatch = [makefile](const char* key, std::string const& value) { return (value == makefile->GetSafeDefinition(key)); }; if (!SMatch(SettingsKeyMoc, this->SettingsStringMoc)) { @@ -519,7 +655,7 @@ void cmQtAutoGenerators::SettingsFileRead(cmMakefile* makefile) // 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->AnySettingsChanged()) { + if (this->SettingsChanged()) { cmSystemTools::RemoveFile(this->SettingsFile); } } else { @@ -534,7 +670,7 @@ bool cmQtAutoGenerators::SettingsFileWrite() { bool success = true; // Only write if any setting changed - if (this->AnySettingsChanged()) { + if (this->SettingsChanged()) { if (this->Verbose) { this->LogInfo(cmQtAutoGen::GEN, "Writing settings file " + cmQtAutoGen::Quoted(this->SettingsFile)); @@ -543,7 +679,7 @@ bool cmQtAutoGenerators::SettingsFileWrite() std::string settings; { auto SettingAppend = [&settings](const char* key, - const std::string& value) { + std::string const& value) { settings += "set("; settings += key; settings += " "; @@ -557,7 +693,7 @@ bool cmQtAutoGenerators::SettingsFileWrite() // Write settings file if (!this->FileWrite(cmQtAutoGen::GEN, this->SettingsFile, settings)) { this->LogFileError(cmQtAutoGen::GEN, this->SettingsFile, - "File writing failed"); + "Settings file writing failed"); // Remove old settings file to trigger a full rebuild on the next run cmSystemTools::RemoveFile(this->SettingsFile); success = false; @@ -566,89 +702,7 @@ bool cmQtAutoGenerators::SettingsFileWrite() return success; } -void cmQtAutoGenerators::Init(cmMakefile* makefile) -{ - // Mocs compilation file - this->MocCompFileRel = "mocs_compilation.cpp"; - this->MocCompFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->MocCompFileRel); - - // Mocs include directory - this->AutogenIncludeDir = "include"; - this->AutogenIncludeDir += this->ConfigSuffix; - this->AutogenIncludeDir += "/"; - - // Moc predefs file - if (!this->MocPredefsCmd.empty()) { - this->MocPredefsFileRel = "moc_predefs"; - this->MocPredefsFileRel += this->ConfigSuffix; - this->MocPredefsFileRel += ".h"; - this->MocPredefsFileAbs = cmSystemTools::CollapseCombinedPath( - this->AutogenBuildDir, this->MocPredefsFileRel); - } - - // Init file path checksum generator - FPathChecksum.setupParentDirs(this->CurrentSourceDir, this->CurrentBinaryDir, - this->ProjectSourceDir, - this->ProjectBinaryDir); - - // Acquire header extensions - this->HeaderExtensions = makefile->GetCMakeInstance()->GetHeaderExtensions(); - - // Sort include directories on demand - if (this->IncludeProjectDirsBefore) { - // Move strings to temporary list - std::list<std::string> includes; - includes.insert(includes.end(), this->MocIncludePaths.begin(), - this->MocIncludePaths.end()); - this->MocIncludePaths.clear(); - this->MocIncludePaths.reserve(includes.size()); - // Append project directories only - { - const std::array<const std::string*, 2> movePaths = { - { &this->ProjectBinaryDir, &this->ProjectSourceDir } - }; - for (const std::string* ppath : movePaths) { - std::list<std::string>::iterator it = includes.begin(); - while (it != includes.end()) { - const std::string& 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<std::string> 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<std::string> 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); - } - } -} - -bool cmQtAutoGenerators::RunAutogen() +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 @@ -660,7 +714,7 @@ bool cmQtAutoGenerators::RunAutogen() // Create AUTOGEN include directory { - const std::string incDirAbs = cmSystemTools::CollapseCombinedPath( + std::string const incDirAbs = cmSystemTools::CollapseCombinedPath( this->AutogenBuildDir, this->AutogenIncludeDir); if (!cmSystemTools::MakeDirectory(incDirAbs)) { this->LogFileError(cmQtAutoGen::GEN, incDirAbs, @@ -669,45 +723,28 @@ bool cmQtAutoGenerators::RunAutogen() } } - // key = moc source filepath, value = moc output filepath - std::map<std::string, std::string> mocsIncluded; - std::map<std::string, std::string> mocsNotIncluded; - std::map<std::string, std::set<std::string>> mocDepends; - std::map<std::string, std::vector<std::string>> uisIncluded; - // collects all headers which may need to be mocced - std::set<std::string> mocHeaderFiles; - std::set<std::string> uicHeaderFiles; - - // Parse sources - for (std::string const& src : this->Sources) { - // Parse source file for MOC/UIC - if (!this->ParseSourceFile(src, mocsIncluded, mocDepends, uisIncluded, - this->MocRelaxedMode)) { + // Parse source files + for (const auto& item : this->SourceJobs) { + if (!this->ParseSourceFile(item.first, item.second)) { return false; } - // Find additional headers - this->SearchHeadersForSourceFile(src, mocHeaderFiles, uicHeaderFiles); } - - // Parse headers - for (std::string const& hdr : this->Headers) { - if (!this->MocSkip(hdr)) { - mocHeaderFiles.insert(hdr); - } - if (!this->UicSkip(hdr)) { - uicHeaderFiles.insert(hdr); + // Parse header files + for (const auto& item : this->HeaderJobs) { + if (!this->ParseHeaderFile(item.first, item.second)) { + return false; } } - if (!this->ParseHeaders(mocHeaderFiles, uicHeaderFiles, mocsIncluded, - mocsNotIncluded, mocDepends, uisIncluded)) { + // Read missing dependency information + if (!this->ParsePostprocess()) { return false; - }; + } // Generate files - if (!this->MocGenerateAll(mocsIncluded, mocsNotIncluded, mocDepends)) { + if (!this->MocGenerateAll()) { return false; } - if (!this->UicGenerateAll(uisIncluded)) { + if (!this->UicGenerateAll()) { return false; } if (!this->RccGenerateAll()) { @@ -718,87 +755,100 @@ bool cmQtAutoGenerators::RunAutogen() } /** - * @brief Tests if the C++ content requires moc processing - * @return True if moc is required + * @return True on success */ -bool cmQtAutoGenerators::MocRequired(const std::string& contentText, - std::string* macroName) +bool cmQtAutoGenerators::ParseSourceFile(std::string const& absFilename, + const SourceJob& job) { - 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; + 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 false; + return success; } -void cmQtAutoGenerators::MocFindDepends( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::set<std::string>>& mocDepends) +/** + * @return True on success + */ +bool cmQtAutoGenerators::ParseHeaderFile(std::string const& absFilename, + const SourceJob& job) { - for (KeyRegExp& filter : this->MocDependFilters) { - // Run a simple find string operation before the expensive - // regular expression check - if (contentText.find(filter.Key) != std::string::npos) { - // Run regular expression check loop - const std::string sourcePath = SubDirPrefix(absFilename); - const char* contentChars = contentText.c_str(); - while (filter.RegExp.find(contentChars)) { - // Evaluate match - const std::string match = filter.RegExp.match(1); - if (!match.empty()) { - // Find the dependency file - std::string incFile; - if (this->MocFindIncludedFile(incFile, sourcePath, match)) { - mocDepends[absFilename].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)); - } - } - contentChars += filter.RegExp.end(); + 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; } /** - * @brief Tests if the file should be ignored for moc scanning - * @return True if the file should be ignored + * @return True on success */ -bool cmQtAutoGenerators::MocSkip(const std::string& absFilename) const +bool cmQtAutoGenerators::ParsePostprocess() { - if (this->MocEnabled()) { - // Test if the file name is on the skip list - if (!ListContains(this->MocSkipList, absFilename)) { - return false; + bool success = true; + // Read missin dependecies + 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 true; + return success; } /** - * @brief Tests if the file name is in the skip list + * @brief Tests if the file should be ignored for moc scanning + * @return True if the file should be ignored */ -bool cmQtAutoGenerators::UicSkip(const std::string& absFilename) const +bool cmQtAutoGenerators::MocSkip(std::string const& absFilename) const { - if (this->UicEnabled()) { + if (this->MocEnabled()) { // Test if the file name is on the skip list - if (!ListContains(this->UicSkipList, absFilename)) { + if (!ListContains(this->MocSkipList, absFilename)) { return false; } } @@ -806,54 +856,29 @@ bool cmQtAutoGenerators::UicSkip(const std::string& absFilename) const } /** - * @return True on success + * @brief Tests if the C++ content requires moc processing + * @return True if moc is required */ -bool cmQtAutoGenerators::ParseSourceFile( - const std::string& absFilename, - std::map<std::string, std::string>& mocsIncluded, - std::map<std::string, std::set<std::string>>& mocDepends, - std::map<std::string, std::vector<std::string>>& uisIncluded, bool relaxed) +bool cmQtAutoGenerators::MocRequired(std::string const& contentText, + std::string* macroName) { - std::string contentText; - bool success = ReadAll(contentText, absFilename); - if (success) { - if (!contentText.empty()) { - // Parse source contents for MOC - if (success && !this->MocSkip(absFilename)) { - success = this->MocParseSourceContent( - absFilename, contentText, mocsIncluded, mocDepends, relaxed); - } - // Parse source contents for UIC - if (success && !this->UicSkip(absFilename)) { - this->UicParseContent(absFilename, contentText, uisIncluded); + 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; } - } else { - this->LogFileWarning(cmQtAutoGen::GEN, absFilename, "The file is empty"); - } - } else { - this->LogFileError(cmQtAutoGen::GEN, absFilename, "Could not read file"); - } - return success; -} - -void cmQtAutoGenerators::UicParseContent( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::vector<std::string>>& uisIncluded) -{ - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::UIC, "Checking " + absFilename); - } - - const char* contentChars = contentText.c_str(); - if (strstr(contentChars, "ui_") != nullptr) { - while (this->UicRegExpInclude.find(contentChars)) { - uisIncluded[absFilename].push_back(this->UicRegExpInclude.match(1)); - contentChars += this->UicRegExpInclude.end(); } } + return false; } -std::string cmQtAutoGenerators::MocMacroNamesString() const +std::string cmQtAutoGenerators::MocStringMacros() const { std::string res; const auto itB = this->MocMacroFilters.cbegin(); @@ -875,339 +900,479 @@ std::string cmQtAutoGenerators::MocMacroNamesString() const return res; } -std::string cmQtAutoGenerators::MocHeaderSuffixesString() const +std::string cmQtAutoGenerators::MocStringHeaders( + std::string const& fileBase) const { - std::string res = ".{"; + 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<std::string>& depends) +{ + if (this->MocDependFilters.empty() && contentText.empty()) { + return; + } + + std::vector<std::string> 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( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::string>& mocsIncluded, - std::map<std::string, std::set<std::string>>& mocDepends, bool relaxed) +bool cmQtAutoGenerators::MocParseSourceContent(std::string const& absFilename, + std::string const& contentText) { if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, "Checking " + absFilename); - } + 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<MocJobIncluded>(); + 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)); + }; - const std::string scanFileDir = SubDirPrefix(absFilename); - const std::string scanFileBase = - cmSystemTools::GetFilenameWithoutLastExtension(absFilename); + 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<MocInc> mocIncsUsc; + std::vector<MocInc> 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_<BASE>.cxx + // Remove the moc_ part from the base name + mocIncsUsc.push_back(MocInc{ std::move(incString), std::move(incDir), + incBase.substr(4) }); + } else { + // <BASE>.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 ownMocUnderscoreInclude; - std::string ownMocUnderscoreHeader; - - // first a simple string check for "moc" is *much* faster than the regexp, - // and if the string search already fails, we don't have to try the - // expensive regexp - const char* contentChars = contentText.c_str(); - if (strstr(contentChars, "moc") != nullptr) { - // Iterate over all included moc files - while (this->MocRegExpInclude.find(contentChars)) { - const std::string incString = this->MocRegExpInclude.match(1); - // Basename of the moc include - const std::string incDir(SubDirPrefix(incString)); - const std::string incBase = - cmSystemTools::GetFilenameWithoutLastExtension(incString); - - // If the moc include is of the moc_foo.cpp style we expect - // the Q_OBJECT class declaration in a header file. - // If the moc include is of the foo.moc style we need to look for - // a Q_OBJECT macro in the current source file, if it contains the - // macro we generate the moc file from the source file. - if (cmHasLiteralPrefix(incBase, "moc_")) { - // Include: moc_FOO.cxx - // Remove the moc_ part - const std::string incRealBase = incBase.substr(4); - const std::string headerToMoc = - this->MocFindHeader(scanFileDir, incDir + incRealBase); - if (!headerToMoc.empty()) { - if (!this->MocSkip(headerToMoc)) { - // Register moc job - mocsIncluded[headerToMoc] = incString; - this->MocFindDepends(headerToMoc, contentText, mocDepends); - // Store meta information for relaxed mode - if (relaxed && (incRealBase == scanFileBase)) { - ownMocUnderscoreInclude = incString; - ownMocUnderscoreHeader = headerToMoc; - } - } - } else { - std::ostringstream ost; - ost << "The file includes the moc file " - << cmQtAutoGen::Quoted(incString) - << ", but could not find header "; - ost << cmQtAutoGen::Quoted(incRealBase + - this->MocHeaderSuffixesString()); - this->LogFileError(cmQtAutoGen::MOC, absFilename, ost.str()); - return false; - } + std::string ownMocUscInclude; + std::string ownMocUscHeader; + + // Process moc_<BASE>.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 <BASE>.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 { - // Include: FOO.moc - bool ownDotMoc = (incBase == scanFileBase); - std::string fileToMoc; - if (relaxed) { - // Mode: Relaxed - if (selfRequiresMoc && ownDotMoc) { - // Include self - fileToMoc = absFilename; - ownDotMocIncluded = true; - } else { - // In relaxed mode try to find a header instead but issue a warning - const std::string headerToMoc = - this->MocFindHeader(scanFileDir, incDir + incBase); - if (!headerToMoc.empty()) { - if (!this->MocSkip(headerToMoc)) { - // This is for KDE4 compatibility: - fileToMoc = headerToMoc; - - auto quoted_inc = cmQtAutoGen::Quoted(incString); - auto quoted_header = cmQtAutoGen::Quoted(headerToMoc); - auto quoted_base = - cmQtAutoGen::Quoted("moc_" + incBase + ".cpp"); - if (!selfRequiresMoc) { - if (ownDotMoc) { - std::ostringstream ost; - ost << "The file includes the moc file " << quoted_inc - << ", but does not contain a " - << this->MocMacroNamesString() << " macro.\n" - << "Running moc on\n" - << " " << quoted_header << "!\n" - << "Better include " << quoted_base - << " for a compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, - ost.str()); - } else if (!ownDotMoc) { - std::ostringstream ost; - ost << "The file includes the moc file " << quoted_inc - << " instead of " << quoted_base << ".\n"; - ost << "Running moc on\n" - << " " << quoted_header << "!\n" - << "Better include " << quoted_base - << " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)\n"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, - ost.str()); - } - } else { - if (!ownDotMoc) { - // Handled further down - } - } - } + // 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::ostringstream ost; - ost << "The file includes the moc file " - << cmQtAutoGen::Quoted(incString) - << ", 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, ost.str()); - return false; + 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 { - // Mode: Strict - if (ownDotMoc) { - // Include self - fileToMoc = absFilename; - ownDotMocIncluded = true; - // Accept but issue a warning if moc isn't required - if (!selfRequiresMoc) { - std::ostringstream ost; - ost << "The file includes the moc file " - << cmQtAutoGen::Quoted(incString) - << ", but does not contain a " << this->MocMacroNamesString() - << " macro."; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, ost.str()); - } - } else { - // Don't allow FOO.moc include other than self in strict mode - std::ostringstream ost; - ost << "The file includes the moc file " - << cmQtAutoGen::Quoted(incString) - << ", which seems to be the moc file from a different " - "source file. This is not supported. Include " - << cmQtAutoGen::Quoted(scanFileBase + ".moc") - << " to run moc on this source file."; - this->LogFileError(cmQtAutoGen::MOC, absFilename, ost.str()); - return false; - } + 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; } - if (!fileToMoc.empty()) { - mocsIncluded[fileToMoc] = incString; - this->MocFindDepends(fileToMoc, contentText, mocDepends); + } + } 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 <BASE>.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; } - // Forward content pointer - contentChars += this->MocRegExpInclude.end(); } } if (selfRequiresMoc && !ownDotMocIncluded) { - // In this case, check whether the scanned file itself contains a - // Q_OBJECT. + // 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 (relaxed && !ownMocUnderscoreInclude.empty()) { + if (this->MocRelaxedMode && !ownMocUscInclude.empty()) { // This is for KDE4 compatibility: - std::ostringstream ost; - ost << "The file contains a " << selfMacroName - << " macro, but does not include " - << cmQtAutoGen::Quoted(scanFileBase + ".moc") - << ". Instead it includes " - << cmQtAutoGen::Quoted(ownMocUnderscoreInclude) << ".\n" - << "Running moc on\n" - << " " << cmQtAutoGen::Quoted(absFilename) << "!\n" - << "Better include " << cmQtAutoGen::Quoted(scanFileBase + ".moc") - << " for compatibility with strict mode.\n" - "(CMAKE_AUTOMOC_RELAXED_MODE warning)"; - this->LogFileWarning(cmQtAutoGen::MOC, absFilename, ost.str()); - - // Use scanned source file instead of scanned header file as moc source - mocsIncluded[absFilename] = ownMocUnderscoreInclude; - this->MocFindDepends(absFilename, contentText, mocDepends); - // Remove - mocsIncluded.erase(ownMocUnderscoreHeader); + 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::ostringstream ost; - ost << "The file contains a " << selfMacroName - << " macro, but does not include " - << cmQtAutoGen::Quoted(scanFileBase + ".moc") << "!\n" - << "Consider adding the include or enabling SKIP_AUTOMOC for this " - "file."; - this->LogFileError(cmQtAutoGen::MOC, absFilename, ost.str()); + 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( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::string>& mocsNotIncluded, - std::map<std::string, std::set<std::string>>& mocDepends) +void cmQtAutoGenerators::MocParseHeaderContent(std::string const& absFilename, + std::string const& contentText) { - // Log if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, "Checking " + absFilename); - } - if (this->MocRequired(contentText)) { - // Register moc job - mocsNotIncluded[absFilename] = - this->ChecksumedPath(absFilename, "moc_", this->ConfigSuffix + ".cpp"); - this->MocFindDepends(absFilename, contentText, mocDepends); - } -} - -void cmQtAutoGenerators::SearchHeadersForSourceFile( - const std::string& absFilename, std::set<std::string>& mocHeaderFiles, - std::set<std::string>& uicHeaderFiles) const -{ - std::array<std::string, 2> basepaths; - { - std::string bpath = SubDirPrefix(absFilename); - bpath += cmSystemTools::GetFilenameWithoutLastExtension(absFilename); - // search for default header files and private header files - basepaths[0] = bpath; - basepaths[1] = bpath; - basepaths[1] += "_p"; + this->LogInfo(cmQtAutoGen::MOC, "Checking: " + absFilename); } - for (const std::string& bPath : basepaths) { - std::string headerName; - if (this->FindHeader(headerName, bPath)) { - // Moc headers - if (!this->MocSkip(absFilename) && !this->MocSkip(headerName)) { - mocHeaderFiles.insert(headerName); - } - // Uic headers - if (!this->UicSkip(absFilename) && !this->UicSkip(headerName)) { - uicHeaderFiles.insert(headerName); - } - } - } -} + auto const fit = + std::find_if(this->MocJobsIncluded.cbegin(), this->MocJobsIncluded.cend(), + [&absFilename](std::unique_ptr<MocJobIncluded> const& job) { + return job->SourceFile == absFilename; + }); + if (fit == this->MocJobsIncluded.cend()) { + if (this->MocRequired(contentText)) { + static std::string const prefix = "moc_"; + static std::string const suffix = this->ConfigSuffix + ".cpp"; -bool cmQtAutoGenerators::ParseHeaders( - const std::set<std::string>& mocHeaderFiles, - const std::set<std::string>& uicHeaderFiles, - const std::map<std::string, std::string>& mocsIncluded, - std::map<std::string, std::string>& mocsNotIncluded, - std::map<std::string, std::set<std::string>>& mocDepends, - std::map<std::string, std::vector<std::string>>& uisIncluded) -{ - bool success = true; - // Merged header files list to read files only once - std::set<std::string> headerFiles; - headerFiles.insert(mocHeaderFiles.begin(), mocHeaderFiles.end()); - headerFiles.insert(uicHeaderFiles.begin(), uicHeaderFiles.end()); - - for (std::string const& headerName : headerFiles) { - std::string contentText; - if (ReadAll(contentText, headerName)) { - // Parse header content for MOC - if ((mocHeaderFiles.find(headerName) != mocHeaderFiles.end()) && - (mocsIncluded.find(headerName) == mocsIncluded.end())) { - this->MocParseHeaderContent(headerName, contentText, mocsNotIncluded, - mocDepends); - } - // Parse header content for UIC - if (uicHeaderFiles.find(headerName) != uicHeaderFiles.end()) { - this->UicParseContent(headerName, contentText, uisIncluded); + auto job = cm::make_unique<MocJobAuto>(); + job->SourceFile = absFilename; + { + std::string& bld = job->BuildFileRel; + bld = this->FilePathChecksum.getPart(absFilename); + bld += "/"; + bld += prefix; + bld += cmSystemTools::GetFilenameWithoutLastExtension(absFilename); + bld += suffix; } - } else { - this->LogFileError(cmQtAutoGen::GEN, headerName, - "Could not read header file"); - success = false; - break; + this->MocFindDepends(absFilename, contentText, job->Depends); + this->MocJobsAuto.push_back(std::move(job)); } } - return success; } -bool cmQtAutoGenerators::MocGenerateAll( - const std::map<std::string, std::string>& mocsIncluded, - const std::map<std::string, std::string>& mocsNotIncluded, - const std::map<std::string, std::set<std::string>>& mocDepends) +bool cmQtAutoGenerators::MocGenerateAll() { if (!this->MocEnabled()) { return true; } - // Look for name collisions + // Look for name collisions in included moc files { - std::multimap<std::string, std::string> collisions; - // Test merged map of included and notIncluded - std::map<std::string, std::string> mergedMocs(mocsIncluded); - mergedMocs.insert(mocsNotIncluded.begin(), mocsNotIncluded.end()); - if (this->NameCollisionTest(mergedMocs, collisions)) { - std::string msg = "The same moc file will be generated " - "from different sources.\n" - "To avoid this error either\n" - " - rename the source files or\n" - " - do not include the (moc_NAME.cpp|NAME.moc) file"; - this->LogNameCollisionError(cmQtAutoGen::MOC, msg, collisions); + bool collision = false; + std::map<std::string, std::vector<MocJobIncluded const*>> 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_<NAME>.cpp\" file\n" + " - add a directory prefix to a \"<NAME>.moc\" include " + "(e.g \"sub/<NAME>.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; } } - // Generate moc_predefs + // (Re)generate moc_predefs.h on demand if (!this->MocPredefsCmd.empty()) { if (this->MocSettingsChanged || - FileAbsentOrOlder(this->MocPredefsFileAbs, this->SettingsFile)) { + !cmSystemTools::FileExists(this->MocPredefsFileAbs)) { if (this->Verbose) { this->LogBold("Generating MOC predefs " + this->MocPredefsFileRel); } @@ -1216,16 +1381,9 @@ bool cmQtAutoGenerators::MocGenerateAll( { // Compose command std::vector<std::string> 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); - } // Add options - cmd.insert(cmd.end(), this->MocOptions.begin(), - this->MocOptions.end()); + cmd.insert(cmd.end(), this->MocAllOptions.begin(), + this->MocAllOptions.end()); // Execute command if (!this->RunCommand(cmd, output)) { this->LogCommandError(cmQtAutoGen::MOC, @@ -1240,6 +1398,8 @@ bool cmQtAutoGenerators::MocGenerateAll( output)) { this->MocPredefsChanged = true; } else { + this->LogFileError(cmQtAutoGen::MOC, this->MocPredefsFileAbs, + "moc_predefs file writing failed"); return false; } } else { @@ -1251,104 +1411,157 @@ bool cmQtAutoGenerators::MocGenerateAll( cmSystemTools::Touch(this->MocPredefsFileAbs, false); } } + + // Add moc_predefs.h to moc file dependecies + 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& it : mocsIncluded) { - if (!this->MocGenerateFile(it.first, it.second, mocDepends, true)) { - if (this->MocRunFailed) { - return false; - } + for (auto const& item : this->MocJobsIncluded) { + if (!this->MocGenerateFile(*item)) { + return false; } } - // Generate moc files that are _not_ included by source files. - bool mocCompFileGenerated = false; - for (auto const& it : mocsNotIncluded) { - if (this->MocGenerateFile(it.first, it.second, mocDepends, false)) { - mocCompFileGenerated = true; - } else { - if (this->MocRunFailed) { - return false; - } + bool autoNameGenerated = false; + for (auto const& item : this->MocJobsAuto) { + if (!this->MocGenerateFile(*item, &autoNameGenerated)) { + return false; } } // Compose mocs compilation file content - std::string automocSource; { - std::ostringstream ost; - ost << "/* This file is autogenerated, do not edit*/\n"; - if (mocsNotIncluded.empty()) { + std::string mocs = "/* This file is autogenerated, do not edit*/\n"; + if (this->MocJobsAuto.empty()) { // Dummy content - ost << "enum some_compilers { need_more_than_nothing };\n"; + mocs += "enum some_compilers { need_more_than_nothing };\n"; } else { // Valid content - for (auto const& it : mocsNotIncluded) { - ost << "#include \"" << it.second << "\"\n"; + for (const auto& item : this->MocJobsAuto) { + mocs += "#include \""; + mocs += item->BuildFileRel; + mocs += "\"\n"; } } - automocSource = ost.str(); - } - if (this->FileDiffers(this->MocCompFileAbs, automocSource)) { - // Actually write mocs compilation file - if (this->Verbose) { - this->LogBold("Generating MOC compilation " + this->MocCompFileRel); - } - if (!this->FileWrite(cmQtAutoGen::MOC, this->MocCompFileAbs, - automocSource)) { - return false; - } - } else if (mocCompFileGenerated) { - // Only touch mocs compilation file - if (this->Verbose) { - this->LogInfo(cmQtAutoGen::MOC, - "Touching mocs compilation " + this->MocCompFileRel); + 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); } - cmSystemTools::Touch(this->MocCompFileAbs, false); } return true; } /** - * @return True if a moc file was created. False may indicate an error. + * @return True on success */ -bool cmQtAutoGenerators::MocGenerateFile( - const std::string& sourceFile, const std::string& mocFileName, - const std::map<std::string, std::set<std::string>>& mocDepends, - bool included) +bool cmQtAutoGenerators::MocGenerateFile(const MocJobAuto& mocJob, + bool* generated) { - bool mocGenerated = false; - bool generateMoc = this->MocSettingsChanged || this->MocPredefsChanged; - - const std::string mocFileRel = - included ? (this->AutogenIncludeDir + mocFileName) : mocFileName; - const std::string mocFileAbs = - cmSystemTools::CollapseCombinedPath(this->AutogenBuildDir, mocFileRel); - - if (!generateMoc) { - // Test if the source file is newer that the build file - generateMoc = FileAbsentOrOlder(mocFileAbs, sourceFile); - if (!generateMoc) { - // Test if a dependency file changed - std::map<std::string, std::set<std::string>>::const_iterator dit = - mocDepends.find(sourceFile); - if (dit != mocDepends.end()) { - for (std::string const& fit : dit->second) { - if (FileAbsentOrOlder(mocFileAbs, fit)) { - generateMoc = true; - break; - } + 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 (generateMoc) { + + if (generate) { // Log if (this->Verbose) { - this->LogBold("Generating MOC source " + mocFileRel); + this->LogBold("Generating MOC source " + mocJob.BuildFileRel); + this->LogInfo(cmQtAutoGen::MOC, generateReason); } // Make sure the parent directory exists @@ -1356,15 +1569,9 @@ bool cmQtAutoGenerators::MocGenerateFile( // Compose moc command std::vector<std::string> cmd; cmd.push_back(this->MocExecutable); - // 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); - } // Add options - cmd.insert(cmd.end(), this->MocOptions.begin(), this->MocOptions.end()); + cmd.insert(cmd.end(), this->MocAllOptions.begin(), + this->MocAllOptions.end()); // Add predefs include if (!this->MocPredefsFileAbs.empty()) { cmd.push_back("--include"); @@ -1372,37 +1579,111 @@ bool cmQtAutoGenerators::MocGenerateFile( } cmd.push_back("-o"); cmd.push_back(mocFileAbs); - cmd.push_back(sourceFile); + cmd.push_back(mocJob.SourceFile); // Execute moc command std::string output; if (this->RunCommand(cmd, output)) { // Success - mocGenerated = true; + if (generated != nullptr) { + *generated = true; + } } else { - this->LogCommandError(cmQtAutoGen::MOC, "moc failed for\n " + - cmQtAutoGen::Quoted(sourceFile), - cmd, output); + // 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); - this->MocRunFailed = true; + success = false; } } else { // Parent directory creation failed - this->MocRunFailed = true; + 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 mocGenerated; + return true; +} + +bool cmQtAutoGenerators::UicParseContent(std::string const& absFilename, + std::string const& contentText) +{ + if (this->Verbose) { + this->LogInfo(cmQtAutoGen::UIC, "Checking: " + absFilename); + } + + std::vector<std::string> 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<UicJob>(); + 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, - const std::string& sourceFile, - const std::string& searchPath, - const std::string& searchFile) + std::string const& sourceFile, + std::string const& includeString) { bool success = false; - std::vector<std::string> testFiles; + std::string searchFile = + cmSystemTools::GetFilenameWithoutLastExtension(includeString).substr(3); + searchFile += ".ui"; // Collect search paths list + std::vector<std::string> testFiles; { + std::string const searchPath = SubDirPrefix(includeString); + std::string searchFileFull; if (!searchPath.empty()) { searchFileFull = searchPath; @@ -1410,7 +1691,7 @@ bool cmQtAutoGenerators::UicFindIncludedFile(std::string& absFile, } // Vicinity of the source { - const std::string sourcePath = SubDirPrefix(sourceFile); + std::string const sourcePath = SubDirPrefix(sourceFile); testFiles.push_back(sourcePath + searchFile); if (!searchPath.empty()) { testFiles.push_back(sourcePath + searchFileFull); @@ -1440,76 +1721,75 @@ bool cmQtAutoGenerators::UicFindIncludedFile(std::string& absFile, // Log error if (!success) { - std::ostringstream ost; - ost << "Could not find " << cmQtAutoGen::Quoted(searchFile) << " in\n"; + std::string emsg = "Could not find "; + emsg += cmQtAutoGen::Quoted(searchFile); + emsg += " in\n"; for (std::string const& testFile : testFiles) { - ost << " " << cmQtAutoGen::Quoted(testFile) << "\n"; + emsg += " "; + emsg += cmQtAutoGen::Quoted(testFile); + emsg += "\n"; } - this->LogFileError(cmQtAutoGen::UIC, sourceFile, ost.str()); + this->LogFileError(cmQtAutoGen::UIC, sourceFile, emsg); } return success; } -bool cmQtAutoGenerators::UicGenerateAll( - const std::map<std::string, std::vector<std::string>>& uisIncluded) +bool cmQtAutoGenerators::UicGenerateAll() { if (!this->UicEnabled()) { return true; } - // single map with input / output names - std::map<std::string, std::map<std::string, std::string>> sourceGenMap; + // Look for name collisions in included uic files { - // Collision lookup map - std::map<std::string, std::string> testMap; - // Compile maps - for (auto const& sit : uisIncluded) { - const std::string& source(sit.first); - const std::vector<std::string>& sourceIncs(sit.second); - // insert new source/destination map - std::map<std::string, std::string>& uiGenMap = sourceGenMap[source]; - for (std::string const& inc : sourceIncs) { - // Remove ui_ from the begin filename by substr() - const std::string uiBasePath = SubDirPrefix(inc); - const std::string uiBaseName = - cmSystemTools::GetFilenameWithoutLastExtension(inc).substr(3); - const std::string uiFileName = uiBaseName + ".ui"; - std::string uiInputFile; - if (UicFindIncludedFile(uiInputFile, source, uiBasePath, uiFileName)) { - std::string uiOutputFile = uiBasePath; - uiOutputFile += "ui_"; - uiOutputFile += uiBaseName; - uiOutputFile += ".h"; - cmSystemTools::ReplaceString(uiOutputFile, "..", "__"); - uiGenMap[uiInputFile] = uiOutputFile; - testMap[uiInputFile] = uiOutputFile; - } else { - return false; - } + bool collision = false; + std::map<std::string, std::vector<UicJob const*>> collisions; + for (auto const& job : this->UicJobs) { + auto& list = collisions[job->IncludeString]; + if (!list.empty()) { + collision = true; } - } - // look for name collisions - { - std::multimap<std::string, std::string> collisions; - if (this->NameCollisionTest(testMap, collisions)) { - std::string msg = "The same ui_NAME.h file will be generated " - "from different sources.\n" - "To avoid this error rename the source files.\n"; - this->LogNameCollisionError(cmQtAutoGen::UIC, msg, collisions); - return false; + 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_<NAME>.h\" include " + "(e.g \"sub/ui_<NAME>.h\")\n" + " - rename the <NAME>.ui file(s) and adjust the \"ui_<NAME>.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 files - for (auto const& srcItem : sourceGenMap) { - for (auto const& item : srcItem.second) { - if (!this->UicGenerateFile(srcItem.first, item.first, item.second)) { - if (this->UicRunFailed) { - return false; - } - } + // Generate ui header files + for (const auto& item : this->UicJobs) { + if (!this->UicGenerateFile(*item)) { + return false; } } @@ -1517,27 +1797,59 @@ bool cmQtAutoGenerators::UicGenerateAll( } /** - * @return True if a uic file was created. False may indicate an error. + * @return True on success */ -bool cmQtAutoGenerators::UicGenerateFile(const std::string& realName, - const std::string& uiInputFile, - const std::string& uiOutputFile) +bool cmQtAutoGenerators::UicGenerateFile(const UicJob& uicJob) { - bool uicGenerated = false; - bool generateUic = this->UicSettingsChanged; + bool success = true; - const std::string uicFileRel = this->AutogenIncludeDir + uiOutputFile; - const std::string uicFileAbs = - cmSystemTools::CollapseCombinedPath(this->AutogenBuildDir, uicFileRel); + std::string const uicFileAbs = cmSystemTools::CollapseCombinedPath( + this->AutogenBuildDir, uicJob.BuildFileRel); - if (!generateUic) { - // Test if the source file is newer that the build file - generateUic = FileAbsentOrOlder(uicFileAbs, uiInputFile); + 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 (generateUic) { + 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 " + uicFileRel); + this->LogBold("Generating UIC header " + uicJob.BuildFileRel); + this->LogInfo(cmQtAutoGen::UIC, generateReason); } // Make sure the parent directory exists @@ -1547,7 +1859,7 @@ bool cmQtAutoGenerators::UicGenerateFile(const std::string& realName, cmd.push_back(this->UicExecutable); { std::vector<std::string> allOpts = this->UicTargetOptions; - auto optionIt = this->UicOptions.find(uiInputFile); + auto optionIt = this->UicOptions.find(uicJob.SourceFile); if (optionIt != this->UicOptions.end()) { cmQtAutoGen::UicMergeOptions(allOpts, optionIt->second, (this->QtMajorVersion == "5")); @@ -1556,31 +1868,29 @@ bool cmQtAutoGenerators::UicGenerateFile(const std::string& realName, } cmd.push_back("-o"); cmd.push_back(uicFileAbs); - cmd.push_back(uiInputFile); + cmd.push_back(uicJob.SourceFile); std::string output; if (this->RunCommand(cmd, output)) { // Success - uicGenerated = true; } else { // Command failed { - std::ostringstream ost; - ost << "uic failed for\n" - << " " << cmQtAutoGen::Quoted(uiInputFile) << "\n" - << "needed by\n" - << " " << cmQtAutoGen::Quoted(realName); - this->LogCommandError(cmQtAutoGen::UIC, ost.str(), cmd, output); + 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); - this->UicRunFailed = true; + success = false; } } else { // Parent directory creation failed - this->UicRunFailed = true; + success = false; } } - return uicGenerated; + return success; } bool cmQtAutoGenerators::RccGenerateAll() @@ -1589,24 +1899,22 @@ bool cmQtAutoGenerators::RccGenerateAll() return true; } - // Generate qrc files - for (RccJob const& rccJob : this->RccJobs) { + // Generate rcc files + for (const RccJob& rccJob : this->RccJobs) { if (!this->RccGenerateFile(rccJob)) { - if (this->RccRunFailed) { - return false; - } + return false; } } return true; } /** - * @return True if a rcc file was created. False may indicate an error. + * @return True on success */ bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob) { + bool success = true; bool rccGenerated = false; - bool generateRcc = this->RccSettingsChanged; std::string rccFileAbs; if (this->ConfigSuffix.empty()) { @@ -1618,46 +1926,114 @@ bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob) rccFileAbs += this->ConfigSuffix; rccFileAbs += cmSystemTools::GetFilenameLastExtension(rccJob.RccFile); } - const std::string rccFileRel = cmSystemTools::RelativePath( + std::string const rccFileRel = cmSystemTools::RelativePath( this->AutogenBuildDir.c_str(), rccFileAbs.c_str()); // Check if regeneration is required - if (!generateRcc) { - // Test if the resources list file is newer than build file - generateRcc = FileAbsentOrOlder(rccFileAbs, rccJob.QrcFile); - if (!generateRcc) { - // Acquire input file list - std::vector<std::string> readFiles; - const std::vector<std::string>* files = &rccJob.Inputs; - if (files->empty()) { - // Read input file list from qrc file - std::string error; - if (cmQtAutoGen::RccListInputs(this->QtMajorVersion, - this->RccExecutable, rccJob.QrcFile, - readFiles, &error)) { - files = &readFiles; - } else { - files = nullptr; - this->LogFileError(cmQtAutoGen::RCC, rccJob.QrcFile, error); - this->RccRunFailed = true; - } + 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; } - // Test if any input file is newer than the build file - if (files != nullptr) { - for (std::string const& file : *files) { - if (FileAbsentOrOlder(rccFileAbs, file)) { - generateRcc = true; - break; + } + } + if (success && !generate) { + // Acquire input file list + std::vector<std::string> readFiles; + std::vector<std::string> 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 (generateRcc) { + if (generate) { // Log if (this->Verbose) { this->LogBold("Generating RCC source " + rccFileRel); + this->LogInfo(cmQtAutoGen::RCC, generateReason); } // Make sure the parent directory exists @@ -1675,22 +2051,24 @@ bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob) // Success rccGenerated = true; } else { - this->LogCommandError(cmQtAutoGen::RCC, "rcc failed for\n " + - cmQtAutoGen::Quoted(rccJob.QrcFile), - cmd, output); + { + std::string emsg = "rcc failed for\n "; + emsg += cmQtAutoGen::Quoted(rccJob.QrcFile); + this->LogCommandError(cmQtAutoGen::RCC, emsg, cmd, output); + } cmSystemTools::RemoveFile(rccFileAbs); - this->RccRunFailed = true; + success = false; } } else { // Parent directory creation failed - this->RccRunFailed = true; + success = false; } } // For a multi configuration generator generate a wrapper file - if (!this->ConfigSuffix.empty() && !this->RccRunFailed) { + if (success && !this->ConfigSuffix.empty()) { // Wrapper file name - const std::string& wrapperFileAbs = rccJob.RccFile; - const std::string wrapperFileRel = cmSystemTools::RelativePath( + 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 " @@ -1700,17 +2078,17 @@ bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob) content += "\"\n"; // Write content to file if (this->FileDiffers(wrapperFileAbs, content)) { - // Write new wrapper file if the content differs + // Write new wrapper file if (this->Verbose) { this->LogBold("Generating RCC wrapper " + wrapperFileRel); } if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) { - // Error - rccGenerated = false; - this->RccRunFailed = true; + this->LogFileError(cmQtAutoGen::RCC, wrapperFileAbs, + "rcc wrapper file writing failed"); + success = false; } } else if (rccGenerated) { - // Only touch wrapper file if the content matches + // Just touch the wrapper file if (this->Verbose) { this->LogInfo(cmQtAutoGen::RCC, "Touching RCC wrapper " + wrapperFileRel); @@ -1719,10 +2097,10 @@ bool cmQtAutoGenerators::RccGenerateFile(const RccJob& rccJob) } } - return rccGenerated; + return success; } -void cmQtAutoGenerators::LogBold(const std::string& message) const +void cmQtAutoGenerators::LogBold(std::string const& message) const { cmSystemTools::MakefileColorEcho(cmsysTerminal_Color_ForegroundBlue | cmsysTerminal_Color_ForegroundBold, @@ -1730,7 +2108,7 @@ void cmQtAutoGenerators::LogBold(const std::string& message) const } void cmQtAutoGenerators::LogInfo(cmQtAutoGen::GeneratorType genType, - const std::string& message) const + std::string const& message) const { std::string msg = cmQtAutoGen::GeneratorName(genType); msg += ": "; @@ -1742,7 +2120,7 @@ void cmQtAutoGenerators::LogInfo(cmQtAutoGen::GeneratorType genType, } void cmQtAutoGenerators::LogWarning(cmQtAutoGen::GeneratorType genType, - const std::string& message) const + std::string const& message) const { std::string msg = cmQtAutoGen::GeneratorName(genType); msg += " warning:"; @@ -1758,182 +2136,102 @@ void cmQtAutoGenerators::LogWarning(cmQtAutoGen::GeneratorType genType, if (msg.back() != '\n') { msg.push_back('\n'); } - msg += "\n"; + msg.push_back('\n'); cmSystemTools::Stdout(msg.c_str(), msg.size()); } void cmQtAutoGenerators::LogFileWarning(cmQtAutoGen::GeneratorType genType, - const std::string& filename, - const std::string& message) const + std::string const& filename, + std::string const& message) const { - std::string emsg = " "; - emsg += cmQtAutoGen::Quoted(filename); - emsg += "\n"; + std::string msg = " "; + msg += cmQtAutoGen::Quoted(filename); + msg.push_back('\n'); // Message - emsg += message; - this->LogWarning(genType, emsg); + msg += message; + this->LogWarning(genType, msg); } void cmQtAutoGenerators::LogError(cmQtAutoGen::GeneratorType genType, - const std::string& message) const + std::string const& message) const { std::string msg; msg.push_back('\n'); - msg = cmQtAutoGen::GeneratorName(genType); - msg += " error:"; - if (message.find('\n') == std::string::npos) { - // Single line message - msg.push_back(' '); - } else { - // Multi line message - msg.push_back('\n'); - } + msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " error"); // Message msg += message; if (msg.back() != '\n') { msg.push_back('\n'); } - msg += "\n"; + msg.push_back('\n'); cmSystemTools::Stderr(msg.c_str(), msg.size()); } void cmQtAutoGenerators::LogFileError(cmQtAutoGen::GeneratorType genType, - const std::string& filename, - const std::string& message) const + std::string const& filename, + std::string const& message) const { std::string emsg = " "; emsg += cmQtAutoGen::Quoted(filename); - emsg += "\n"; + emsg += '\n'; // Message emsg += message; this->LogError(genType, emsg); } -void cmQtAutoGenerators::LogNameCollisionError( - cmQtAutoGen::GeneratorType genType, const std::string& message, - const std::multimap<std::string, std::string>& collisions) const -{ - std::string emsg; - // Add message - if (!message.empty()) { - emsg += message; - if (emsg.back() != '\n') { - emsg.push_back('\n'); - } - } - // Append collision list - for (auto const& item : collisions) { - emsg += " "; - emsg += item.first; - emsg += " -> "; - emsg += item.second; - emsg += "\n"; - } - this->LogError(genType, emsg); -} - void cmQtAutoGenerators::LogCommandError( - cmQtAutoGen::GeneratorType genType, const std::string& message, - const std::vector<std::string>& command, const std::string& output) const + cmQtAutoGen::GeneratorType genType, std::string const& message, + std::vector<std::string> const& command, std::string const& output) const { std::string msg; msg.push_back('\n'); - msg += cmQtAutoGen::GeneratorName(genType); - msg += " subprocess error: "; + msg += HeadLine(cmQtAutoGen::GeneratorName(genType) + " subprocess error"); msg += message; if (msg.back() != '\n') { msg.push_back('\n'); } msg.push_back('\n'); - msg += "Command\n"; - msg += "-------\n"; + msg += HeadLine("Command"); msg += QuotedCommand(command); if (msg.back() != '\n') { msg.push_back('\n'); } msg.push_back('\n'); - msg += "Output\n"; - msg += "------\n"; + msg += HeadLine("Output"); msg += output; if (msg.back() != '\n') { msg.push_back('\n'); } - msg += "\n"; + msg.push_back('\n'); cmSystemTools::Stderr(msg.c_str(), msg.size()); } /** - * @brief Collects name collisions as output/input pairs - * @return True if there were collisions - */ -bool cmQtAutoGenerators::NameCollisionTest( - const std::map<std::string, std::string>& genFiles, - std::multimap<std::string, std::string>& collisions) const -{ - typedef std::map<std::string, std::string>::const_iterator Iter; - typedef std::map<std::string, std::string>::value_type VType; - for (Iter ait = genFiles.begin(); ait != genFiles.end(); ++ait) { - bool first_match(true); - for (Iter bit = (++Iter(ait)); bit != genFiles.end(); ++bit) { - if (ait->second == bit->second) { - if (first_match) { - if (collisions.find(ait->second) != collisions.end()) { - // We already know of this collision from before - break; - } - collisions.insert(VType(ait->second, ait->first)); - first_match = false; - } - collisions.insert(VType(bit->second, bit->first)); - } - } - } - - return !collisions.empty(); -} - -/** - * @brief Generates a file path based on the checksum of the source file path - * @return The path - */ -std::string cmQtAutoGenerators::ChecksumedPath( - const std::string& sourceFile, const std::string& basePrefix, - const std::string& baseSuffix) const -{ - std::string res = FPathChecksum.getPart(sourceFile); - res += "/"; - res += basePrefix; - res += cmSystemTools::GetFilenameWithoutLastExtension(sourceFile); - res += baseSuffix; - return res; -} - -/** * @brief Generates the parent directory of the given file on demand * @return True on success */ bool cmQtAutoGenerators::MakeParentDirectory( - cmQtAutoGen::GeneratorType genType, const std::string& filename) const + cmQtAutoGen::GeneratorType genType, std::string const& filename) const { bool success = true; - const std::string dirName = cmSystemTools::GetFilenamePath(filename); + std::string const dirName = cmSystemTools::GetFilenamePath(filename); if (!dirName.empty()) { - success = cmSystemTools::MakeDirectory(dirName); - if (!success) { + if (!cmSystemTools::MakeDirectory(dirName)) { this->LogFileError(genType, filename, "Could not create parent directory"); + success = false; } } return success; } -bool cmQtAutoGenerators::FileDiffers(const std::string& filename, - const std::string& content) +bool cmQtAutoGenerators::FileDiffers(std::string const& filename, + std::string const& content) { bool differs = true; { std::string oldContents; - if (ReadAll(oldContents, filename)) { + if (ReadFile(oldContents, filename)) { differs = (oldContents != content); } } @@ -1941,14 +2239,15 @@ bool cmQtAutoGenerators::FileDiffers(const std::string& filename, } bool cmQtAutoGenerators::FileWrite(cmQtAutoGen::GeneratorType genType, - const std::string& filename, - const std::string& content) + 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::trunc); + outfile.open(filename.c_str(), + (std::ios::out | std::ios::binary | std::ios::trunc)); if (outfile) { outfile << content; // Check for write errors @@ -1970,7 +2269,7 @@ bool cmQtAutoGenerators::FileWrite(cmQtAutoGen::GeneratorType genType, * @brief Runs a command and returns true on success * @return True on success */ -bool cmQtAutoGenerators::RunCommand(const std::vector<std::string>& command, +bool cmQtAutoGenerators::RunCommand(std::vector<std::string> const& command, std::string& output) const { // Log command @@ -1992,7 +2291,7 @@ bool cmQtAutoGenerators::RunCommand(const std::vector<std::string>& command, * @return True on success */ bool cmQtAutoGenerators::FindHeader(std::string& header, - const std::string& testBasePath) const + std::string const& testBasePath) const { for (std::string const& ext : this->HeaderExtensions) { std::string testFilePath(testBasePath); @@ -2005,56 +2304,3 @@ bool cmQtAutoGenerators::FindHeader(std::string& header, } return false; } - -std::string cmQtAutoGenerators::MocFindHeader( - const std::string& sourcePath, const std::string& 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 (FindHeader(header, fullPath)) { - break; - } - } - } - // Sanitize - if (!header.empty()) { - header = cmSystemTools::GetRealPath(header); - } - return header; -} - -bool cmQtAutoGenerators::MocFindIncludedFile( - std::string& absFile, const std::string& sourcePath, - const std::string& 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; -} diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h index 5d3ad74..0b502de 100644 --- a/Source/cmQtAutoGenerators.h +++ b/Source/cmQtAutoGenerators.h @@ -10,6 +10,7 @@ #include "cmsys/RegularExpression.hxx" #include <map> +#include <memory> // IWYU pragma: keep #include <set> #include <string> #include <vector> @@ -21,7 +22,7 @@ class cmQtAutoGenerators CM_DISABLE_COPY(cmQtAutoGenerators) public: cmQtAutoGenerators(); - bool Run(const std::string& targetDirectory, const std::string& config); + bool Run(std::string const& targetDirectory, std::string const& config); private: // -- Types @@ -37,7 +38,7 @@ private: { } - KeyRegExp(const std::string& key, const std::string& regExp) + KeyRegExp(std::string const& key, std::string const& regExp) : Key(key) , RegExp(regExp) { @@ -47,6 +48,38 @@ private: 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<std::string> 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; + }; + /// @brief RCC job struct RccJob { @@ -56,149 +89,110 @@ private: std::vector<std::string> Inputs; }; - // -- Configuration - bool MocDependFilterPush(const std::string& key, const std::string& regExp); - bool ReadAutogenInfoFile(cmMakefile* makefile, - const std::string& targetDirectory, - const std::string& config); - - bool MocEnabled() const { return !this->MocExecutable.empty(); } - bool UicEnabled() const { return !this->UicExecutable.empty(); } - bool RccEnabled() const { return !this->RccExecutable.empty(); } + // -- Initialization + bool InitInfoFile(cmMakefile* makefile, std::string const& targetDirectory, + std::string const& config); // -- Settings file void SettingsFileRead(cmMakefile* makefile); bool SettingsFileWrite(); - - bool AnySettingsChanged() const + bool SettingsChanged() const { return (this->MocSettingsChanged || this->RccSettingsChanged || this->UicSettingsChanged); } - // -- Init and run - void Init(cmMakefile* makefile); - bool RunAutogen(); - - // -- Content analysis - bool MocRequired(const std::string& contentText, - std::string* macroName = nullptr); - void MocFindDepends( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::set<std::string>>& mocDepends); - - bool MocSkip(const std::string& absFilename) const; - bool UicSkip(const std::string& absFilename) const; - - bool ParseSourceFile( - const std::string& absFilename, - std::map<std::string, std::string>& mocsIncluded, - std::map<std::string, std::set<std::string>>& mocDepends, - std::map<std::string, std::vector<std::string>>& includedUis, - bool relaxed); - - void SearchHeadersForSourceFile(const std::string& absFilename, - std::set<std::string>& mocHeaderFiles, - std::set<std::string>& uicHeaderFiles) const; - - bool ParseHeaders( - const std::set<std::string>& mocHeaderFiles, - const std::set<std::string>& uicHeaderFiles, - const std::map<std::string, std::string>& mocsIncluded, - std::map<std::string, std::string>& mocsNotIncluded, - std::map<std::string, std::set<std::string>>& mocDepends, - std::map<std::string, std::vector<std::string>>& includedUis); - - void UicParseContent( - const std::string& fileName, const std::string& contentText, - std::map<std::string, std::vector<std::string>>& includedUis); - - std::string MocMacroNamesString() const; - std::string MocHeaderSuffixesString() const; - - bool MocParseSourceContent( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::string>& mocsIncluded, - std::map<std::string, std::set<std::string>>& mocDepends, bool relaxed); + // -- Central processing + bool Process(); - void MocParseHeaderContent( - const std::string& absFilename, const std::string& contentText, - std::map<std::string, std::string>& mocsNotIncluded, - std::map<std::string, std::set<std::string>>& mocDepends); + // -- Source parsing + bool ParseSourceFile(std::string const& absFilename, const SourceJob& job); + bool ParseHeaderFile(std::string const& absFilename, const SourceJob& job); + bool ParsePostprocess(); - // -- Moc file generation - bool MocGenerateAll( - const std::map<std::string, std::string>& mocsIncluded, - const std::map<std::string, std::string>& mocsNotIncluded, - const std::map<std::string, std::set<std::string>>& mocDepends); - bool MocGenerateFile( - const std::string& sourceFile, const std::string& mocFileName, - const std::map<std::string, std::set<std::string>>& mocDepends, - bool included); + // -- 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<std::string>& 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 file generation - bool UicFindIncludedFile(std::string& absFile, const std::string& sourceFile, - const std::string& searchPath, - const std::string& searchFile); - bool UicGenerateAll( - const std::map<std::string, std::vector<std::string>>& includedUis); - bool UicGenerateFile(const std::string& realName, - const std::string& uiInputFile, - const std::string& uiOutputFile); + // -- 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); - // -- Rcc file generation + // -- Rcc + bool RccEnabled() const { return !this->RccExecutable.empty(); } bool RccGenerateAll(); bool RccGenerateFile(const RccJob& rccJob); // -- Log info - void LogBold(const std::string& message) const; + void LogBold(std::string const& message) const; void LogInfo(cmQtAutoGen::GeneratorType genType, - const std::string& message) const; + std::string const& message) const; // -- Log warning void LogWarning(cmQtAutoGen::GeneratorType genType, - const std::string& message) const; + std::string const& message) const; void LogFileWarning(cmQtAutoGen::GeneratorType genType, - const std::string& filename, - const std::string& message) const; + std::string const& filename, + std::string const& message) const; // -- Log error void LogError(cmQtAutoGen::GeneratorType genType, - const std::string& message) const; + std::string const& message) const; void LogFileError(cmQtAutoGen::GeneratorType genType, - const std::string& filename, - const std::string& message) const; + std::string const& filename, + std::string const& message) const; void LogCommandError(cmQtAutoGen::GeneratorType genType, - const std::string& message, - const std::vector<std::string>& command, - const std::string& output) const; - void LogNameCollisionError( - cmQtAutoGen::GeneratorType genType, const std::string& message, - const std::multimap<std::string, std::string>& collisions) const; + std::string const& message, + std::vector<std::string> const& command, + std::string const& output) const; // -- Utility - bool NameCollisionTest( - const std::map<std::string, std::string>& genFiles, - std::multimap<std::string, std::string>& collisions) const; - std::string ChecksumedPath(const std::string& sourceFile, - const std::string& basePrefix, - const std::string& baseSuffix) const; bool MakeParentDirectory(cmQtAutoGen::GeneratorType genType, - const std::string& filename) const; - bool FileDiffers(const std::string& filename, const std::string& content); + std::string const& filename) const; + bool FileDiffers(std::string const& filename, std::string const& content); bool FileWrite(cmQtAutoGen::GeneratorType genType, - const std::string& filename, const std::string& content); - - bool RunCommand(const std::vector<std::string>& command, + std::string const& filename, std::string const& content); + bool FindHeader(std::string& header, std::string const& testBasePath) const; + bool RunCommand(std::vector<std::string> const& command, std::string& output) const; - bool FindHeader(std::string& header, const std::string& testBasePath) const; - - std::string MocFindHeader(const std::string& sourcePath, - const std::string& includeBase) const; - bool MocFindIncludedFile(std::string& absFile, const std::string& sourceFile, - const std::string& includeString) const; - // -- Meta std::string ConfigSuffix; + std::string InfoFile; + // -- Settings + bool IncludeProjectDirsBefore; + bool Verbose; + bool ColorOutput; + std::string SettingsFile; + std::string SettingsStringMoc; + std::string SettingsStringUic; + std::string SettingsStringRcc; // -- Directories std::string ProjectSourceDir; std::string ProjectBinaryDir; @@ -213,23 +207,14 @@ private: std::string UicExecutable; std::string RccExecutable; // -- File lists - std::vector<std::string> Sources; - std::vector<std::string> Headers; + std::map<std::string, SourceJob> HeaderJobs; + std::map<std::string, SourceJob> SourceJobs; std::vector<std::string> HeaderExtensions; - cmFilePathChecksum FPathChecksum; - // -- Settings - bool IncludeProjectDirsBefore; - bool Verbose; - bool ColorOutput; - std::string SettingsFile; - std::string SettingsStringMoc; - std::string SettingsStringUic; - std::string SettingsStringRcc; + cmFilePathChecksum FilePathChecksum; // -- Moc bool MocSettingsChanged; bool MocPredefsChanged; bool MocRelaxedMode; - bool MocRunFailed; std::string MocCompFileRel; std::string MocCompFileAbs; std::string MocPredefsFileRel; @@ -239,21 +224,23 @@ private: std::vector<std::string> MocIncludes; std::vector<std::string> MocDefinitions; std::vector<std::string> MocOptions; + std::vector<std::string> MocAllOptions; std::vector<std::string> MocPredefsCmd; std::vector<KeyRegExp> MocDependFilters; std::vector<KeyRegExp> MocMacroFilters; cmsys::RegularExpression MocRegExpInclude; + std::vector<std::unique_ptr<MocJobIncluded>> MocJobsIncluded; + std::vector<std::unique_ptr<MocJobAuto>> MocJobsAuto; // -- Uic bool UicSettingsChanged; - bool UicRunFailed; std::vector<std::string> UicSkipList; std::vector<std::string> UicTargetOptions; std::map<std::string, std::vector<std::string>> UicOptions; std::vector<std::string> UicSearchPaths; cmsys::RegularExpression UicRegExpInclude; + std::vector<std::unique_ptr<UicJob>> UicJobs; // -- Rcc bool RccSettingsChanged; - bool RccRunFailed; std::vector<RccJob> RccJobs; }; diff --git a/Source/cmServer.cxx b/Source/cmServer.cxx index 30d0f51..e923c22 100644 --- a/Source/cmServer.cxx +++ b/Source/cmServer.cxx @@ -84,7 +84,7 @@ void cmServer::ProcessRequest(cmConnection* connection, const cmServerRequest request(this, connection, value[kTYPE_KEY].asString(), value[kCOOKIE_KEY].asString(), value); - if (request.Type == "") { + if (request.Type.empty()) { cmServerResponse response(request); response.SetError("No type given in request."); this->WriteResponse(connection, response, nullptr); diff --git a/Source/cmSourceFileLocation.cxx b/Source/cmSourceFileLocation.cxx index 727adeb..4f337f2 100644 --- a/Source/cmSourceFileLocation.cxx +++ b/Source/cmSourceFileLocation.cxx @@ -28,21 +28,6 @@ cmSourceFileLocation::cmSourceFileLocation(const cmSourceFileLocation& loc) this->Name = loc.Name; } -cmSourceFileLocation& cmSourceFileLocation::operator=( - const cmSourceFileLocation& loc) -{ - if (this == &loc) { - return *this; - } - this->Makefile = loc.Makefile; - this->AmbiguousDirectory = loc.AmbiguousDirectory; - this->AmbiguousExtension = loc.AmbiguousExtension; - this->Directory = loc.Directory; - this->Name = loc.Name; - this->UpdateExtension(this->Name); - return *this; -} - cmSourceFileLocation::cmSourceFileLocation(cmMakefile const* mf, const std::string& name) : Makefile(mf) diff --git a/Source/cmSourceFileLocation.h b/Source/cmSourceFileLocation.h index 6dbc2da..fb5b330 100644 --- a/Source/cmSourceFileLocation.h +++ b/Source/cmSourceFileLocation.h @@ -29,7 +29,6 @@ public: cmSourceFileLocation(cmMakefile const* mf, const std::string& name); cmSourceFileLocation(); cmSourceFileLocation(const cmSourceFileLocation& loc); - cmSourceFileLocation& operator=(const cmSourceFileLocation& loc); /** * Return whether the given source file location could refers to the @@ -79,7 +78,7 @@ public: */ cmMakefile const* GetMakefile() const { return this->Makefile; } private: - cmMakefile const* Makefile; + cmMakefile const* const Makefile; bool AmbiguousDirectory; bool AmbiguousExtension; std::string Directory; @@ -90,6 +89,9 @@ private: // Update the location with additional knowledge. void Update(cmSourceFileLocation const& loc); void UpdateExtension(const std::string& name); + + cmSourceFileLocation& operator=(const cmSourceFileLocation& loc) + CM_EQ_DELETE; }; #endif diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 5f2737d..58adc43 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -1375,7 +1375,7 @@ std::string cmSystemTools::CollapseCombinedPath(std::string const& dir, if (fileComponents.empty()) { return dir; } - if (fileComponents[0] != "") { + if (!fileComponents[0].empty()) { // File is not a relative path. return file; } diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index c26d49a..49b0664 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -5,6 +5,7 @@ #include "cmsys/RegularExpression.hxx" #include <algorithm> #include <assert.h> +#include <iterator> #include <map> #include <set> #include <sstream> @@ -592,8 +593,9 @@ public: { std::vector<std::string> files; cmSystemTools::ExpandListArgument(entry, files); - std::vector<cmSourceFileLocation> locations(files.size()); - std::transform(files.begin(), files.end(), locations.begin(), + std::vector<cmSourceFileLocation> locations; + locations.reserve(files.size()); + std::transform(files.begin(), files.end(), std::back_inserter(locations), CreateLocation(this->Needle.GetMakefile())); return std::find_if(locations.begin(), locations.end(), diff --git a/Source/cmVariableWatch.cxx b/Source/cmVariableWatch.cxx index 0e072b8..bd5d19c 100644 --- a/Source/cmVariableWatch.cxx +++ b/Source/cmVariableWatch.cxx @@ -2,11 +2,9 @@ file Copyright.txt or https://cmake.org/licensing for details. */ #include "cmVariableWatch.h" -#include "cmAlgorithms.h" - -#include "cm_auto_ptr.hxx" -#include <algorithm> +#include <memory> #include <utility> +#include <vector> static const char* const cmVariableWatchAccessStrings[] = { "READ_ACCESS", "UNKNOWN_READ_ACCESS", "UNKNOWN_DEFINED_ACCESS", @@ -25,35 +23,27 @@ cmVariableWatch::cmVariableWatch() { } -template <typename C> -void deleteAllSecond(typename C::value_type it) -{ - cmDeleteAll(it.second); -} - cmVariableWatch::~cmVariableWatch() { - std::for_each(this->WatchMap.begin(), this->WatchMap.end(), - deleteAllSecond<cmVariableWatch::StringToVectorOfPairs>); } bool cmVariableWatch::AddWatch(const std::string& variable, WatchMethod method, void* client_data /*=0*/, DeleteData delete_data /*=0*/) { - CM_AUTO_PTR<cmVariableWatch::Pair> p(new cmVariableWatch::Pair); + auto p = std::make_shared<cmVariableWatch::Pair>(); p->Method = method; p->ClientData = client_data; p->DeleteDataCall = delete_data; cmVariableWatch::VectorOfPairs& vp = this->WatchMap[variable]; - for (cmVariableWatch::Pair* pair : vp) { + for (auto& pair : vp) { if (pair->Method == method && client_data && client_data == pair->ClientData) { // Callback already exists return false; } } - vp.push_back(p.release()); + vp.push_back(std::move(p)); return true; } @@ -70,7 +60,6 @@ void cmVariableWatch::RemoveWatch(const std::string& variable, // If client_data is NULL, we want to disconnect all watches against // the given method; otherwise match ClientData as well. (!client_data || (client_data == (*it)->ClientData))) { - delete *it; vp->erase(it); return; } @@ -84,9 +73,17 @@ bool cmVariableWatch::VariableAccessed(const std::string& variable, cmVariableWatch::StringToVectorOfPairs::const_iterator mit = this->WatchMap.find(variable); if (mit != this->WatchMap.end()) { - const cmVariableWatch::VectorOfPairs* vp = &mit->second; - for (cmVariableWatch::Pair* it : *vp) { - it->Method(variable, access_type, it->ClientData, newValue, mf); + // The strategy here is to copy the list of callbacks, and ignore + // new callbacks that existing ones may add. + std::vector<std::weak_ptr<Pair>> vp(mit->second.begin(), + mit->second.end()); + for (auto& weak_it : vp) { + // In the case where a callback was removed, the weak_ptr will not be + // lockable, and so this ensures we don't attempt to call into freed + // memory + if (auto it = weak_it.lock()) { + it->Method(variable, access_type, it->ClientData, newValue, mf); + } } return true; } diff --git a/Source/cmVariableWatch.h b/Source/cmVariableWatch.h index 05a0a56..27d1b12 100644 --- a/Source/cmVariableWatch.h +++ b/Source/cmVariableWatch.h @@ -6,6 +6,7 @@ #include "cmConfigure.h" // IWYU pragma: keep #include <map> +#include <memory> // IWYU pragma: keep #include <string> #include <vector> @@ -79,7 +80,7 @@ protected: } }; - typedef std::vector<Pair*> VectorOfPairs; + typedef std::vector<std::shared_ptr<Pair>> VectorOfPairs; typedef std::map<std::string, VectorOfPairs> StringToVectorOfPairs; StringToVectorOfPairs WatchMap; diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 99b9bce..8dce028 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -407,7 +407,7 @@ void cmVisualStudio10TargetGenerator::Generate() continue; std::string globalKey = keyIt->substr(strlen(prefix)); // Skip invalid or separately-handled properties. - if (globalKey == "" || globalKey == "PROJECT_TYPES" || + if (globalKey.empty() || globalKey == "PROJECT_TYPES" || globalKey == "ROOTNAMESPACE" || globalKey == "KEYWORD") { continue; } @@ -614,7 +614,7 @@ void cmVisualStudio10TargetGenerator::WriteDotNetReferences() ++i) { if (i->first.find("VS_DOTNET_REFERENCE_") == 0) { std::string name = i->first.substr(20); - if (name != "") { + if (!name.empty()) { std::string path = i->second.GetValue(); if (!cmsys::SystemTools::FileIsFullPath(path)) { path = std::string(this->GeneratorTarget->Target->GetMakefile() diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend1.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend1.cmake index 70d6edf..73fd0ab 100644 --- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend1.cmake +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend1.cmake @@ -6,7 +6,7 @@ include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake) # expected results -set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2-1_*.deb") +set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb") set(expected_count 3) diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend2.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend2.cmake index 415d536..81dbbc5 100644 --- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend2.cmake +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-depend2.cmake @@ -6,7 +6,7 @@ include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake) # expected results -set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2-1_*.deb") +set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb") set(expected_count 3) set(config_verbose -V) diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description1.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description1.cmake index 337cc16..ad52f56 100644 --- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description1.cmake +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description1.cmake @@ -6,7 +6,7 @@ include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake) # expected results -set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2-1_*.deb") +set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb") set(expected_count 3) diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake index 35ca74c..af27c51 100644 --- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-description2.cmake @@ -7,7 +7,7 @@ include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake) # expected results -set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2-1_*.deb") +set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb") set(expected_count 3) diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-lintian-dpkgdeb-checks.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-lintian-dpkgdeb-checks.cmake index f1391cd..1fe9258 100644 --- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-lintian-dpkgdeb-checks.cmake +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-lintian-dpkgdeb-checks.cmake @@ -5,7 +5,7 @@ endif() include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake) # TODO: currently debian doens't produce lower cased names -set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2-1_*.deb") +set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb") set(expected_count 3) diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-shlibdeps1.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-shlibdeps1.cmake index fcfc7ea..e57488c 100644 --- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-shlibdeps1.cmake +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-shlibdeps1.cmake @@ -9,7 +9,7 @@ include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake) # requirements # debian now produces lower case names -set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2-1_*.deb") +set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb") set(expected_count 3) diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-source.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-source.cmake index 351bf21..5ee057a 100644 --- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-source.cmake +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-components-source.cmake @@ -6,7 +6,7 @@ include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake) # expected results -set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2-1_*.deb") +set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib-*_1.0.2_*.deb") set(expected_count 3) set(config_verbose -V) diff --git a/Tests/CPackComponentsDEB/RunCPackVerifyResult-compression.cmake b/Tests/CPackComponentsDEB/RunCPackVerifyResult-compression.cmake index c97ecb0..13a626a 100644 --- a/Tests/CPackComponentsDEB/RunCPackVerifyResult-compression.cmake +++ b/Tests/CPackComponentsDEB/RunCPackVerifyResult-compression.cmake @@ -5,7 +5,7 @@ endif() include(${CPackComponentsDEB_SOURCE_DIR}/RunCPackVerifyResult.cmake) # TODO: currently debian doens't produce lower cased names -set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib_1.0.2-1_*.deb") +set(expected_file_mask "${CPackComponentsDEB_BINARY_DIR}/mylib_1.0.2_*.deb") set(expected_count 1) set(actual_output) diff --git a/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake b/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake index a5b38fd..2c3a849 100644 --- a/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake +++ b/Tests/CPackComponentsForAll/RunCPackVerifyResult.cmake @@ -60,7 +60,7 @@ elseif (CPackGen MATCHES "RPM") set(expected_count 1) endif () elseif (CPackGen MATCHES "DEB") - set(expected_file_mask "${CPackComponentsForAll_BINARY_DIR}/mylib*_1.0.2-1_*.deb") + set(expected_file_mask "${CPackComponentsForAll_BINARY_DIR}/mylib*_1.0.2_*.deb") if (${CPackComponentWay} STREQUAL "default") set(expected_count 1) elseif (${CPackComponentWay} STREQUAL "OnePackPerGroup") diff --git a/Tests/QtAutogen/sameName/CMakeLists.txt b/Tests/QtAutogen/sameName/CMakeLists.txt index 4d2dcd9..f695875 100644 --- a/Tests/QtAutogen/sameName/CMakeLists.txt +++ b/Tests/QtAutogen/sameName/CMakeLists.txt @@ -17,7 +17,10 @@ add_executable(sameName main.cpp ) target_link_libraries(sameName ${QT_LIBRARIES}) -set_target_properties(sameName PROPERTIES AUTOMOC TRUE AUTORCC TRUE) +set_target_properties(sameName PROPERTIES + AUTOMOC TRUE + AUTOUIC TRUE + AUTORCC TRUE) # Set different compression levels if (QT_TEST_VERSION STREQUAL 4) diff --git a/Tests/QtAutogen/sameName/aaa/bbb/item.cpp b/Tests/QtAutogen/sameName/aaa/bbb/item.cpp index 20d0044..850206f 100644 --- a/Tests/QtAutogen/sameName/aaa/bbb/item.cpp +++ b/Tests/QtAutogen/sameName/aaa/bbb/item.cpp @@ -3,8 +3,20 @@ namespace aaa { namespace bbb { +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + void Item::go() { + MocLocal obj; } } } + +#include "aaa/bbb/item.moc" diff --git a/Tests/QtAutogen/sameName/aaa/item.cpp b/Tests/QtAutogen/sameName/aaa/item.cpp index 95dd3b6..e35d3d1 100644 --- a/Tests/QtAutogen/sameName/aaa/item.cpp +++ b/Tests/QtAutogen/sameName/aaa/item.cpp @@ -1,8 +1,22 @@ #include "item.hpp" +// Include ui_view.h only in header namespace aaa { +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + void Item::go() { + Ui_ViewAAA ui; + MocLocal obj; } } + +#include "aaa/item.moc" diff --git a/Tests/QtAutogen/sameName/aaa/item.hpp b/Tests/QtAutogen/sameName/aaa/item.hpp index b63466f..875f72f 100644 --- a/Tests/QtAutogen/sameName/aaa/item.hpp +++ b/Tests/QtAutogen/sameName/aaa/item.hpp @@ -2,6 +2,8 @@ #define AAA_ITEM_HPP #include <QObject> +// Include ui_view.h only in header +#include <aaa/ui_view.h> namespace aaa { diff --git a/Tests/QtAutogen/sameName/aaa/view.ui b/Tests/QtAutogen/sameName/aaa/view.ui new file mode 100644 index 0000000..0f09980 --- /dev/null +++ b/Tests/QtAutogen/sameName/aaa/view.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ViewAAA</class> + <widget class="QWidget" name="Base"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeView" name="treeView"/> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Tests/QtAutogen/sameName/bbb/aaa/item.cpp b/Tests/QtAutogen/sameName/bbb/aaa/item.cpp index ac4b2c2..7ad01c3 100644 --- a/Tests/QtAutogen/sameName/bbb/aaa/item.cpp +++ b/Tests/QtAutogen/sameName/bbb/aaa/item.cpp @@ -3,8 +3,20 @@ namespace bbb { namespace aaa { +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + void Item::go() { + MocLocal obj; } } } + +#include "bbb/aaa/item.moc" diff --git a/Tests/QtAutogen/sameName/bbb/item.cpp b/Tests/QtAutogen/sameName/bbb/item.cpp index f97a143..9ef128e 100644 --- a/Tests/QtAutogen/sameName/bbb/item.cpp +++ b/Tests/QtAutogen/sameName/bbb/item.cpp @@ -1,8 +1,23 @@ #include "item.hpp" +// Include ui_view.h only in source +#include <bbb/ui_view.h> namespace bbb { +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; + void Item::go() { + Ui_ViewBBB ui; + MocLocal obj; } } + +#include "bbb/item.moc" diff --git a/Tests/QtAutogen/sameName/bbb/item.hpp b/Tests/QtAutogen/sameName/bbb/item.hpp index 5b7f985..d39a9d7 100644 --- a/Tests/QtAutogen/sameName/bbb/item.hpp +++ b/Tests/QtAutogen/sameName/bbb/item.hpp @@ -2,6 +2,7 @@ #define BBB_ITEM_HPP #include <QObject> +// Include ui_view.h only in source namespace bbb { diff --git a/Tests/QtAutogen/sameName/bbb/view.ui b/Tests/QtAutogen/sameName/bbb/view.ui new file mode 100644 index 0000000..a8f506e --- /dev/null +++ b/Tests/QtAutogen/sameName/bbb/view.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ViewBBB</class> + <widget class="QWidget" name="Base"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeView" name="treeView"/> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Tests/QtAutogen/sameName/ccc/item.cpp b/Tests/QtAutogen/sameName/ccc/item.cpp index d90b2b8..ab8a281 100644 --- a/Tests/QtAutogen/sameName/ccc/item.cpp +++ b/Tests/QtAutogen/sameName/ccc/item.cpp @@ -1,23 +1,25 @@ #include "item.hpp" +// Include ui_view.h in source and header +#include <ccc/ui_view.h> namespace ccc { -void Item::go() -{ -} - -class MocTest : public QObject +class MocLocal : public QObject { Q_OBJECT; - Q_SLOT - void go(); + +public: + MocLocal() = default; + ~MocLocal() = default; }; -void MocTest::go() +void Item::go() { + Ui_ViewCCC ui; + MocLocal obj; } } // Include own moc files -#include "item.moc" +#include "ccc/item.moc" #include "moc_item.cpp" diff --git a/Tests/QtAutogen/sameName/ccc/item.hpp b/Tests/QtAutogen/sameName/ccc/item.hpp index 96fcc24..20d9dd9 100644 --- a/Tests/QtAutogen/sameName/ccc/item.hpp +++ b/Tests/QtAutogen/sameName/ccc/item.hpp @@ -2,6 +2,8 @@ #define CCC_ITEM_HPP #include <QObject> +// Include ui_view.h in source and header +#include <ccc/ui_view.h> namespace ccc { diff --git a/Tests/QtAutogen/sameName/ccc/view.ui b/Tests/QtAutogen/sameName/ccc/view.ui new file mode 100644 index 0000000..7989c69 --- /dev/null +++ b/Tests/QtAutogen/sameName/ccc/view.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ViewCCC</class> + <widget class="QWidget" name="Base"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeView" name="treeView"/> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Tests/QtAutogen/sameName/item.cpp b/Tests/QtAutogen/sameName/item.cpp index e013cf3..3d1fbe7 100644 --- a/Tests/QtAutogen/sameName/item.cpp +++ b/Tests/QtAutogen/sameName/item.cpp @@ -1,5 +1,20 @@ #include "item.hpp" +// Include ui_view.h in source and header +#include <ui_view.h> + +class MocLocal : public QObject +{ + Q_OBJECT; + +public: + MocLocal() = default; + ~MocLocal() = default; +}; void Item::go() { + Ui_View ui; + MocLocal obj; } + +#include "item.moc" diff --git a/Tests/QtAutogen/sameName/item.hpp b/Tests/QtAutogen/sameName/item.hpp index 91bba3b..75e83f4 100644 --- a/Tests/QtAutogen/sameName/item.hpp +++ b/Tests/QtAutogen/sameName/item.hpp @@ -2,6 +2,8 @@ #define ITEM_HPP #include <QObject> +// Include ui_view.h in source and header +#include <ui_view.h> class Item : public QObject { diff --git a/Tests/QtAutogen/sameName/view.ui b/Tests/QtAutogen/sameName/view.ui new file mode 100644 index 0000000..2ffe734 --- /dev/null +++ b/Tests/QtAutogen/sameName/view.ui @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>View</class> + <widget class="QWidget" name="Base"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QTreeView" name="treeView"/> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Tests/QtAutogenRerun/CMakeLists.txt b/Tests/QtAutogenRerun/CMakeLists.txt index 088025f..e72c191 100644 --- a/Tests/QtAutogenRerun/CMakeLists.txt +++ b/Tests/QtAutogenRerun/CMakeLists.txt @@ -36,204 +36,17 @@ else() endif() -# -- Test -# Dummy test to generate clean target +# Dummy executable to generate clean target add_executable(dummy dummy.cpp) # -- Test -# When a file listed in a .qrc file changes the target must be rebuilt -set(timeformat "%Y%j%H%M%S") -set(RCC_DEPENDS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/rccDepends") -set(RCC_DEPENDS_BIN "${CMAKE_CURRENT_BINARY_DIR}/rccDepends") -configure_file(${RCC_DEPENDS_SRC}/res1a.qrc.in ${RCC_DEPENDS_BIN}/res1.qrc COPYONLY) -configure_file(${RCC_DEPENDS_SRC}/res2a.qrc.in ${RCC_DEPENDS_BIN}/res2.qrc.in COPYONLY) -try_compile(RCC_DEPENDS - "${RCC_DEPENDS_BIN}" - "${RCC_DEPENDS_SRC}" - rccDepends - CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}" - "-DQT_TEST_VERSION=${QT_TEST_VERSION}" - "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}" - OUTPUT_VARIABLE output -) -if (NOT RCC_DEPENDS) - message(SEND_ERROR "Initial build of rccDepends failed. Output: ${output}") -endif() -# Get name and timestamp of the output binary -file(STRINGS "${RCC_DEPENDS_BIN}/target.txt" targetList ENCODING UTF-8) -list(GET targetList 0 rccDependsBin) -file(TIMESTAMP "${rccDependsBin}" timeBegin "${timeformat}") -# Sleep, touch regular qrc input file, rebuild and compare timestamp -execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change. -execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${RCC_DEPENDS_BIN}/res1/input.txt") -execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${RCC_DEPENDS_BIN}" RESULT_VARIABLE result) -if (result) - message(SEND_ERROR "Second build of rccDepends failed.") -endif() -file(TIMESTAMP "${rccDependsBin}" timeStep1 "${timeformat}") -if (NOT timeStep1 GREATER timeBegin) - message(SEND_ERROR "File (${rccDependsBin}) should have changed in the first step!") -endif() -# Sleep, update regular qrc file, rebuild and compare timestamp -execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change. -configure_file(${RCC_DEPENDS_SRC}/res1b.qrc.in ${RCC_DEPENDS_BIN}/res1.qrc COPYONLY) -execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${RCC_DEPENDS_BIN}" RESULT_VARIABLE result) -if (result) - message(SEND_ERROR "Third build of rccDepends failed.") -endif() -file(TIMESTAMP "${rccDependsBin}" timeStep2 "${timeformat}") -if (NOT timeStep2 GREATER timeStep1) - message(SEND_ERROR "File (${rccDependsBin}) should have changed in the second step!") -endif() -# Sleep, touch regular qrc newly added input file, rebuild and compare timestamp -execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change. -execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${RCC_DEPENDS_BIN}/res1/inputAdded.txt") -execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${RCC_DEPENDS_BIN}" RESULT_VARIABLE result) -if (result) - message(SEND_ERROR "Fourth build of rccDepends failed.") -endif() -file(TIMESTAMP "${rccDependsBin}" timeStep3 "${timeformat}") -if (NOT timeStep3 GREATER timeStep2) - message(SEND_ERROR "File (${rccDependsBin}) should have changed in the third step!") -endif() -# Sleep, touch generated qrc input file, rebuild and compare timestamp -execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change. -execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${RCC_DEPENDS_BIN}/res2/input.txt") -execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${RCC_DEPENDS_BIN}" RESULT_VARIABLE result) -if (result) - message(SEND_ERROR "Fifth build of rccDepends failed.") -endif() -file(TIMESTAMP "${rccDependsBin}" timeStep4 "${timeformat}") -if (NOT timeStep4 GREATER timeStep3) - message(SEND_ERROR "File (${rccDependsBin}) should have changed in the fourth step!") -endif() -# Sleep, update generated qrc file, rebuild and compare timestamp -execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change. -configure_file(${RCC_DEPENDS_SRC}/res2b.qrc.in ${RCC_DEPENDS_BIN}/res2.qrc.in COPYONLY) -execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${RCC_DEPENDS_BIN}" RESULT_VARIABLE result) -if (result) - message(SEND_ERROR "Sixth build of rccDepends failed.") -endif() -file(TIMESTAMP "${rccDependsBin}" timeStep5 "${timeformat}") -if (NOT timeStep5 GREATER timeStep4) - message(SEND_ERROR "File (${rccDependsBin}) should have changed in the fitfh step!") -endif() -# Sleep, touch generated qrc newly added input file, rebuild and compare timestamp -execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change. -execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${RCC_DEPENDS_BIN}/res2/inputAdded.txt") -execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${RCC_DEPENDS_BIN}" RESULT_VARIABLE result) -if (result) - message(SEND_ERROR "Seventh build of rccDepends failed.") -endif() -file(TIMESTAMP "${rccDependsBin}" timeStep6 "${timeformat}") -if (NOT timeStep6 GREATER timeStep5) - message(SEND_ERROR "File (${rccDependsBin}) should have changed in the sixth step!") -endif() - - -# -- Test -# Ensure a repeated build succeeds when a header containing a QObject changes -set(timeformat "%Y%j%H%M%S") -configure_file(mocRerun/test1a.h.in mocRerun/test1.h COPYONLY) -try_compile(MOC_RERUN - "${CMAKE_CURRENT_BINARY_DIR}/mocRerun" - "${CMAKE_CURRENT_SOURCE_DIR}/mocRerun" - mocRerun - CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}" - "-DQT_TEST_VERSION=${QT_TEST_VERSION}" - "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}" - OUTPUT_VARIABLE output -) -if (NOT MOC_RERUN) - message(SEND_ERROR "Initial build of mocRerun failed. Output: ${output}") -endif() -# Get name and timestamp of the output binary -file(STRINGS "${CMAKE_CURRENT_BINARY_DIR}/mocRerun/target1.txt" target1List ENCODING UTF-8) -list(GET target1List 0 binFile) -file(TIMESTAMP "${binFile}" timeBegin "${timeformat}") -# Change file content and rebuild -execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) -configure_file(mocRerun/test1b.h.in mocRerun/test1.h COPYONLY) -execute_process(COMMAND "${CMAKE_COMMAND}" --build . - WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mocRerun" - RESULT_VARIABLE mocRerun_result - ) -if (mocRerun_result) - message(SEND_ERROR "Second build of mocRerun failed.") -endif() -# Compare timestamps -file(TIMESTAMP "${binFile}" timeStep1 "${timeformat}") -if (NOT timeStep1 GREATER timeBegin) - message(SEND_ERROR "File (${binFile}) should have changed in the first step!") -endif() - +include("mocRerun.cmake") # -- Test # Tests Q_PLUGIN_METADATA json file change detection if (NOT QT_TEST_VERSION STREQUAL 4) - try_compile(MOC_PLUGIN - "${CMAKE_CURRENT_BINARY_DIR}/mocPlugin" - "${CMAKE_CURRENT_SOURCE_DIR}/mocPlugin" - mocPlugin - CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}" - "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}" - OUTPUT_VARIABLE output - ) - if (NOT MOC_PLUGIN) - message(SEND_ERROR "Initial build of mocPlugin failed. Output: ${output}") - endif() - - set(timeformat "%Y%j%H%M%S") - set(mocPlugSrcDir "${CMAKE_CURRENT_SOURCE_DIR}/mocPlugin") - set(mocPlugBinDir "${CMAKE_CURRENT_BINARY_DIR}/mocPlugin") - find_library(plAFile "PlugA" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH) - find_library(plBFile "PlugB" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH) - find_library(plCFile "PlugC" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH) - find_library(plDFile "PlugD" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH) - - file(TIMESTAMP "${plAFile}" plABefore "${timeformat}") - file(TIMESTAMP "${plBFile}" plBBefore "${timeformat}") - file(TIMESTAMP "${plCFile}" plCBefore "${timeformat}") - file(TIMESTAMP "${plDFile}" plDBefore "${timeformat}") - - # Ensure that the timestamp will change and change the json files - execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) - configure_file("${mocPlugSrcDir}/jsonIn/StyleD.json" "${mocPlugBinDir}/jsonFiles/StyleC.json") - configure_file("${mocPlugSrcDir}/jsonIn/StyleC.json" "${mocPlugBinDir}/jsonFiles/sub/StyleD.json") - execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocPlugBinDir}") - - file(TIMESTAMP "${plAFile}" plAAfter "${timeformat}") - file(TIMESTAMP "${plBFile}" plBAfter "${timeformat}") - file(TIMESTAMP "${plCFile}" plCAfter "${timeformat}") - file(TIMESTAMP "${plDFile}" plDAfter "${timeformat}") - - if (plAAfter GREATER plABefore) - message(SEND_ERROR "file (${plAFile}) should not have changed!") - endif() - if (plBAfter GREATER plBBefore) - message(SEND_ERROR "file (${plBFile}) should not have changed!") - endif() - if (NOT plCAfter GREATER plCBefore) - message(SEND_ERROR "file (${plCFile}) should have changed!") - endif() - if (NOT plDAfter GREATER plDBefore) - message(SEND_ERROR "file (${plDFile}) should have changed!") - endif() - - # Test custom macro - file(TIMESTAMP "${plCFile}" plCBefore "${timeformat}") - file(TIMESTAMP "${plDFile}" plDBefore "${timeformat}") - execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) - configure_file("${mocPlugSrcDir}/jsonIn/StyleD.json" "${mocPlugBinDir}/jsonFiles/StyleC_Custom.json") - configure_file("${mocPlugSrcDir}/jsonIn/StyleC.json" "${mocPlugBinDir}/jsonFiles/sub/StyleD_Custom.json") - execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocPlugBinDir}") - file(TIMESTAMP "${plCFile}" plCAfter "${timeformat}") - file(TIMESTAMP "${plDFile}" plDAfter "${timeformat}") - if (NOT plCAfter GREATER plCBefore) - message(SEND_ERROR "file (${plCFile}) should have changed!") - endif() - if (NOT plDAfter GREATER plDBefore) - message(SEND_ERROR "file (${plDFile}) should have changed!") - endif() - + include("mocPlugin.cmake") endif() + +# -- Test +include("rccDepends.cmake") diff --git a/Tests/QtAutogenRerun/mocPlugin.cmake b/Tests/QtAutogenRerun/mocPlugin.cmake new file mode 100644 index 0000000..7ad5ccb --- /dev/null +++ b/Tests/QtAutogenRerun/mocPlugin.cmake @@ -0,0 +1,96 @@ + +# Utility variables +set(timeformat "%Y%j%H%M%S") +set(mocPlugSrcDir "${CMAKE_CURRENT_SOURCE_DIR}/mocPlugin") +set(mocPlugBinDir "${CMAKE_CURRENT_BINARY_DIR}/mocPlugin") + +# Initial buid +try_compile(MOC_PLUGIN + "${mocPlugBinDir}" + "${mocPlugSrcDir}" + mocPlugin + CMAKE_FLAGS "-DQT_TEST_VERSION=${QT_TEST_VERSION}" + "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}" + OUTPUT_VARIABLE output +) +if (NOT MOC_PLUGIN) + message(SEND_ERROR "Initial build of mocPlugin failed. Output: ${output}") +endif() + +find_library(plAFile "PlugA" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH) +find_library(plBFile "PlugB" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH) +find_library(plCFile "PlugC" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH) +find_library(plDFile "PlugD" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH) +find_library(plEFile "PlugE" PATHS "${mocPlugBinDir}/Debug" "${mocPlugBinDir}" NO_DEFAULT_PATH) + +# - Ensure that the timestamp will change. +# - Change the json files referenced by Q_PLUGIN_METADATA +# - Rebuild +file(TIMESTAMP "${plAFile}" plABefore "${timeformat}") +file(TIMESTAMP "${plBFile}" plBBefore "${timeformat}") +file(TIMESTAMP "${plCFile}" plCBefore "${timeformat}") +file(TIMESTAMP "${plDFile}" plDBefore "${timeformat}") +file(TIMESTAMP "${plEFile}" plEBefore "${timeformat}") + +execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) +configure_file("${mocPlugSrcDir}/jsonIn/StyleD.json" "${mocPlugBinDir}/jsonFiles/StyleC.json") +configure_file("${mocPlugSrcDir}/jsonIn/StyleE.json" "${mocPlugBinDir}/jsonFiles/sub/StyleD.json") +configure_file("${mocPlugSrcDir}/jsonIn/StyleC.json" "${mocPlugBinDir}/jsonFiles/StyleE.json") +execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocPlugBinDir}") + +file(TIMESTAMP "${plAFile}" plAAfter "${timeformat}") +file(TIMESTAMP "${plBFile}" plBAfter "${timeformat}") +file(TIMESTAMP "${plCFile}" plCAfter "${timeformat}") +file(TIMESTAMP "${plDFile}" plDAfter "${timeformat}") +file(TIMESTAMP "${plEFile}" plEAfter "${timeformat}") + +if (plAAfter GREATER plABefore) + message(SEND_ERROR "file (${plAFile}) should not have changed!") +endif() +if (plBAfter GREATER plBBefore) + message(SEND_ERROR "file (${plBFile}) should not have changed!") +endif() +if (NOT plCAfter GREATER plCBefore) + message(SEND_ERROR "file (${plCFile}) should have changed!") +endif() +if (NOT plDAfter GREATER plDBefore) + message(SEND_ERROR "file (${plDFile}) should have changed!") +endif() +if (NOT plEAfter GREATER plEBefore) + # There's a bug in Ninja on Windows + # https://gitlab.kitware.com/cmake/cmake/issues/16776 + if(NOT ("${CMAKE_GENERATOR}" MATCHES "Ninja")) + message(SEND_ERROR "file (${plEFile}) should have changed!") + endif() +endif() + +# - Ensure that the timestamp will change. +# - Change the json files referenced by A_CUSTOM_MACRO +# - Rebuild +file(TIMESTAMP "${plCFile}" plCBefore "${timeformat}") +file(TIMESTAMP "${plDFile}" plDBefore "${timeformat}") +file(TIMESTAMP "${plEFile}" plEBefore "${timeformat}") + +execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) +configure_file("${mocPlugSrcDir}/jsonIn/StyleE.json" "${mocPlugBinDir}/jsonFiles/StyleC_Custom.json") +configure_file("${mocPlugSrcDir}/jsonIn/StyleC.json" "${mocPlugBinDir}/jsonFiles/sub/StyleD_Custom.json") +configure_file("${mocPlugSrcDir}/jsonIn/StyleD.json" "${mocPlugBinDir}/jsonFiles/StyleE_Custom.json") +execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocPlugBinDir}") + +file(TIMESTAMP "${plCFile}" plCAfter "${timeformat}") +file(TIMESTAMP "${plDFile}" plDAfter "${timeformat}") +file(TIMESTAMP "${plEFile}" plEAfter "${timeformat}") + +if (NOT plCAfter GREATER plCBefore) + message(SEND_ERROR "file (${plCFile}) should have changed!") +endif() +if (NOT plDAfter GREATER plDBefore) + message(SEND_ERROR "file (${plDFile}) should have changed!") +endif() +if (NOT plEAfter GREATER plEBefore) + # There's a bug in Ninja on Windows + # https://gitlab.kitware.com/cmake/cmake/issues/16776 + if(NOT ("${CMAKE_GENERATOR}" MATCHES "Ninja")) + message(SEND_ERROR "file (${plEFile}) should have changed!") + endif() +endif() diff --git a/Tests/QtAutogenRerun/mocPlugin/CMakeLists.txt b/Tests/QtAutogenRerun/mocPlugin/CMakeLists.txt index 9b224fb..b7cc5e9 100644 --- a/Tests/QtAutogenRerun/mocPlugin/CMakeLists.txt +++ b/Tests/QtAutogenRerun/mocPlugin/CMakeLists.txt @@ -16,9 +16,11 @@ if(Qt5_POSITION_INDEPENDENT_CODE AND CMAKE_CXX_COMPILE_OPTIONS_PIC) endif() configure_file(jsonIn/StyleC.json jsonFiles/StyleC.json) -configure_file(jsonIn/StyleD.json jsonFiles/sub/StyleD.json) configure_file(jsonIn/StyleC.json jsonFiles/StyleC_Custom.json) +configure_file(jsonIn/StyleD.json jsonFiles/sub/StyleD.json) configure_file(jsonIn/StyleD.json jsonFiles/sub/StyleD_Custom.json) +configure_file(jsonIn/StyleE.json jsonFiles/StyleE.json) +configure_file(jsonIn/StyleE.json jsonFiles/StyleE_Custom.json) # Enable automoc set(CMAKE_AUTOMOC TRUE) diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleA.hpp b/Tests/QtAutogenRerun/mocPlugin/StyleA.hpp index 1b6154d..35158a4 100644 --- a/Tests/QtAutogenRerun/mocPlugin/StyleA.hpp +++ b/Tests/QtAutogenRerun/mocPlugin/StyleA.hpp @@ -1,13 +1,13 @@ #ifndef STYLEA_HPP #define STYLEA_HPP -#include "StyleCommon.hpp" +#include "UtilityMacros.hpp" #include <QStylePlugin> class StyleA : public QStylePlugin { Q_OBJECT - // Json file in local directory + // Json file in source local directory Q_PLUGIN_METADATA(IID "org.styles.A" FILE "StyleA.json") A_CUSTOM_MACRO(SomeArg, "StyleA_Custom.json", AnotherArg) public: diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleB.hpp b/Tests/QtAutogenRerun/mocPlugin/StyleB.hpp index 163c9b2..15b79c5 100644 --- a/Tests/QtAutogenRerun/mocPlugin/StyleB.hpp +++ b/Tests/QtAutogenRerun/mocPlugin/StyleB.hpp @@ -1,13 +1,13 @@ #ifndef STYLEB_HPP #define STYLEB_HPP -#include "StyleCommon.hpp" +#include "UtilityMacros.hpp" #include <QStylePlugin> class StyleB : public QStylePlugin { Q_OBJECT - // Json file in local subdirectory + // Json file in source local subdirectory Q_PLUGIN_METADATA(IID "org.styles.B" FILE "jsonIn/StyleB.json") A_CUSTOM_MACRO(SomeArg, "jsonIn/StyleB_Custom.json", AnotherArg) public: diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleC.hpp b/Tests/QtAutogenRerun/mocPlugin/StyleC.hpp index 52a887a..b0a4115 100644 --- a/Tests/QtAutogenRerun/mocPlugin/StyleC.hpp +++ b/Tests/QtAutogenRerun/mocPlugin/StyleC.hpp @@ -1,7 +1,7 @@ #ifndef STYLEC_HPP #define STYLEC_HPP -#include "StyleCommon.hpp" +#include "UtilityMacros.hpp" #include <QStylePlugin> class StyleC : public QStylePlugin diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleD.hpp b/Tests/QtAutogenRerun/mocPlugin/StyleD.hpp index df8a439..9696aaa 100644 --- a/Tests/QtAutogenRerun/mocPlugin/StyleD.hpp +++ b/Tests/QtAutogenRerun/mocPlugin/StyleD.hpp @@ -1,7 +1,7 @@ #ifndef STYLED_HPP #define STYLED_HPP -#include "StyleCommon.hpp" +#include "UtilityMacros.hpp" #include <QStylePlugin> class StyleD : public QStylePlugin diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleE.cpp b/Tests/QtAutogenRerun/mocPlugin/StyleE.cpp index 8fc9a7f..3448319 100644 --- a/Tests/QtAutogenRerun/mocPlugin/StyleE.cpp +++ b/Tests/QtAutogenRerun/mocPlugin/StyleE.cpp @@ -4,3 +4,6 @@ QStyle* StyleE::create(const QString& key) { return 0; } + +// AUTOMOC the StyleEInclude.hpp header +#include "moc_StyleEInclude.cpp" diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleE.hpp b/Tests/QtAutogenRerun/mocPlugin/StyleE.hpp index e7915a8..a069034 100644 --- a/Tests/QtAutogenRerun/mocPlugin/StyleE.hpp +++ b/Tests/QtAutogenRerun/mocPlugin/StyleE.hpp @@ -1,17 +1,10 @@ #ifndef STYLEE_HPP #define STYLEE_HPP -#include "StyleCommon.hpp" -#include <QStylePlugin> - -class StyleE : public QStylePlugin -{ - Q_OBJECT - // No Json file - Q_PLUGIN_METADATA(IID "org.styles.E") - A_CUSTOM_MACRO(SomeArg, InvalidFileArg, AnotherArg) -public: - QStyle* create(const QString& key); -}; +// The included file is not in the sources list and won't be detected by +// AUTOMOC source file with the same base name. +// It is registered to AUTOMOCed via a moc_<NAME>.cpp include in StyleE.cpp +// though. +#include "StyleEInclude.hpp" #endif diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleEInclude.hpp b/Tests/QtAutogenRerun/mocPlugin/StyleEInclude.hpp new file mode 100644 index 0000000..f9734db --- /dev/null +++ b/Tests/QtAutogenRerun/mocPlugin/StyleEInclude.hpp @@ -0,0 +1,17 @@ +#ifndef STYLEE_INCLUDE_HPP +#define STYLEE_INCLUDE_HPP + +#include "UtilityMacros.hpp" +#include <QStylePlugin> + +class StyleE : public QStylePlugin +{ + Q_OBJECT + // Json files in global root directory + Q_PLUGIN_METADATA(IID "org.styles.E" FILE "StyleE.json") + A_CUSTOM_MACRO(SomeArg, "StyleE_Custom.json", AnotherArg) +public: + QStyle* create(const QString& key); +}; + +#endif diff --git a/Tests/QtAutogenRerun/mocPlugin/StyleCommon.hpp b/Tests/QtAutogenRerun/mocPlugin/UtilityMacros.hpp index f1a7ec6..53a4284 100644 --- a/Tests/QtAutogenRerun/mocPlugin/StyleCommon.hpp +++ b/Tests/QtAutogenRerun/mocPlugin/UtilityMacros.hpp @@ -1,5 +1,5 @@ -#ifndef STYLECOMMON_HPP -#define STYLECOMMON_HPP +#ifndef UTILITYMACROS_HPP +#define UTILITYMACROS_HPP // Empty test macro definition #define A_CUSTOM_MACRO(name, jsonFile, pluginRegistrations) diff --git a/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleB.json b/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleB.json index 129cac4..cd155dc 100644 --- a/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleB.json +++ b/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleB.json @@ -1 +1 @@ -{ "Keys": [ "Rocket", "StarbusterB" ] } +{ "Keys": [ "Red", "Green" ] } diff --git a/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleE.json b/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleE.json new file mode 100644 index 0000000..5412c94 --- /dev/null +++ b/Tests/QtAutogenRerun/mocPlugin/jsonIn/StyleE.json @@ -0,0 +1 @@ +{ "Keys": [ "Floor", "Ceiling" ] } diff --git a/Tests/QtAutogenRerun/mocRerun.cmake b/Tests/QtAutogenRerun/mocRerun.cmake new file mode 100644 index 0000000..a92912b --- /dev/null +++ b/Tests/QtAutogenRerun/mocRerun.cmake @@ -0,0 +1,60 @@ + +set(timeformat "%Y%j%H%M%S") +set(mocRerunSrcDir "${CMAKE_CURRENT_SOURCE_DIR}/mocRerun") +set(mocRerunBinDir "${CMAKE_CURRENT_BINARY_DIR}/mocRerun") + +# Initial build +configure_file("${mocRerunSrcDir}/test1a.h.in" "${mocRerunBinDir}/test1.h" COPYONLY) +try_compile(MOC_RERUN + "${mocRerunBinDir}" + "${mocRerunSrcDir}" + mocRerun + CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}" + "-DQT_TEST_VERSION=${QT_TEST_VERSION}" + "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}" + OUTPUT_VARIABLE output +) +if (NOT MOC_RERUN) + message(SEND_ERROR "Initial build of mocRerun failed. Output: ${output}") +endif() +# Get name of the output binary +file(STRINGS "${mocRerunBinDir}/mocRerun.txt" mocRerunList ENCODING UTF-8) +list(GET mocRerunList 0 mocRerunBin) + +message("Changing the header content for a MOC rerun") +# - Acquire binary timestamps before the build +file(TIMESTAMP "${mocRerunBin}" timeBefore "${timeformat}") +# - Ensure that the timestamp will change +# - Change header file content and rebuild +# - Rebuild +execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) +configure_file("${mocRerunSrcDir}/test1b.h.in" "${mocRerunBinDir}/test1.h" COPYONLY) +execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocRerunBinDir}" RESULT_VARIABLE result ) +if (result) + message(SEND_ERROR "Second build of mocRerun failed.") +endif() +# - Acquire binary timestamps after the build +file(TIMESTAMP "${mocRerunBin}" timeAfter "${timeformat}") +# - Test if timestamps changed +if (NOT timeAfter GREATER timeBefore) + message(SEND_ERROR "File (${mocRerunBin}) should have changed!") +endif() + + +message("Changing nothing for a MOC rerun") +# - Acquire binary timestamps before the build +file(TIMESTAMP "${mocRerunBin}" timeBefore "${timeformat}") +# - Ensure that the timestamp would change +# - Change nothing +# - Rebuild +execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) +execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${mocRerunBinDir}" RESULT_VARIABLE result ) +if (result) + message(SEND_ERROR "Third build of mocRerun failed.") +endif() +# - Acquire binary timestamps after the build +file(TIMESTAMP "${mocRerunBin}" timeAfter "${timeformat}") +# - Test if timestamps changed +if (timeAfter GREATER timeBefore) + message(SEND_ERROR "File (${mocRerunBin}) should not have changed!") +endif() diff --git a/Tests/QtAutogenRerun/mocRerun/CMakeLists.txt b/Tests/QtAutogenRerun/mocRerun/CMakeLists.txt index 7380bdd..bafd9cf 100644 --- a/Tests/QtAutogenRerun/mocRerun/CMakeLists.txt +++ b/Tests/QtAutogenRerun/mocRerun/CMakeLists.txt @@ -19,17 +19,15 @@ set(CMAKE_AUTORCC ON) # Generated source file add_custom_command(OUTPUT main.cpp - COMMAND ${CMAKE_COMMAND} -E sleep 2 - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/main.cpp - ) + COMMAND ${CMAKE_COMMAND} -E sleep 2 + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/main.cpp) add_executable(mocRerun ${CMAKE_CURRENT_BINARY_DIR}/test1.h ${CMAKE_CURRENT_BINARY_DIR}/main.cpp - res1.qrc - ) + res1.qrc) target_include_directories(mocRerun PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(mocRerun ${QT_CORE_TARGET}) # Write target name to text file add_custom_command(TARGET mocRerun POST_BUILD COMMAND - ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:mocRerun>" > target1.txt) + ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:mocRerun>" > mocRerun.txt) diff --git a/Tests/QtAutogenRerun/rccDepends.cmake b/Tests/QtAutogenRerun/rccDepends.cmake new file mode 100644 index 0000000..68e1482 --- /dev/null +++ b/Tests/QtAutogenRerun/rccDepends.cmake @@ -0,0 +1,131 @@ +# When a .qrc or a file listed in a .qrc file changes, +# the target must be rebuilt +set(timeformat "%Y%j%H%M%S") +set(rccDepSD "${CMAKE_CURRENT_SOURCE_DIR}/rccDepends") +set(rccDepBD "${CMAKE_CURRENT_BINARY_DIR}/rccDepends") + +# Initial build +configure_file(${rccDepSD}/resPlainA.qrc.in ${rccDepBD}/resPlain.qrc COPYONLY) +configure_file(${rccDepSD}/resGenA.qrc.in ${rccDepBD}/resGen.qrc.in COPYONLY) +try_compile(RCC_DEPENDS + "${rccDepBD}" + "${rccDepSD}" + rccDepends + CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}" + "-DQT_TEST_VERSION=${QT_TEST_VERSION}" + "-DCMAKE_PREFIX_PATH=${Qt_PREFIX_DIR}" + OUTPUT_VARIABLE output +) +if (NOT RCC_DEPENDS) + message(SEND_ERROR "Initial build of rccDepends failed. Output: ${output}") +endif() + +# Get name of the output binaries +file(STRINGS "${rccDepBD}/targetPlain.txt" targetListPlain ENCODING UTF-8) +file(STRINGS "${rccDepBD}/targetGen.txt" targetListGen ENCODING UTF-8) +list(GET targetListPlain 0 rccDepBinPlain) +list(GET targetListGen 0 rccDepBinGen) +message("Target that uses a plain .qrc file is:\n ${rccDepBinPlain}") +message("Target that uses a GENERATED .qrc file is:\n ${rccDepBinGen}") + + +message("Changing a resource files listed in the .qrc file") +# - Acquire binary timestamps before the build +file(TIMESTAMP "${rccDepBinPlain}" rdPlainBefore "${timeformat}") +file(TIMESTAMP "${rccDepBinGen}" rdGenBefore "${timeformat}") +# - Ensure that the timestamp will change +# - Change a resource files listed in the .qrc file +# - Rebuild +execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) +execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${rccDepBD}/resPlain/input.txt") +execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${rccDepBD}/resGen/input.txt") +execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${rccDepBD}" RESULT_VARIABLE result) +if (result) + message(SEND_ERROR "Second build of rccDepends failed.") +endif() +# - Acquire binary timestamps after the build +file(TIMESTAMP "${rccDepBinPlain}" rdPlainAfter "${timeformat}") +file(TIMESTAMP "${rccDepBinGen}" rdGenAfter "${timeformat}") +# - Test if timestamps changed +if (NOT rdPlainAfter GREATER rdPlainBefore) + message(SEND_ERROR "Plain .qrc binary ${rccDepBinPlain}) should have changed!") +endif() +if (NOT rdGenAfter GREATER rdGenBefore) + message(SEND_ERROR "GENERATED .qrc binary ${rccDepBinGen} should have changed!") +endif() + + +message("Changing a the .qrc file") +# - Acquire binary timestamps before the build +file(TIMESTAMP "${rccDepBinPlain}" rdPlainBefore "${timeformat}") +file(TIMESTAMP "${rccDepBinGen}" rdGenBefore "${timeformat}") +# - Ensure that the timestamp will change +# - Change the .qrc file +# - Rebuild +execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) +configure_file(${rccDepSD}/resPlainB.qrc.in ${rccDepBD}/resPlain.qrc COPYONLY) +configure_file(${rccDepSD}/resGenB.qrc.in ${rccDepBD}/resGen.qrc.in COPYONLY) +execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${rccDepBD}" RESULT_VARIABLE result) +if (result) + message(SEND_ERROR "Third build of rccDepends failed.") +endif() +# - Acquire binary timestamps after the build +file(TIMESTAMP "${rccDepBinPlain}" rdPlainAfter "${timeformat}") +file(TIMESTAMP "${rccDepBinGen}" rdGenAfter "${timeformat}") +# - Test if timestamps changed +if (NOT rdPlainAfter GREATER rdPlainBefore) + message(SEND_ERROR "Plain .qrc binary ${rccDepBinPlain}) should have changed!") +endif() +if (NOT rdGenAfter GREATER rdGenBefore) + message(SEND_ERROR "GENERATED .qrc binary ${rccDepBinGen} should have changed!") +endif() + + +message("Changing a newly added resource files listed in the .qrc file") +# - Acquire binary timestamps before the build +file(TIMESTAMP "${rccDepBinPlain}" rdPlainBefore "${timeformat}") +file(TIMESTAMP "${rccDepBinGen}" rdGenBefore "${timeformat}") +# - Ensure that the timestamp will change +# - Change a newly added resource files listed in the .qrc file +# - Rebuild +execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) +execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${rccDepBD}/resPlain/inputAdded.txt") +execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${rccDepBD}/resGen/inputAdded.txt") +execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${rccDepBD}" RESULT_VARIABLE result) +if (result) + message(SEND_ERROR "Fourth build of rccDepends failed.") +endif() +# - Acquire binary timestamps after the build +file(TIMESTAMP "${rccDepBinPlain}" rdPlainAfter "${timeformat}") +file(TIMESTAMP "${rccDepBinGen}" rdGenAfter "${timeformat}") +# - Test if timestamps changed +if (NOT rdPlainAfter GREATER rdPlainBefore) + message(SEND_ERROR "Plain .qrc binary ${rccDepBinPlain}) should have changed!") +endif() +if (NOT rdGenAfter GREATER rdGenBefore) + message(SEND_ERROR "GENERATED .qrc binary ${rccDepBinGen} should have changed!") +endif() + + +message("Changing nothing in the .qrc file") +# - Acquire binary timestamps before the build +file(TIMESTAMP "${rccDepBinPlain}" rdPlainBefore "${timeformat}") +file(TIMESTAMP "${rccDepBinGen}" rdGenBefore "${timeformat}") +# - Ensure that the timestamp will change +# - Change nothing +# - Rebuild +execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) +execute_process(COMMAND "${CMAKE_COMMAND}" --build . WORKING_DIRECTORY "${rccDepBD}" RESULT_VARIABLE result) +if (result) + message(SEND_ERROR "Fifth build of rccDepends failed.") +endif() +# - Acquire binary timestamps after the build +file(TIMESTAMP "${rccDepBinPlain}" rdPlainAfter "${timeformat}") +file(TIMESTAMP "${rccDepBinGen}" rdGenAfter "${timeformat}") +# - Test if timestamps changed +if (rdPlainAfter GREATER rdPlainBefore) + message(SEND_ERROR "Plain .qrc binary ${rccDepBinPlain}) should NOT have changed!") +endif() +if (rdGenAfter GREATER rdGenBefore) + message(SEND_ERROR "GENERATED .qrc binary ${rccDepBinGen} should NOT have changed!") +endif() diff --git a/Tests/QtAutogenRerun/rccDepends/CMakeLists.txt b/Tests/QtAutogenRerun/rccDepends/CMakeLists.txt index edc0ac3..291592e 100644 --- a/Tests/QtAutogenRerun/rccDepends/CMakeLists.txt +++ b/Tests/QtAutogenRerun/rccDepends/CMakeLists.txt @@ -1,8 +1,6 @@ cmake_minimum_required(VERSION 3.9) project(rccDepends CXX) -set(CMAKE_AUTORCC ON) - if (QT_TEST_VERSION STREQUAL 4) find_package(Qt4 REQUIRED) set(QT_CORE_TARGET Qt4::QtCore) @@ -15,21 +13,29 @@ else() set(QT_CORE_TARGET Qt5::Core) endif() -configure_file(res/input1.txt.in res1/input.txt COPYONLY) -configure_file(res/input1.txt.in res1/inputAdded.txt COPYONLY) -configure_file(res/input2.txt.in res2/input.txt COPYONLY) -configure_file(res/input2.txt.in res2/inputAdded.txt COPYONLY) -# Dependency generated qrc file -add_custom_command(OUTPUT res2.qrc - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/res2.qrc.in - COMMAND ${CMAKE_COMMAND} -E sleep 2 - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/res2.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/res2.qrc - ) +# Enable AUTORCC for all targets +set(CMAKE_AUTORCC ON) + +# Initial resource files setup +configure_file(resPlain/input.txt.in resPlain/input.txt COPYONLY) +configure_file(resPlain/input.txt.in resPlain/inputAdded.txt COPYONLY) +configure_file(resGen/input.txt.in resGen/input.txt COPYONLY) +configure_file(resGen/input.txt.in resGen/inputAdded.txt COPYONLY) + +# Generated qrc file with dependency +add_custom_command(OUTPUT resGen.qrc + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/resGen.qrc.in + COMMAND ${CMAKE_COMMAND} -E sleep 2 + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/resGen.qrc.in ${CMAKE_CURRENT_BINARY_DIR}/resGen.qrc) + +# Target that uses a plain .qrc file +add_executable(rccDependsPlain main.cpp ${CMAKE_CURRENT_BINARY_DIR}/resPlain.qrc) +target_link_libraries(rccDependsPlain ${QT_CORE_TARGET}) +add_custom_command(TARGET rccDependsPlain POST_BUILD COMMAND + ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:rccDependsPlain>" > targetPlain.txt) -add_executable(rccDepends - main.cpp - ${CMAKE_CURRENT_BINARY_DIR}/res1.qrc - ${CMAKE_CURRENT_BINARY_DIR}/res2.qrc ) -target_link_libraries(rccDepends ${QT_CORE_TARGET}) -add_custom_command(TARGET rccDepends POST_BUILD COMMAND - ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:rccDepends>" > target.txt) +# Target that uses a GENERATED .qrc file +add_executable(rccDependsGen main.cpp ${CMAKE_CURRENT_BINARY_DIR}/resGen.qrc ) +target_link_libraries(rccDependsGen ${QT_CORE_TARGET}) +add_custom_command(TARGET rccDependsGen POST_BUILD COMMAND + ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:rccDependsGen>" > targetGen.txt) diff --git a/Tests/QtAutogenRerun/rccDepends/res/input1.txt.in b/Tests/QtAutogenRerun/rccDepends/res/input1.txt.in deleted file mode 100644 index da62762..0000000 --- a/Tests/QtAutogenRerun/rccDepends/res/input1.txt.in +++ /dev/null @@ -1 +0,0 @@ -Res1 input. diff --git a/Tests/QtAutogenRerun/rccDepends/res/input2.txt.in b/Tests/QtAutogenRerun/rccDepends/res/input2.txt.in deleted file mode 100644 index 08e14b7..0000000 --- a/Tests/QtAutogenRerun/rccDepends/res/input2.txt.in +++ /dev/null @@ -1 +0,0 @@ -Res2 input. diff --git a/Tests/QtAutogenRerun/rccDepends/res1a.qrc.in b/Tests/QtAutogenRerun/rccDepends/res1a.qrc.in deleted file mode 100644 index d111ffb..0000000 --- a/Tests/QtAutogenRerun/rccDepends/res1a.qrc.in +++ /dev/null @@ -1,5 +0,0 @@ -<RCC> - <qresource prefix="/Texts1"> - <file>res1/input.txt</file> - </qresource> -</RCC> diff --git a/Tests/QtAutogenRerun/rccDepends/res1b.qrc.in b/Tests/QtAutogenRerun/rccDepends/res1b.qrc.in deleted file mode 100644 index 4cb3f04..0000000 --- a/Tests/QtAutogenRerun/rccDepends/res1b.qrc.in +++ /dev/null @@ -1,6 +0,0 @@ -<RCC> - <qresource prefix="/Texts1"> - <file>res1/input.txt</file> - <file alias="Added">res1/inputAdded.txt</file> - </qresource> -</RCC> diff --git a/Tests/QtAutogenRerun/rccDepends/res2a.qrc.in b/Tests/QtAutogenRerun/rccDepends/res2a.qrc.in deleted file mode 100644 index 19f34ac..0000000 --- a/Tests/QtAutogenRerun/rccDepends/res2a.qrc.in +++ /dev/null @@ -1,5 +0,0 @@ -<RCC> - <qresource prefix="/Texts2"> - <file>res2/input.txt</file> - </qresource> -</RCC> diff --git a/Tests/QtAutogenRerun/rccDepends/res2b.qrc.in b/Tests/QtAutogenRerun/rccDepends/res2b.qrc.in deleted file mode 100644 index 19e8ba1..0000000 --- a/Tests/QtAutogenRerun/rccDepends/res2b.qrc.in +++ /dev/null @@ -1,6 +0,0 @@ -<RCC> - <qresource prefix="/Texts2"> - <file>res2/input.txt</file> - <file alias="Added">res2/inputAdded.txt</file> - </qresource> -</RCC> diff --git a/Tests/QtAutogenRerun/rccDepends/resGen/input.txt.in b/Tests/QtAutogenRerun/rccDepends/resGen/input.txt.in new file mode 100644 index 0000000..4f24589 --- /dev/null +++ b/Tests/QtAutogenRerun/rccDepends/resGen/input.txt.in @@ -0,0 +1 @@ +Generated resource input. diff --git a/Tests/QtAutogenRerun/rccDepends/resGen/inputAdded.txt.in b/Tests/QtAutogenRerun/rccDepends/resGen/inputAdded.txt.in new file mode 100644 index 0000000..4f24589 --- /dev/null +++ b/Tests/QtAutogenRerun/rccDepends/resGen/inputAdded.txt.in @@ -0,0 +1 @@ +Generated resource input. diff --git a/Tests/QtAutogenRerun/rccDepends/resGenA.qrc.in b/Tests/QtAutogenRerun/rccDepends/resGenA.qrc.in new file mode 100644 index 0000000..c131a34 --- /dev/null +++ b/Tests/QtAutogenRerun/rccDepends/resGenA.qrc.in @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/TextsGenerated"> + <file>resGen/input.txt</file> + </qresource> +</RCC> diff --git a/Tests/QtAutogenRerun/rccDepends/resGenB.qrc.in b/Tests/QtAutogenRerun/rccDepends/resGenB.qrc.in new file mode 100644 index 0000000..8c7e643 --- /dev/null +++ b/Tests/QtAutogenRerun/rccDepends/resGenB.qrc.in @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/TextsGenerated"> + <file>resGen/input.txt</file> + <file alias="Added">resGen/inputAdded.txt</file> + </qresource> +</RCC> diff --git a/Tests/QtAutogenRerun/rccDepends/resPlain/input.txt.in b/Tests/QtAutogenRerun/rccDepends/resPlain/input.txt.in new file mode 100644 index 0000000..a5e407a --- /dev/null +++ b/Tests/QtAutogenRerun/rccDepends/resPlain/input.txt.in @@ -0,0 +1 @@ +Plaint resource input. diff --git a/Tests/QtAutogenRerun/rccDepends/resPlain/inputAdded.txt.in b/Tests/QtAutogenRerun/rccDepends/resPlain/inputAdded.txt.in new file mode 100644 index 0000000..a5e407a --- /dev/null +++ b/Tests/QtAutogenRerun/rccDepends/resPlain/inputAdded.txt.in @@ -0,0 +1 @@ +Plaint resource input. diff --git a/Tests/QtAutogenRerun/rccDepends/resPlainA.qrc.in b/Tests/QtAutogenRerun/rccDepends/resPlainA.qrc.in new file mode 100644 index 0000000..c135d85 --- /dev/null +++ b/Tests/QtAutogenRerun/rccDepends/resPlainA.qrc.in @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/TextsPlain"> + <file>resPlain/input.txt</file> + </qresource> +</RCC> diff --git a/Tests/QtAutogenRerun/rccDepends/resPlainB.qrc.in b/Tests/QtAutogenRerun/rccDepends/resPlainB.qrc.in new file mode 100644 index 0000000..186b653 --- /dev/null +++ b/Tests/QtAutogenRerun/rccDepends/resPlainB.qrc.in @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/TextsPlain"> + <file>resPlain/input.txt</file> + <file alias="Added">resPlain/inputAdded.txt</file> + </qresource> +</RCC> diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 73fa8fb..aa4c5ac 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -227,20 +227,38 @@ add_RunCMake_test(separate_arguments) add_RunCMake_test(set_property) add_RunCMake_test(string) add_RunCMake_test(test_include_dirs) -foreach(var - CMAKE_C_COMPILER_ID - CMAKE_C_COMPILER_VERSION - CMAKE_C_STANDARD_DEFAULT - CMAKE_CXX_COMPILER_ID - CMAKE_CXX_COMPILER_VERSION - CMAKE_CXX_STANDARD_DEFAULT - CMake_TEST_CUDA - ) - if(DEFINED ${var}) - list(APPEND try_compile_ARGS -D${var}=${${var}}) + +function(add_RunCMake_test_try_compile) + if(CMAKE_VERSION VERSION_LESS 3.9.20170907 AND "x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") + # Older CMake versions do not know about MSVC language standards. + # Approximate our logic from MSVC-CXX.cmake. + if ((CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.0.24215.1 AND + CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10) OR + CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.10.25017) + set(CMAKE_CXX_STANDARD_DEFAULT 14) + elseif (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 16.0) + set(CMAKE_CXX_STANDARD_DEFAULT "") + else() + unset(CMAKE_CXX_STANDARD_DEFAULT) + endif() endif() -endforeach() -add_RunCMake_test(try_compile) + foreach(var + CMAKE_C_COMPILER_ID + CMAKE_C_COMPILER_VERSION + CMAKE_C_STANDARD_DEFAULT + CMAKE_CXX_COMPILER_ID + CMAKE_CXX_COMPILER_VERSION + CMAKE_CXX_STANDARD_DEFAULT + CMake_TEST_CUDA + ) + if(DEFINED ${var}) + list(APPEND try_compile_ARGS -D${var}=${${var}}) + endif() + endforeach() + add_RunCMake_test(try_compile) +endfunction() +add_RunCMake_test_try_compile() + add_RunCMake_test(try_run) add_RunCMake_test(set) add_RunCMake_test(variable_watch) diff --git a/Tests/RunCMake/CPack/DEB/Helpers.cmake b/Tests/RunCMake/CPack/DEB/Helpers.cmake index ad1b47b..6d8e84a 100644 --- a/Tests/RunCMake/CPack/DEB/Helpers.cmake +++ b/Tests/RunCMake/CPack/DEB/Helpers.cmake @@ -23,7 +23,7 @@ function(getPackageNameGlobexpr NAME COMPONENT VERSION REVISION FILE_NO RESULT_V endif() if(GENERATOR_SPECIFIC_FORMAT) - set(${RESULT_VAR} "${NAME}${COMPONENT}_${VERSION}-${REVISION}_*.deb" PARENT_SCOPE) + set(${RESULT_VAR} "${NAME}${COMPONENT}_${VERSION}_*.deb" PARENT_SCOPE) else() set(${RESULT_VAR} "${NAME}-${VERSION}-*${COMPONENT}.deb" PARENT_SCOPE) endif() diff --git a/Tests/RunCMake/CPack/RPM/Helpers.cmake b/Tests/RunCMake/CPack/RPM/Helpers.cmake index d8012b1..88fc231 100644 --- a/Tests/RunCMake/CPack/RPM/Helpers.cmake +++ b/Tests/RunCMake/CPack/RPM/Helpers.cmake @@ -23,6 +23,9 @@ function(getPackageNameGlobexpr NAME COMPONENT VERSION REVISION FILE_NO RESULT_V endif() if(GENERATOR_SPECIFIC_FORMAT) + if(NOT REVISION) + set(REVISION "1") + endif() set(${RESULT_VAR} "${NAME}${COMPONENT}-${VERSION}-${REVISION}.*.rpm" PARENT_SCOPE) else() set(${RESULT_VAR} "${NAME}-${VERSION}-*${COMPONENT}.rpm" PARENT_SCOPE) diff --git a/Tests/RunCMake/CPack/RunCMakeTest.cmake b/Tests/RunCMake/CPack/RunCMakeTest.cmake index fb1f476..04ac584 100644 --- a/Tests/RunCMake/CPack/RunCMakeTest.cmake +++ b/Tests/RunCMake/CPack/RunCMakeTest.cmake @@ -10,6 +10,7 @@ run_cpack_test(DEBUGINFO "RPM" true "COMPONENT") run_cpack_test(DEPENDENCIES "RPM;DEB" true "COMPONENT") run_cpack_test(DIST "RPM" false "MONOLITHIC") run_cpack_test(EMPTY_DIR "RPM;DEB;TGZ" true "MONOLITHIC;COMPONENT") +run_cpack_test(VERSION "RPM;DEB" false "MONOLITHIC;COMPONENT") run_cpack_test(EXTRA "DEB" false "COMPONENT") run_cpack_test(GENERATE_SHLIBS "DEB" true "COMPONENT") run_cpack_test(GENERATE_SHLIBS_LDCONFIG "DEB" true "COMPONENT") diff --git a/Tests/RunCMake/CPack/VerifyResult.cmake b/Tests/RunCMake/CPack/VerifyResult.cmake index 470ebf7..1f5ab87 100644 --- a/Tests/RunCMake/CPack/VerifyResult.cmake +++ b/Tests/RunCMake/CPack/VerifyResult.cmake @@ -8,9 +8,7 @@ function(findExpectedFile FILE_NO RESULT_VAR GLOBING_EXPR_VAR) endif() if(NOT DEFINED EXPECTED_FILE_${FILE_NO}_VERSION) set(EXPECTED_FILE_${FILE_NO}_VERSION "0.1.1") - endif() - if(NOT DEFINED EXPECTED_FILE_${FILE_NO}_REVISION) - set(EXPECTED_FILE_${FILE_NO}_REVISION "1") + set(EXPECTED_FILE_${FILE_NO}_VERSION "0.1.1" PARENT_SCOPE) endif() getPackageNameGlobexpr("${EXPECTED_FILE_${FILE_NO}_NAME}" diff --git a/Tests/RunCMake/CPack/tests/VERSION/ExpectedFiles.cmake b/Tests/RunCMake/CPack/tests/VERSION/ExpectedFiles.cmake new file mode 100644 index 0000000..85c571c --- /dev/null +++ b/Tests/RunCMake/CPack/tests/VERSION/ExpectedFiles.cmake @@ -0,0 +1,3 @@ +set(EXPECTED_FILES_COUNT "1") +set(EXPECTED_FILE_CONTENT_1_LIST "/usr;/usr/foo;/usr/foo/CMakeLists.txt") +set(EXPECTED_FILE_1_REVISION "1") diff --git a/Tests/RunCMake/CPack/tests/VERSION/VerifyResult.cmake b/Tests/RunCMake/CPack/tests/VERSION/VerifyResult.cmake new file mode 100644 index 0000000..eed9696 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/VERSION/VerifyResult.cmake @@ -0,0 +1,17 @@ +function(checkPackageInfo_ TYPE FILE REGEX) + getPackageInfo("${FILE}" "FILE_INFO_") + if(NOT FILE_INFO_ MATCHES "${REGEX}") + message(FATAL_ERROR "Unexpected ${TYPE} in '${FILE}' ${EXPECTED_FILE_1_VERSION} ${EXPECTED_FILE_1_REVISION}; file info: '${FILE_INFO_}'") + endif() +endfunction() + +set(whitespaces_ "[\t\n\r ]*") + +if(GENERATOR_TYPE STREQUAL "RPM") + checkPackageInfo_("package version" "${FOUND_FILE_1}" "Version${whitespaces_}:${whitespaces_}${EXPECTED_FILE_1_VERSION}") + checkPackageInfo_("package revision" "${FOUND_FILE_1}" "Release${whitespaces_}:${whitespaces_}${EXPECTED_FILE_1_REVISION}") + checkPackageInfo_("epoch version" "${FOUND_FILE_1}" "Epoch${whitespaces_}:${whitespaces_}3") +else() # DEB + checkPackageInfo_("version" "${FOUND_FILE_1}" + ".*Version${whitespaces_}:${whitespaces_}3:${EXPECTED_FILE_1_VERSION}-${EXPECTED_FILE_1_REVISION}") +endif() diff --git a/Tests/RunCMake/CPack/tests/VERSION/test.cmake b/Tests/RunCMake/CPack/tests/VERSION/test.cmake new file mode 100644 index 0000000..301ab61 --- /dev/null +++ b/Tests/RunCMake/CPack/tests/VERSION/test.cmake @@ -0,0 +1,14 @@ +install(FILES CMakeLists.txt DESTINATION foo COMPONENT test) + +if(GENERATOR_TYPE STREQUAL "DEB") + set(package_type_ "DEBIAN") + set(CPACK_DEBIAN_PACKAGE_RELEASE "1") +else() + set(package_type_ "${GENERATOR_TYPE}") +endif() + +set(CPACK_${package_type_}_PACKAGE_EPOCH "3") + +if(PACKAGING_TYPE STREQUAL "COMPONENT") + set(CPACK_COMPONENTS_ALL test) +endif() diff --git a/Tests/RunCMake/variable_watch/ModifyWatchInCallback.cmake b/Tests/RunCMake/variable_watch/ModifyWatchInCallback.cmake new file mode 100644 index 0000000..1dee837 --- /dev/null +++ b/Tests/RunCMake/variable_watch/ModifyWatchInCallback.cmake @@ -0,0 +1,17 @@ +function (watch2) + +endfunction () + +function (watch1) + variable_watch(watched watch2) + variable_watch(watched watch2) + variable_watch(watched watch2) + variable_watch(watched watch2) + variable_watch(watched watch2) + variable_watch(watched watch2) +endfunction () + +variable_watch(watched watch1) +variable_watch(watched watch2) + +set(access "${watched}") diff --git a/Tests/RunCMake/variable_watch/RunCMakeTest.cmake b/Tests/RunCMake/variable_watch/RunCMakeTest.cmake index 9becb4c..2fa6275 100644 --- a/Tests/RunCMake/variable_watch/RunCMakeTest.cmake +++ b/Tests/RunCMake/variable_watch/RunCMakeTest.cmake @@ -3,3 +3,4 @@ include(RunCMake) run_cmake(ModifiedAccess) run_cmake(NoWatcher) run_cmake(WatchTwice) +run_cmake(ModifyWatchInCallback) |