QtAutogen: Regenerate qrc files if their input changes (#15074)
Get dependencies from the output of ``rcc --list`` if using Qt 5. Otherwise process the file in the same way as the qt4_add_resources macro. This does not work for RCC files which are generated. The cmake_autogen build step is implemented as a PRE_BUILD step of the target currently if possible, rather than a standalone custom target. This is to keep the number of (synthesized) custom targets that appear in the UI low, but in some cases it is necessary to fall back to a full custom target. Fall back to a full custom target for the VS builds if autorcc is used.
This commit is contained in:
parent
a29953180c
commit
6e1c359fe9
|
@ -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@ )
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
|
@ -0,0 +1,5 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>res1_input.txt</file>
|
||||
</qresource>
|
||||
</RCC>
|
|
@ -0,0 +1 @@
|
|||
Res1 input.
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue