cmake/Source/cmQtAutoGen.cxx

395 lines
11 KiB
C++
Raw Normal View History

2018-01-26 17:06:56 +01:00
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmQtAutoGen.h"
2019-11-11 23:01:05 +01:00
2018-01-26 17:06:56 +01:00
#include <algorithm>
2019-11-11 23:01:05 +01:00
#include <array>
2020-02-01 23:06:01 +01:00
#include <initializer_list>
2018-01-26 17:06:56 +01:00
#include <sstream>
2019-11-11 23:01:05 +01:00
#include <utility>
2018-01-26 17:06:56 +01:00
2020-08-30 11:54:41 +02:00
#include <cmext/algorithm>
2020-02-01 23:06:01 +01:00
#include "cmsys/FStream.hxx"
#include "cmsys/RegularExpression.hxx"
#include "cmDuration.h"
#include "cmProcessOutput.h"
#include "cmStringAlgorithms.h"
#include "cmSystemTools.h"
2018-01-26 17:06:56 +01:00
// - Static functions
/// @brief Merges newOpts into baseOpts
/// @arg valueOpts list of options that accept a value
2022-03-29 21:10:50 +02:00
static void MergeOptions(std::vector<std::string>& baseOpts,
std::vector<std::string> const& newOpts,
std::initializer_list<cm::string_view> valueOpts,
bool isQt5OrLater)
2018-01-26 17:06:56 +01:00
{
if (newOpts.empty()) {
return;
}
if (baseOpts.empty()) {
baseOpts = newOpts;
return;
}
std::vector<std::string> extraOpts;
2020-02-01 23:06:01 +01:00
for (auto fit = newOpts.begin(), fitEnd = newOpts.end(); fit != fitEnd;
2018-01-26 17:06:56 +01:00
++fit) {
std::string const& newOpt = *fit;
2020-02-01 23:06:01 +01:00
auto existIt = std::find(baseOpts.begin(), baseOpts.end(), newOpt);
2018-01-26 17:06:56 +01:00
if (existIt != baseOpts.end()) {
if (newOpt.size() >= 2) {
// Acquire the option name
std::string optName;
{
auto oit = newOpt.begin();
if (*oit == '-') {
++oit;
2021-11-20 13:41:27 +01:00
if (isQt5OrLater && (*oit == '-')) {
2018-01-26 17:06:56 +01:00
++oit;
}
optName.assign(oit, newOpt.end());
}
}
// Test if this is a value option and change the existing value
2020-08-30 11:54:41 +02:00
if (!optName.empty() && cm::contains(valueOpts, optName)) {
2020-02-01 23:06:01 +01:00
const auto existItNext(existIt + 1);
const auto fitNext(fit + 1);
2018-01-26 17:06:56 +01:00
if ((existItNext != baseOpts.end()) && (fitNext != fitEnd)) {
*existItNext = *fitNext;
++fit;
}
}
}
} else {
extraOpts.push_back(newOpt);
}
}
// Append options
2020-08-30 11:54:41 +02:00
cm::append(baseOpts, extraOpts);
2018-01-26 17:06:56 +01:00
}
// - Class definitions
2018-04-23 21:13:27 +02:00
unsigned int const cmQtAutoGen::ParallelMax = 64;
2018-01-26 17:06:56 +01:00
2022-08-04 22:12:04 +02:00
#ifdef _WIN32
// Actually 32767 (see
// https://devblogs.microsoft.com/oldnewthing/20031210-00/?p=41553) but we
// allow for a small margin
size_t const cmQtAutoGen::CommandLineLengthMax = 32000;
#endif
2020-02-01 23:06:01 +01:00
cm::string_view cmQtAutoGen::GeneratorName(GenT genType)
2018-01-26 17:06:56 +01:00
{
2019-11-11 23:01:05 +01:00
switch (genType) {
case GenT::GEN:
2020-02-01 23:06:01 +01:00
return "AutoGen";
2019-11-11 23:01:05 +01:00
case GenT::MOC:
2020-02-01 23:06:01 +01:00
return "AutoMoc";
2019-11-11 23:01:05 +01:00
case GenT::UIC:
2020-02-01 23:06:01 +01:00
return "AutoUic";
2019-11-11 23:01:05 +01:00
case GenT::RCC:
2020-02-01 23:06:01 +01:00
return "AutoRcc";
2019-11-11 23:01:05 +01:00
}
2020-02-01 23:06:01 +01:00
return "AutoGen";
2019-11-11 23:01:05 +01:00
}
2020-02-01 23:06:01 +01:00
cm::string_view cmQtAutoGen::GeneratorNameUpper(GenT genType)
2019-11-11 23:01:05 +01:00
{
switch (genType) {
case GenT::GEN:
2020-02-01 23:06:01 +01:00
return "AUTOGEN";
2019-11-11 23:01:05 +01:00
case GenT::MOC:
2020-02-01 23:06:01 +01:00
return "AUTOMOC";
2019-11-11 23:01:05 +01:00
case GenT::UIC:
2020-02-01 23:06:01 +01:00
return "AUTOUIC";
2019-11-11 23:01:05 +01:00
case GenT::RCC:
2020-02-01 23:06:01 +01:00
return "AUTORCC";
2018-01-26 17:06:56 +01:00
}
2020-02-01 23:06:01 +01:00
return "AUTOGEN";
2018-01-26 17:06:56 +01:00
}
2019-11-11 23:01:05 +01:00
std::string cmQtAutoGen::Tools(bool moc, bool uic, bool rcc)
2018-01-26 17:06:56 +01:00
{
2020-02-01 23:06:01 +01:00
std::array<cm::string_view, 3> lst;
decltype(lst)::size_type num = 0;
2019-11-11 23:01:05 +01:00
if (moc) {
2020-02-01 23:06:01 +01:00
lst.at(num++) = "AUTOMOC";
2019-11-11 23:01:05 +01:00
}
if (uic) {
2020-02-01 23:06:01 +01:00
lst.at(num++) = "AUTOUIC";
2019-11-11 23:01:05 +01:00
}
if (rcc) {
2020-02-01 23:06:01 +01:00
lst.at(num++) = "AUTORCC";
2019-11-11 23:01:05 +01:00
}
2020-02-01 23:06:01 +01:00
switch (num) {
2019-11-11 23:01:05 +01:00
case 1:
2020-02-01 23:06:01 +01:00
return std::string(lst[0]);
2019-11-11 23:01:05 +01:00
case 2:
2020-02-01 23:06:01 +01:00
return cmStrCat(lst[0], " and ", lst[1]);
2019-11-11 23:01:05 +01:00
case 3:
2020-02-01 23:06:01 +01:00
return cmStrCat(lst[0], ", ", lst[1], " and ", lst[2]);
2019-11-11 23:01:05 +01:00
default:
break;
}
2020-02-01 23:06:01 +01:00
return std::string();
2018-01-26 17:06:56 +01:00
}
2020-02-01 23:06:01 +01:00
std::string cmQtAutoGen::Quoted(cm::string_view text)
2018-01-26 17:06:56 +01:00
{
2020-02-01 23:06:01 +01:00
static std::initializer_list<std::pair<const char*, const char*>> const
replacements = { { "\\", "\\\\" }, { "\"", "\\\"" }, { "\a", "\\a" },
{ "\b", "\\b" }, { "\f", "\\f" }, { "\n", "\\n" },
{ "\r", "\\r" }, { "\t", "\\t" }, { "\v", "\\v" } };
2018-01-26 17:06:56 +01:00
2020-02-01 23:06:01 +01:00
std::string res(text);
for (auto const& pair : replacements) {
2019-11-11 23:01:05 +01:00
cmSystemTools::ReplaceString(res, pair.first, pair.second);
2018-01-26 17:06:56 +01:00
}
2020-02-01 23:06:01 +01:00
return cmStrCat('"', res, '"');
2018-01-26 17:06:56 +01:00
}
2018-04-23 21:13:27 +02:00
std::string cmQtAutoGen::QuotedCommand(std::vector<std::string> const& command)
{
std::string res;
for (std::string const& item : command) {
if (!res.empty()) {
res.push_back(' ');
}
std::string const cesc = cmQtAutoGen::Quoted(item);
if (item.empty() || (cesc.size() > (item.size() + 2)) ||
(cesc.find(' ') != std::string::npos)) {
res += cesc;
} else {
res += item;
}
}
return res;
}
2020-02-01 23:06:01 +01:00
std::string cmQtAutoGen::FileNameWithoutLastExtension(cm::string_view filename)
{
auto slashPos = filename.rfind('/');
if (slashPos != cm::string_view::npos) {
filename.remove_prefix(slashPos + 1);
}
auto dotPos = filename.rfind('.');
return std::string(filename.substr(0, dotPos));
}
std::string cmQtAutoGen::ParentDir(cm::string_view filename)
2018-04-23 21:13:27 +02:00
{
2020-02-01 23:06:01 +01:00
auto slashPos = filename.rfind('/');
if (slashPos == cm::string_view::npos) {
2019-11-11 23:01:05 +01:00
return std::string();
2018-04-23 21:13:27 +02:00
}
2020-02-01 23:06:01 +01:00
return std::string(filename.substr(0, slashPos));
2018-04-23 21:13:27 +02:00
}
2020-02-01 23:06:01 +01:00
std::string cmQtAutoGen::SubDirPrefix(cm::string_view filename)
2018-01-26 17:06:56 +01:00
{
2020-02-01 23:06:01 +01:00
auto slashPos = filename.rfind('/');
if (slashPos == cm::string_view::npos) {
return std::string();
2018-01-26 17:06:56 +01:00
}
2020-02-01 23:06:01 +01:00
return std::string(filename.substr(0, slashPos + 1));
}
std::string cmQtAutoGen::AppendFilenameSuffix(cm::string_view filename,
cm::string_view suffix)
{
auto dotPos = filename.rfind('.');
if (dotPos == cm::string_view::npos) {
return cmStrCat(filename, suffix);
}
return cmStrCat(filename.substr(0, dotPos), suffix,
filename.substr(dotPos, filename.size() - dotPos));
2018-01-26 17:06:56 +01:00
}
void cmQtAutoGen::UicMergeOptions(std::vector<std::string>& baseOpts,
std::vector<std::string> const& newOpts,
2021-11-20 13:41:27 +01:00
bool isQt5OrLater)
2018-01-26 17:06:56 +01:00
{
2020-02-01 23:06:01 +01:00
static std::initializer_list<cm::string_view> const valueOpts = {
2018-01-26 17:06:56 +01:00
"tr", "translate", "postfix", "generator",
"include", // Since Qt 5.3
"g"
};
2021-11-20 13:41:27 +01:00
MergeOptions(baseOpts, newOpts, valueOpts, isQt5OrLater);
2018-01-26 17:06:56 +01:00
}
void cmQtAutoGen::RccMergeOptions(std::vector<std::string>& baseOpts,
std::vector<std::string> const& newOpts,
2021-11-20 13:41:27 +01:00
bool isQt5OrLater)
2018-01-26 17:06:56 +01:00
{
2020-02-01 23:06:01 +01:00
static std::initializer_list<cm::string_view> const valueOpts = {
"name", "root", "compress", "threshold"
};
2021-11-20 13:41:27 +01:00
MergeOptions(baseOpts, newOpts, valueOpts, isQt5OrLater);
2018-01-26 17:06:56 +01:00
}
2019-11-11 23:01:05 +01:00
static void RccListParseContent(std::string const& content,
std::vector<std::string>& files)
2018-01-26 17:06:56 +01:00
{
2018-04-23 21:13:27 +02:00
cmsys::RegularExpression fileMatchRegex("(<file[^<]+)");
cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)");
const char* contentChars = content.c_str();
while (fileMatchRegex.find(contentChars)) {
std::string const qrcEntry = fileMatchRegex.match(1);
contentChars += qrcEntry.size();
{
fileReplaceRegex.find(qrcEntry);
std::string const tag = fileReplaceRegex.match(1);
files.push_back(qrcEntry.substr(tag.size()));
2018-01-26 17:06:56 +01:00
}
2018-04-23 21:13:27 +02:00
}
}
2019-11-11 23:01:05 +01:00
static bool RccListParseOutput(std::string const& rccStdOut,
std::string const& rccStdErr,
std::vector<std::string>& files,
std::string& error)
2018-04-23 21:13:27 +02:00
{
// Lambda to strip CR characters
auto StripCR = [](std::string& line) {
std::string::size_type cr = line.find('\r');
if (cr != std::string::npos) {
line = line.substr(0, cr);
2018-01-26 17:06:56 +01:00
}
2018-04-23 21:13:27 +02:00
};
// Parse rcc std output
{
std::istringstream ostr(rccStdOut);
std::string oline;
while (std::getline(ostr, oline)) {
StripCR(oline);
if (!oline.empty()) {
files.push_back(oline);
}
}
}
// Parse rcc error output
{
std::istringstream estr(rccStdErr);
std::string eline;
while (std::getline(estr, eline)) {
StripCR(eline);
if (cmHasLiteralPrefix(eline, "RCC: Error in")) {
static std::string const searchString = "Cannot find file '";
std::string::size_type pos = eline.find(searchString);
if (pos == std::string::npos) {
2020-02-01 23:06:01 +01:00
error = cmStrCat("rcc lists unparsable output:\n",
cmQtAutoGen::Quoted(eline), '\n');
2018-04-23 21:13:27 +02:00
return false;
}
pos += searchString.length();
std::string::size_type sz = eline.size() - pos - 1;
files.push_back(eline.substr(pos, sz));
}
}
}
return true;
}
2019-11-11 23:01:05 +01:00
cmQtAutoGen::RccLister::RccLister() = default;
cmQtAutoGen::RccLister::RccLister(std::string rccExecutable,
std::vector<std::string> listOptions)
: RccExcutable_(std::move(rccExecutable))
, ListOptions_(std::move(listOptions))
{
}
bool cmQtAutoGen::RccLister::list(std::string const& qrcFile,
std::vector<std::string>& files,
std::string& error, bool verbose) const
2018-04-23 21:13:27 +02:00
{
2019-11-11 23:01:05 +01:00
error.clear();
if (!cmSystemTools::FileExists(qrcFile, true)) {
2020-02-01 23:06:01 +01:00
error =
cmStrCat("The resource file ", Quoted(qrcFile), " does not exist.");
2019-11-11 23:01:05 +01:00
return false;
}
// Run rcc list command in the directory of the qrc file with the pathless
// qrc file name argument. This way rcc prints relative paths.
// This avoids issues on Windows when the qrc file is in a path that
// contains non-ASCII characters.
std::string const fileDir = cmSystemTools::GetFilenamePath(qrcFile);
if (!this->RccExcutable_.empty() &&
cmSystemTools::FileExists(this->RccExcutable_, true) &&
!this->ListOptions_.empty()) {
bool result = false;
int retVal = 0;
std::string rccStdOut;
std::string rccStdErr;
{
std::vector<std::string> cmd;
cmd.emplace_back(this->RccExcutable_);
2020-08-30 11:54:41 +02:00
cm::append(cmd, this->ListOptions_);
2019-11-11 23:01:05 +01:00
cmd.emplace_back(cmSystemTools::GetFilenameName(qrcFile));
// Log command
if (verbose) {
2020-02-01 23:06:01 +01:00
cmSystemTools::Stdout(
cmStrCat("Running command:\n", QuotedCommand(cmd), '\n'));
2019-11-11 23:01:05 +01:00
}
result = cmSystemTools::RunSingleCommand(
cmd, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(),
cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto);
}
if (!result || retVal) {
2020-02-01 23:06:01 +01:00
error =
cmStrCat("The rcc list process failed for ", Quoted(qrcFile), '\n');
2019-11-11 23:01:05 +01:00
if (!rccStdOut.empty()) {
2020-02-01 23:06:01 +01:00
error += cmStrCat(rccStdOut, '\n');
2019-11-11 23:01:05 +01:00
}
if (!rccStdErr.empty()) {
2020-02-01 23:06:01 +01:00
error += cmStrCat(rccStdErr, '\n');
2019-11-11 23:01:05 +01:00
}
return false;
}
if (!RccListParseOutput(rccStdOut, rccStdErr, files, error)) {
return false;
}
} else {
// We can't use rcc for the file listing.
// Read the qrc file content into string and parse it.
{
std::string qrcContents;
{
cmsys::ifstream ifs(qrcFile.c_str());
if (ifs) {
std::ostringstream osst;
osst << ifs.rdbuf();
qrcContents = osst.str();
} else {
2020-02-01 23:06:01 +01:00
error = cmStrCat("The resource file ", Quoted(qrcFile),
" is not readable\n");
2019-11-11 23:01:05 +01:00
return false;
}
}
// Parse string content
RccListParseContent(qrcContents, files);
}
}
// Convert relative paths to absolute paths
2018-04-23 21:13:27 +02:00
for (std::string& entry : files) {
2019-11-11 23:01:05 +01:00
entry = cmSystemTools::CollapseFullPath(entry, fileDir);
2018-01-26 17:06:56 +01:00
}
2019-11-11 23:01:05 +01:00
return true;
2018-01-26 17:06:56 +01:00
}