| /* 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; |
| } |