blob: 3c9f1a84baef2c29d4acfb43ca48ed459972f2df [file] [log] [blame]
/* 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;
}