mirror of
https://github.com/reactos/CMake.git
synced 2025-01-07 11:40:23 +00:00
ab9d5896ae
We used to detect the `rcc` features before every `rcc` list invocation wich resulted in `rcc` be called twice for every listing operation. Now we detect the `rcc` list capabilities once during configuration and pass it to the cmake_autorcc target in the info file.
426 lines
13 KiB
C++
426 lines
13 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmQtAutoGen.h"
|
|
#include "cmQtAutoGeneratorRcc.h"
|
|
|
|
#include "cmAlgorithms.h"
|
|
#include "cmCryptoHash.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmOutputConverter.h"
|
|
#include "cmSystemTools.h"
|
|
|
|
// -- Static variables
|
|
|
|
static const char* SettingsKeyRcc = "ARCC_SETTINGS_HASH";
|
|
|
|
// -- Class methods
|
|
|
|
cmQtAutoGeneratorRcc::cmQtAutoGeneratorRcc()
|
|
: MultiConfig(cmQtAutoGen::WRAP)
|
|
, SettingsChanged(false)
|
|
{
|
|
}
|
|
|
|
bool cmQtAutoGeneratorRcc::InfoFileRead(cmMakefile* makefile)
|
|
{
|
|
// Utility lambdas
|
|
auto InfoGet = [makefile](const char* key) {
|
|
return makefile->GetSafeDefinition(key);
|
|
};
|
|
auto InfoGetList = [makefile](const char* key) -> std::vector<std::string> {
|
|
std::vector<std::string> list;
|
|
cmSystemTools::ExpandListArgument(makefile->GetSafeDefinition(key), list);
|
|
return list;
|
|
};
|
|
auto InfoGetConfig = [makefile, this](const char* key) -> std::string {
|
|
const char* valueConf = nullptr;
|
|
{
|
|
std::string keyConf = key;
|
|
keyConf += '_';
|
|
keyConf += this->GetInfoConfig();
|
|
valueConf = makefile->GetDefinition(keyConf);
|
|
}
|
|
if (valueConf == nullptr) {
|
|
valueConf = makefile->GetSafeDefinition(key);
|
|
}
|
|
return std::string(valueConf);
|
|
};
|
|
auto InfoGetConfigList =
|
|
[&InfoGetConfig](const char* key) -> std::vector<std::string> {
|
|
std::vector<std::string> list;
|
|
cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
|
|
return list;
|
|
};
|
|
|
|
// -- Read info file
|
|
if (!makefile->ReadListFile(this->GetInfoFile().c_str())) {
|
|
this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
|
|
"File processing failed");
|
|
return false;
|
|
}
|
|
|
|
// -- Meta
|
|
this->MultiConfig =
|
|
cmQtAutoGen::MultiConfigType(InfoGet("ARCC_MULTI_CONFIG"));
|
|
this->ConfigSuffix = InfoGetConfig("ARCC_CONFIG_SUFFIX");
|
|
if (this->ConfigSuffix.empty()) {
|
|
this->ConfigSuffix = "_";
|
|
this->ConfigSuffix += this->GetInfoConfig();
|
|
}
|
|
|
|
this->SettingsFile = InfoGetConfig("ARCC_SETTINGS_FILE");
|
|
|
|
// - Files and directories
|
|
this->ProjectSourceDir = InfoGet("ARCC_CMAKE_SOURCE_DIR");
|
|
this->ProjectBinaryDir = InfoGet("ARCC_CMAKE_BINARY_DIR");
|
|
this->CurrentSourceDir = InfoGet("ARCC_CMAKE_CURRENT_SOURCE_DIR");
|
|
this->CurrentBinaryDir = InfoGet("ARCC_CMAKE_CURRENT_BINARY_DIR");
|
|
this->AutogenBuildDir = InfoGet("ARCC_BUILD_DIR");
|
|
|
|
// - Qt environment
|
|
this->RccExecutable = InfoGet("ARCC_RCC_EXECUTABLE");
|
|
this->RccListOptions = InfoGetList("ARCC_RCC_LIST_OPTIONS");
|
|
|
|
// - Job
|
|
this->QrcFile = InfoGet("ARCC_SOURCE");
|
|
this->RccFile = InfoGet("ARCC_OUTPUT");
|
|
this->Options = InfoGetConfigList("ARCC_OPTIONS");
|
|
this->Inputs = InfoGetList("ARCC_INPUTS");
|
|
|
|
// - Validity checks
|
|
if (this->SettingsFile.empty()) {
|
|
this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
|
|
"Settings file name missing");
|
|
return false;
|
|
}
|
|
if (this->AutogenBuildDir.empty()) {
|
|
this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
|
|
"Autogen build directory missing");
|
|
return false;
|
|
}
|
|
if (this->RccExecutable.empty()) {
|
|
this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
|
|
"rcc executable missing");
|
|
return false;
|
|
}
|
|
if (this->QrcFile.empty()) {
|
|
this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
|
|
"rcc input file missing");
|
|
return false;
|
|
}
|
|
if (this->RccFile.empty()) {
|
|
this->LogFileError(cmQtAutoGen::RCC, this->GetInfoFile(),
|
|
"rcc output file missing");
|
|
return false;
|
|
}
|
|
|
|
// Init derived information
|
|
// ------------------------
|
|
|
|
// Init file path checksum generator
|
|
this->FilePathChecksum.setupParentDirs(
|
|
this->CurrentSourceDir, this->CurrentBinaryDir, this->ProjectSourceDir,
|
|
this->ProjectBinaryDir);
|
|
|
|
return true;
|
|
}
|
|
|
|
void cmQtAutoGeneratorRcc::SettingsFileRead(cmMakefile* makefile)
|
|
{
|
|
// Compose current settings strings
|
|
{
|
|
cmCryptoHash crypt(cmCryptoHash::AlgoSHA256);
|
|
std::string const sep(" ~~~ ");
|
|
{
|
|
std::string str;
|
|
str += this->RccExecutable;
|
|
str += sep;
|
|
str += cmJoin(this->RccListOptions, ";");
|
|
str += sep;
|
|
str += this->QrcFile;
|
|
str += sep;
|
|
str += this->RccFile;
|
|
str += sep;
|
|
str += cmJoin(this->Options, ";");
|
|
str += sep;
|
|
str += cmJoin(this->Inputs, ";");
|
|
str += sep;
|
|
this->SettingsString = crypt.HashString(str);
|
|
}
|
|
}
|
|
|
|
// Read old settings
|
|
if (makefile->ReadListFile(this->SettingsFile.c_str())) {
|
|
{
|
|
auto SMatch = [makefile](const char* key, std::string const& value) {
|
|
return (value == makefile->GetSafeDefinition(key));
|
|
};
|
|
if (!SMatch(SettingsKeyRcc, this->SettingsString)) {
|
|
this->SettingsChanged = true;
|
|
}
|
|
}
|
|
// In case any setting changed remove the old settings file.
|
|
// This triggers a full rebuild on the next run if the current
|
|
// build is aborted before writing the current settings in the end.
|
|
if (this->SettingsChanged) {
|
|
cmSystemTools::RemoveFile(this->SettingsFile);
|
|
}
|
|
} else {
|
|
// If the file could not be read re-generate everythiung.
|
|
this->SettingsChanged = true;
|
|
}
|
|
}
|
|
|
|
bool cmQtAutoGeneratorRcc::SettingsFileWrite()
|
|
{
|
|
bool success = true;
|
|
// Only write if any setting changed
|
|
if (this->SettingsChanged) {
|
|
if (this->GetVerbose()) {
|
|
this->LogInfo(cmQtAutoGen::RCC, "Writing settings file " +
|
|
cmQtAutoGen::Quoted(this->SettingsFile));
|
|
}
|
|
// Compose settings file content
|
|
std::string settings;
|
|
{
|
|
auto SettingAppend = [&settings](const char* key,
|
|
std::string const& value) {
|
|
settings += "set(";
|
|
settings += key;
|
|
settings += " ";
|
|
settings += cmOutputConverter::EscapeForCMake(value);
|
|
settings += ")\n";
|
|
};
|
|
SettingAppend(SettingsKeyRcc, this->SettingsString);
|
|
}
|
|
// Write settings file
|
|
if (!this->FileWrite(cmQtAutoGen::RCC, this->SettingsFile, settings)) {
|
|
this->LogFileError(cmQtAutoGen::RCC, this->SettingsFile,
|
|
"Settings file writing failed");
|
|
// Remove old settings file to trigger a full rebuild on the next run
|
|
cmSystemTools::RemoveFile(this->SettingsFile);
|
|
success = false;
|
|
}
|
|
}
|
|
return success;
|
|
}
|
|
|
|
bool cmQtAutoGeneratorRcc::Process(cmMakefile* makefile)
|
|
{
|
|
// Read info file
|
|
if (!this->InfoFileRead(makefile)) {
|
|
return false;
|
|
}
|
|
// Read latest settings
|
|
this->SettingsFileRead(makefile);
|
|
// Generate rcc file
|
|
if (!this->RccGenerate()) {
|
|
return false;
|
|
}
|
|
// Write latest settings
|
|
if (!this->SettingsFileWrite()) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @return True on success
|
|
*/
|
|
bool cmQtAutoGeneratorRcc::RccGenerate()
|
|
{
|
|
bool success = true;
|
|
bool rccGenerated = false;
|
|
|
|
std::string rccFileAbs;
|
|
{
|
|
std::string suffix;
|
|
switch (this->MultiConfig) {
|
|
case cmQtAutoGen::SINGLE:
|
|
break;
|
|
case cmQtAutoGen::WRAP:
|
|
suffix = "_CMAKE";
|
|
suffix += this->ConfigSuffix;
|
|
suffix += "_";
|
|
break;
|
|
case cmQtAutoGen::FULL:
|
|
suffix = this->ConfigSuffix;
|
|
break;
|
|
}
|
|
rccFileAbs = cmQtAutoGen::AppendFilenameSuffix(this->RccFile, suffix);
|
|
}
|
|
std::string const rccFileRel = cmSystemTools::RelativePath(
|
|
this->AutogenBuildDir.c_str(), rccFileAbs.c_str());
|
|
|
|
// Check if regeneration is required
|
|
bool generate = false;
|
|
std::string generateReason;
|
|
if (!cmSystemTools::FileExists(this->QrcFile)) {
|
|
{
|
|
std::string error = "Could not find the file\n ";
|
|
error += cmQtAutoGen::Quoted(this->QrcFile);
|
|
this->LogError(cmQtAutoGen::RCC, error);
|
|
}
|
|
success = false;
|
|
}
|
|
if (success && !generate && !cmSystemTools::FileExists(rccFileAbs.c_str())) {
|
|
if (this->GetVerbose()) {
|
|
generateReason = "Generating ";
|
|
generateReason += cmQtAutoGen::Quoted(rccFileAbs);
|
|
generateReason += " from its source file ";
|
|
generateReason += cmQtAutoGen::Quoted(this->QrcFile);
|
|
generateReason += " because it doesn't exist";
|
|
}
|
|
generate = true;
|
|
}
|
|
if (success && !generate && this->SettingsChanged) {
|
|
if (this->GetVerbose()) {
|
|
generateReason = "Generating ";
|
|
generateReason += cmQtAutoGen::Quoted(rccFileAbs);
|
|
generateReason += " from ";
|
|
generateReason += cmQtAutoGen::Quoted(this->QrcFile);
|
|
generateReason += " because the RCC settings changed";
|
|
}
|
|
generate = true;
|
|
}
|
|
if (success && !generate) {
|
|
std::string error;
|
|
if (FileIsOlderThan(rccFileAbs, this->QrcFile, &error)) {
|
|
if (this->GetVerbose()) {
|
|
generateReason = "Generating ";
|
|
generateReason += cmQtAutoGen::Quoted(rccFileAbs);
|
|
generateReason += " because it is older than ";
|
|
generateReason += cmQtAutoGen::Quoted(this->QrcFile);
|
|
}
|
|
generate = true;
|
|
} else {
|
|
if (!error.empty()) {
|
|
this->LogError(cmQtAutoGen::RCC, error);
|
|
success = false;
|
|
}
|
|
}
|
|
}
|
|
if (success && !generate) {
|
|
// Acquire input file list
|
|
std::vector<std::string> readFiles;
|
|
std::vector<std::string> const* files = nullptr;
|
|
if (!this->Inputs.empty()) {
|
|
files = &this->Inputs;
|
|
} else {
|
|
// Read input file list from qrc file
|
|
std::string error;
|
|
if (cmQtAutoGen::RccListInputs(this->RccExecutable, this->RccListOptions,
|
|
this->QrcFile, readFiles, &error)) {
|
|
files = &readFiles;
|
|
} else {
|
|
this->LogFileError(cmQtAutoGen::RCC, this->QrcFile, error);
|
|
success = false;
|
|
}
|
|
}
|
|
// Test if any input file is newer than the build file
|
|
if (files != nullptr) {
|
|
std::string error;
|
|
for (std::string const& resFile : *files) {
|
|
if (!cmSystemTools::FileExists(resFile.c_str())) {
|
|
error = "Could not find the file\n ";
|
|
error += cmQtAutoGen::Quoted(resFile);
|
|
error += "\nwhich is listed in\n ";
|
|
error += cmQtAutoGen::Quoted(this->QrcFile);
|
|
break;
|
|
}
|
|
if (FileIsOlderThan(rccFileAbs, resFile, &error)) {
|
|
if (this->GetVerbose()) {
|
|
generateReason = "Generating ";
|
|
generateReason += cmQtAutoGen::Quoted(rccFileAbs);
|
|
generateReason += " from ";
|
|
generateReason += cmQtAutoGen::Quoted(this->QrcFile);
|
|
generateReason += " because it is older than ";
|
|
generateReason += cmQtAutoGen::Quoted(resFile);
|
|
}
|
|
generate = true;
|
|
break;
|
|
}
|
|
if (!error.empty()) {
|
|
break;
|
|
}
|
|
}
|
|
// Print error
|
|
if (!error.empty()) {
|
|
this->LogError(cmQtAutoGen::RCC, error);
|
|
success = false;
|
|
}
|
|
}
|
|
}
|
|
// Regenerate on demand
|
|
if (generate) {
|
|
// Log
|
|
if (this->GetVerbose()) {
|
|
this->LogBold("Generating RCC source " + rccFileRel);
|
|
this->LogInfo(cmQtAutoGen::RCC, generateReason);
|
|
}
|
|
|
|
// Make sure the parent directory exists
|
|
if (this->MakeParentDirectory(cmQtAutoGen::RCC, rccFileAbs)) {
|
|
// Compose rcc command
|
|
std::vector<std::string> cmd;
|
|
cmd.push_back(this->RccExecutable);
|
|
cmd.insert(cmd.end(), this->Options.begin(), this->Options.end());
|
|
cmd.push_back("-o");
|
|
cmd.push_back(rccFileAbs);
|
|
cmd.push_back(this->QrcFile);
|
|
|
|
std::string output;
|
|
if (this->RunCommand(cmd, output)) {
|
|
// Success
|
|
rccGenerated = true;
|
|
} else {
|
|
{
|
|
std::string emsg = "rcc failed for\n ";
|
|
emsg += cmQtAutoGen::Quoted(this->QrcFile);
|
|
this->LogCommandError(cmQtAutoGen::RCC, emsg, cmd, output);
|
|
}
|
|
cmSystemTools::RemoveFile(rccFileAbs);
|
|
success = false;
|
|
}
|
|
} else {
|
|
// Parent directory creation failed
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
// Generate a wrapper source file on demand
|
|
if (success && (this->MultiConfig == cmQtAutoGen::WRAP)) {
|
|
// Wrapper file name
|
|
std::string const& wrapperFileAbs = this->RccFile;
|
|
std::string const wrapperFileRel = cmSystemTools::RelativePath(
|
|
this->AutogenBuildDir.c_str(), wrapperFileAbs.c_str());
|
|
// Wrapper file content
|
|
std::string content = "// This is an autogenerated configuration "
|
|
"wrapper file. Changes will be overwritten.\n"
|
|
"#include \"";
|
|
content += cmSystemTools::GetFilenameName(rccFileRel);
|
|
content += "\"\n";
|
|
// Write content to file
|
|
if (this->FileDiffers(wrapperFileAbs, content)) {
|
|
// Write new wrapper file
|
|
if (this->GetVerbose()) {
|
|
this->LogBold("Generating RCC wrapper " + wrapperFileRel);
|
|
}
|
|
if (!this->FileWrite(cmQtAutoGen::RCC, wrapperFileAbs, content)) {
|
|
this->LogFileError(cmQtAutoGen::RCC, wrapperFileAbs,
|
|
"rcc wrapper file writing failed");
|
|
success = false;
|
|
}
|
|
} else if (rccGenerated) {
|
|
// Just touch the wrapper file
|
|
if (this->GetVerbose()) {
|
|
this->LogInfo(cmQtAutoGen::RCC,
|
|
"Touching RCC wrapper " + wrapperFileRel);
|
|
}
|
|
cmSystemTools::Touch(wrapperFileAbs, false);
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|