diff options
-rw-r--r-- | Modules/AutogenInfo.cmake.in | 1 | ||||
-rw-r--r-- | Source/cmQtAutoGenerators.cxx | 275 | ||||
-rw-r--r-- | Source/cmQtAutoGenerators.h | 10 | ||||
-rw-r--r-- | Tests/QtAutogen/CMakeLists.txt | 32 | ||||
-rw-r--r-- | Tests/QtAutogen/autorcc_depends/CMakeLists.txt | 24 | ||||
-rw-r--r-- | Tests/QtAutogen/autorcc_depends/res1.qrc | 5 | ||||
-rw-r--r-- | Tests/QtAutogen/autorcc_depends/res1_input.txt | 1 | ||||
-rw-r--r-- | Tests/QtAutogen/autorcc_depends/test_res1.cpp | 5 |
8 files changed, 345 insertions, 8 deletions
diff --git a/Modules/AutogenInfo.cmake.in b/Modules/AutogenInfo.cmake.in index 602b065..7d89420 100644 --- a/Modules/AutogenInfo.cmake.in +++ b/Modules/AutogenInfo.cmake.in @@ -1,5 +1,6 @@ set(AM_SOURCES @_cpp_files@ ) set(AM_RCC_SOURCES @_rcc_files@ ) +set(AM_RCC_INPUTS @_qt_rcc_inputs@) set(AM_SKIP_MOC @_skip_moc@ ) set(AM_SKIP_UIC @_skip_uic@ ) set(AM_HEADERS @_moc_headers@ ) diff --git a/Source/cmQtAutoGenerators.cxx b/Source/cmQtAutoGenerators.cxx index 7b81f4f..929cbc0 100644 --- a/Source/cmQtAutoGenerators.cxx +++ b/Source/cmQtAutoGenerators.cxx @@ -166,6 +166,112 @@ static std::string getAutogenTargetDir(cmTarget const* target) return targetDir; } +std::string cmQtAutoGenerators::ListQt5RccInputs(cmSourceFile* sf, + cmTarget const* target, + std::vector<std::string>& depends) +{ + std::string rccCommand = this->GetRccExecutable(target); + std::vector<std::string> qrcEntries; + + std::vector<std::string> command; + command.push_back(rccCommand); + command.push_back("--list"); + + std::string absFile = cmsys::SystemTools::GetRealPath( + sf->GetFullPath().c_str()); + + command.push_back(absFile); + + std::string output; + int retVal = 0; + bool result = cmSystemTools::RunSingleCommand(command, &output, + &retVal, 0, + cmSystemTools::OUTPUT_NONE); + if (!result || retVal) + { + std::cerr << "AUTOGEN: error: Rcc list process for " << sf->GetFullPath() + << " failed:\n" << output << std::endl; + return std::string(); + } + + std::istringstream ostr(output); + std::string oline; + while(std::getline(ostr, oline)) + { + if (oline.empty()) + { + // The output of rcc --list contains many empty lines. + continue; + } + if (cmHasLiteralPrefix(oline, "RCC: Error in")) + { + static std::string searchString = "Cannot find file '"; + + std::string::size_type pos = oline.find(searchString); + if (pos == std::string::npos) + { + std::cerr << "AUTOGEN: error: Rcc lists unparsable output " + << oline << std::endl; + return std::string(); + } + pos += searchString.length(); + std::string::size_type sz = oline.size() - pos - 1; + qrcEntries.push_back(oline.substr(pos, sz)); + } + else + { + qrcEntries.push_back(oline); + } + } + depends.insert(depends.end(), qrcEntries.begin(), qrcEntries.end()); + std::string entriesList; + const char* sep = ""; + for(std::vector<std::string>::const_iterator it = qrcEntries.begin(); + it != qrcEntries.end(); ++it) + { + entriesList += sep; + entriesList += *it; + sep = "@list_sep@"; + } + return entriesList; +} + +std::string cmQtAutoGenerators::ListQt4RccInputs(cmSourceFile* sf, + std::vector<std::string>& depends) +{ + const std::string qrcContents = ReadAll(sf->GetFullPath()); + + cmsys::RegularExpression fileMatchRegex("(<file[^<]+)"); + + std::string entriesList; + const char* sep = ""; + + size_t offset = 0; + while (fileMatchRegex.find(qrcContents.c_str() + offset)) + { + std::string qrcEntry = fileMatchRegex.match(1); + + offset += qrcEntry.size(); + + cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)"); + fileReplaceRegex.find(qrcEntry); + std::string tag = fileReplaceRegex.match(1); + + qrcEntry = qrcEntry.substr(tag.size()); + + if (!cmSystemTools::FileIsFullPath(qrcEntry.c_str())) + { + qrcEntry = sf->GetLocation().GetDirectory() + "/" + qrcEntry; + } + + entriesList += sep; + entriesList += qrcEntry; + sep = "@list_sep@"; + depends.push_back(qrcEntry); + } + return entriesList; +} + bool cmQtAutoGenerators::InitializeAutogenTarget(cmTarget* target) { cmMakefile* makefile = target->GetMakefile(); @@ -271,6 +377,61 @@ bool cmQtAutoGenerators::InitializeAutogenTarget(cmTarget* target) } } } +#endif + + std::vector<std::string> rcc_output; + if(makefile->GetLocalGenerator()->GetGlobalGenerator()->GetName() == "Ninja" +#if defined(_WIN32) && !defined(__CYGWIN__) + || usePRE_BUILD +#endif + ) + { + std::vector<cmSourceFile*> srcFiles; + target->GetConfigCommonSourceFiles(srcFiles); + for(std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin(); + fileIt != srcFiles.end(); + ++fileIt) + { + cmSourceFile* sf = *fileIt; + std::string absFile = cmsys::SystemTools::GetRealPath( + sf->GetFullPath().c_str()); + + std::string ext = sf->GetExtension(); + + if (target->GetPropertyAsBool("AUTORCC")) + { + if (ext == "qrc" + && !cmSystemTools::IsOn(sf->GetPropertyForUser("SKIP_AUTORCC"))) + { + std::string basename = cmsys::SystemTools:: + GetFilenameWithoutLastExtension(absFile); + + std::string rcc_output_dir = target->GetSupportDirectory(); + cmSystemTools::MakeDirectory(rcc_output_dir.c_str()); + std::string rcc_output_file = rcc_output_dir; + rcc_output_file += "/qrc_" + basename + ".cpp"; + rcc_output.push_back(rcc_output_file); + + if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) + { + if (qtMajorVersion == "5") + { + this->ListQt5RccInputs(sf, target, depends); + } + else + { + this->ListQt4RccInputs(sf, depends); + } +#if defined(_WIN32) && !defined(__CYGWIN__) + usePRE_BUILD = false; +#endif + } + } + } + } + } + +#if defined(_WIN32) && !defined(__CYGWIN__) if(usePRE_BUILD) { // Add the pre-build command directly to bypass the OBJECT_LIBRARY @@ -287,10 +448,29 @@ bool cmQtAutoGenerators::InitializeAutogenTarget(cmTarget* target) else #endif { - cmTarget* autogenTarget = makefile->AddUtilityCommand( + cmTarget* autogenTarget = 0; + if (!rcc_output.empty()) + { + makefile->AddCustomCommandToOutput(rcc_output, depends, "", + commandLines, 0, + workingDirectory.c_str(), + false, false); + + cmCustomCommandLines no_commands; + autogenTarget = makefile->AddUtilityCommand( + autogenTargetName, true, + workingDirectory.c_str(), rcc_output, + no_commands, false, autogenComment.c_str()); + + } + else + { + autogenTarget = makefile->AddUtilityCommand( autogenTargetName, true, workingDirectory.c_str(), depends, commandLines, false, autogenComment.c_str()); + } + // Set target folder const char* autogenFolder = makefile->GetCMakeInstance()->GetProperty( "AUTOMOC_TARGETS_FOLDER"); @@ -418,6 +598,8 @@ void cmQtAutoGenerators::SetupAutoGenerateTarget(cmTarget const* target) inputFile += "/Modules/AutogenInfo.cmake.in"; std::string outputFile = targetDir; outputFile += "/AutogenInfo.cmake"; + makefile->AddDefinition("_qt_rcc_inputs", + makefile->GetDefinition("_qt_rcc_inputs_" + target->GetName())); makefile->ConfigureFile(inputFile.c_str(), outputFile.c_str(), false, true, false); @@ -869,9 +1051,12 @@ void cmQtAutoGenerators::SetupAutoRccTarget(cmTarget const* target) std::vector<cmSourceFile*> srcFiles; target->GetConfigCommonSourceFiles(srcFiles); + std::string qrcInputs; + const char* qrcInputsSep = ""; + std::string rccFileFiles; std::string rccFileOptions; - const char *sep = ""; + const char *optionSep = ""; const char *qtVersion = makefile->GetDefinition("_target_qt_version"); @@ -880,6 +1065,11 @@ void cmQtAutoGenerators::SetupAutoRccTarget(cmTarget const* target) { cmSystemTools::ExpandListArgument(opts, rccOptions); } + std::string qtMajorVersion = makefile->GetSafeDefinition("QT_VERSION_MAJOR"); + if (qtMajorVersion == "") + { + qtMajorVersion = makefile->GetSafeDefinition("Qt5Core_VERSION_MAJOR"); + } for(std::vector<cmSourceFile*>::const_iterator fileIt = srcFiles.begin(); fileIt != srcFiles.end(); @@ -909,9 +1099,9 @@ void cmQtAutoGenerators::SetupAutoRccTarget(cmTarget const* target) if (!rccOptions.empty()) { - rccFileFiles += sep; + rccFileFiles += optionSep; rccFileFiles += absFile; - rccFileOptions += sep; + rccFileOptions += optionSep; } const char *listSep = ""; for(std::vector<std::string>::const_iterator it = rccOptions.begin(); @@ -922,10 +1112,34 @@ void cmQtAutoGenerators::SetupAutoRccTarget(cmTarget const* target) rccFileOptions += *it; listSep = "@list_sep@"; } - sep = ";"; + optionSep = ";"; + + std::vector<std::string> depends; + + std::string entriesList; + if (!cmSystemTools::IsOn(sf->GetPropertyForUser("GENERATED"))) + { + if (qtMajorVersion == "5") + { + entriesList = this->ListQt5RccInputs(sf, target, depends); + } + else + { + entriesList = this->ListQt4RccInputs(sf, depends); + } + if (entriesList.empty()) + { + return; + } + } + qrcInputs += qrcInputsSep; + qrcInputs += entriesList; + qrcInputsSep = ";"; } } } + makefile->AddDefinition("_qt_rcc_inputs_" + target->GetName(), + cmLocalGenerator::EscapeForCMake(qrcInputs).c_str()); makefile->AddDefinition("_rcc_files", cmLocalGenerator::EscapeForCMake(_rcc_files).c_str()); @@ -1153,6 +1367,28 @@ bool cmQtAutoGenerators::ReadAutogenInfoFile(cmMakefile* makefile, cmSystemTools::ReplaceString(*optionIt, "@list_sep@", ";"); this->RccOptions[*fileIt] = *optionIt; } + + const char *rccInputs = makefile->GetSafeDefinition("AM_RCC_INPUTS"); + std::vector<std::string> rccInputLists; + cmSystemTools::ExpandListArgument(rccInputs, rccInputLists); + + if (this->RccSources.size() != rccInputLists.size()) + { + cmSystemTools::Error("Error processing file: ", filename.c_str()); + return false; + } + + for (std::vector<std::string>::iterator fileIt = this->RccSources.begin(), + inputIt = rccInputLists.begin(); + fileIt != this->RccSources.end(); + ++fileIt, ++inputIt) + { + cmSystemTools::ReplaceString(*inputIt, "@list_sep@", ";"); + std::vector<std::string> rccInputFiles; + cmSystemTools::ExpandListArgument(*inputIt, rccInputFiles); + + this->RccInputs[*fileIt] = rccInputFiles; + } } this->CurrentCompileSettingsStr = this->MakeCompileSettingsString(makefile); @@ -2075,6 +2311,25 @@ bool cmQtAutoGenerators::GenerateUi(const std::string& realName, return false; } +bool cmQtAutoGenerators::InputFilesNewerThanQrc(const std::string& qrcFile, + const std::string& rccOutput) +{ + std::vector<std::string> const& files = this->RccInputs[qrcFile]; + for (std::vector<std::string>::const_iterator it = files.begin(); + it != files.end(); ++it) + { + int inputNewerThanQrc = 0; + bool success = cmsys::SystemTools::FileTimeCompare(it->c_str(), + rccOutput.c_str(), + &inputNewerThanQrc); + if (!success || inputNewerThanQrc >= 0) + { + return true; + } + } + return false; +} + bool cmQtAutoGenerators::GenerateQrc() { for(std::vector<std::string>::const_iterator si = this->RccSources.begin(); @@ -2097,10 +2352,14 @@ bool cmQtAutoGenerators::GenerateQrc() + ".dir/qrc_" + basename + ".cpp"; int sourceNewerThanQrc = 0; - bool success = cmsys::SystemTools::FileTimeCompare(*si, - rcc_output_file, + bool generateQrc = !cmsys::SystemTools::FileTimeCompare(*si, + rcc_output_file.c_str(), &sourceNewerThanQrc); - if (this->GenerateAll || !success || sourceNewerThanQrc >= 0) + generateQrc = generateQrc || (sourceNewerThanQrc >= 0); + generateQrc = generateQrc || this->InputFilesNewerThanQrc(*si, + rcc_output_file); + + if (this->GenerateAll || generateQrc) { std::map<std::string, std::string>::const_iterator optionIt = this->RccOptions.find(*si); diff --git a/Source/cmQtAutoGenerators.h b/Source/cmQtAutoGenerators.h index ce27852..79fa5df 100644 --- a/Source/cmQtAutoGenerators.h +++ b/Source/cmQtAutoGenerators.h @@ -88,6 +88,15 @@ private: std::string GetRccExecutable(cmTarget const* target); + std::string ListQt5RccInputs(cmSourceFile* sf, cmTarget const* target, + std::vector<std::string>& depends); + + std::string ListQt4RccInputs(cmSourceFile* sf, + std::vector<std::string>& depends); + + bool InputFilesNewerThanQrc(const std::string& qrcFile, + const std::string& rccOutput); + std::string QtMajorVersion; std::string Sources; std::vector<std::string> RccSources; @@ -118,6 +127,7 @@ private: std::vector<std::string> UicTargetOptions; std::map<std::string, std::string> UicOptions; std::map<std::string, std::string> RccOptions; + std::map<std::string, std::vector<std::string> > RccInputs; bool Verbose; bool ColorOutput; diff --git a/Tests/QtAutogen/CMakeLists.txt b/Tests/QtAutogen/CMakeLists.txt index 3fd00b8..a60b556 100644 --- a/Tests/QtAutogen/CMakeLists.txt +++ b/Tests/QtAutogen/CMakeLists.txt @@ -113,3 +113,35 @@ set_target_properties(no_link_language PROPERTIES AUTOMOC TRUE) qtx_wrap_cpp(uicOnlyMoc sub/uiconly.h) add_executable(uiconly sub/uiconly.cpp ${uicOnlyMoc}) target_link_libraries(uiconly ${QT_LIBRARIES}) + +try_compile(RCC_DEPENDS + "${CMAKE_CURRENT_BINARY_DIR}/autorcc_depends" + "${CMAKE_CURRENT_SOURCE_DIR}/autorcc_depends" + autorcc_depends + CMAKE_FLAGS "-DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE}" "-DQT_TEST_VERSION=${QT_TEST_VERSION}" + OUTPUT_VARIABLE output +) +if (NOT RCC_DEPENDS) + message(SEND_ERROR "Initial build of autorcc_depends failed. Output: ${output}") +endif() + +file(STRINGS "${CMAKE_CURRENT_BINARY_DIR}/autorcc_depends/info_file.txt" qrc_files) + +list(GET qrc_files 0 qrc_file1) + +set(timeformat "%Y%j%H%M%S") + +file(TIMESTAMP "${qrc_file1}" file1_before "${timeformat}") + +execute_process(COMMAND "${CMAKE_COMMAND}" -E sleep 1) # Ensure that the timestamp will change. +execute_process(COMMAND "${CMAKE_COMMAND}" -E touch "${CMAKE_CURRENT_SOURCE_DIR}/autorcc_depends/res1_input.txt") + +execute_process(COMMAND "${CMAKE_COMMAND}" --build . + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/autorcc_depends" +) + +file(TIMESTAMP "${qrc_file1}" file1_step1 "${timeformat}") + +if (NOT file1_step1 GREATER file1_before) + message(SEND_ERROR "file1 (${qrc_file1}) should have changed in the first step!") +endif() diff --git a/Tests/QtAutogen/autorcc_depends/CMakeLists.txt b/Tests/QtAutogen/autorcc_depends/CMakeLists.txt new file mode 100644 index 0000000..afd95bc --- /dev/null +++ b/Tests/QtAutogen/autorcc_depends/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 2.8) +project(autorcc_depends) + +set(CMAKE_AUTORCC ON) + +if (QT_TEST_VERSION STREQUAL 4) + find_package(Qt4 REQUIRED) + set(QT_CORE_TARGET Qt4::QtCore) +else() + if (NOT QT_TEST_VERSION STREQUAL 5) + message(SEND_ERROR "Invalid Qt version specified.") + endif() + + find_package(Qt5Core REQUIRED) + set(QT_CORE_TARGET Qt5::Core) +endif() + +add_executable(test_res1 + test_res1.cpp + res1.qrc +) +target_link_libraries(test_res1 ${QT_CORE_TARGET}) +add_custom_command(TARGET test_res1 POST_BUILD COMMAND + ${CMAKE_COMMAND} -E echo "$<TARGET_FILE:test_res1>" > info_file.txt) diff --git a/Tests/QtAutogen/autorcc_depends/res1.qrc b/Tests/QtAutogen/autorcc_depends/res1.qrc new file mode 100644 index 0000000..cfea618 --- /dev/null +++ b/Tests/QtAutogen/autorcc_depends/res1.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>res1_input.txt</file> + </qresource> +</RCC> diff --git a/Tests/QtAutogen/autorcc_depends/res1_input.txt b/Tests/QtAutogen/autorcc_depends/res1_input.txt new file mode 100644 index 0000000..da62762 --- /dev/null +++ b/Tests/QtAutogen/autorcc_depends/res1_input.txt @@ -0,0 +1 @@ +Res1 input. diff --git a/Tests/QtAutogen/autorcc_depends/test_res1.cpp b/Tests/QtAutogen/autorcc_depends/test_res1.cpp new file mode 100644 index 0000000..766b775 --- /dev/null +++ b/Tests/QtAutogen/autorcc_depends/test_res1.cpp @@ -0,0 +1,5 @@ + +int main() +{ + return 0; +} |