Merge topic 'curl-tls-verify'
[kiteware-cmake.git] / Source / cmQtAutoGen.cxx
blob0a394b57cdb20a56d502f55c5b7a50b8fc32a221
1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmQtAutoGen.h"
5 #include <algorithm>
6 #include <array>
7 #include <initializer_list>
8 #include <sstream>
9 #include <utility>
11 #include <cmext/algorithm>
13 #include "cmsys/FStream.hxx"
14 #include "cmsys/RegularExpression.hxx"
16 #include "cmDuration.h"
17 #include "cmProcessOutput.h"
18 #include "cmStringAlgorithms.h"
19 #include "cmSystemTools.h"
21 // - Static functions
23 /// @brief Merges newOpts into baseOpts
24 /// @arg valueOpts list of options that accept a value
25 static void MergeOptions(std::vector<std::string>& baseOpts,
26 std::vector<std::string> const& newOpts,
27 std::initializer_list<cm::string_view> valueOpts,
28 bool isQt5OrLater)
30 if (newOpts.empty()) {
31 return;
33 if (baseOpts.empty()) {
34 baseOpts = newOpts;
35 return;
38 std::vector<std::string> extraOpts;
39 for (auto fit = newOpts.begin(), fitEnd = newOpts.end(); fit != fitEnd;
40 ++fit) {
41 std::string const& newOpt = *fit;
42 auto existIt = std::find(baseOpts.begin(), baseOpts.end(), newOpt);
43 if (existIt != baseOpts.end()) {
44 if (newOpt.size() >= 2) {
45 // Acquire the option name
46 std::string optName;
48 auto oit = newOpt.begin();
49 if (*oit == '-') {
50 ++oit;
51 if (isQt5OrLater && (*oit == '-')) {
52 ++oit;
54 optName.assign(oit, newOpt.end());
57 // Test if this is a value option and change the existing value
58 if (!optName.empty() && cm::contains(valueOpts, optName)) {
59 const auto existItNext(existIt + 1);
60 const auto fitNext(fit + 1);
61 if ((existItNext != baseOpts.end()) && (fitNext != fitEnd)) {
62 *existItNext = *fitNext;
63 ++fit;
67 } else {
68 extraOpts.push_back(newOpt);
71 // Append options
72 cm::append(baseOpts, extraOpts);
75 // - Class definitions
77 unsigned int const cmQtAutoGen::ParallelMax = 64;
79 cm::string_view cmQtAutoGen::GeneratorName(GenT genType)
81 switch (genType) {
82 case GenT::GEN:
83 return "AutoGen";
84 case GenT::MOC:
85 return "AutoMoc";
86 case GenT::UIC:
87 return "AutoUic";
88 case GenT::RCC:
89 return "AutoRcc";
91 return "AutoGen";
94 cm::string_view cmQtAutoGen::GeneratorNameUpper(GenT genType)
96 switch (genType) {
97 case GenT::GEN:
98 return "AUTOGEN";
99 case GenT::MOC:
100 return "AUTOMOC";
101 case GenT::UIC:
102 return "AUTOUIC";
103 case GenT::RCC:
104 return "AUTORCC";
106 return "AUTOGEN";
109 std::string cmQtAutoGen::Tools(bool moc, bool uic, bool rcc)
111 std::array<cm::string_view, 3> lst;
112 decltype(lst)::size_type num = 0;
113 if (moc) {
114 lst.at(num++) = "AUTOMOC";
116 if (uic) {
117 lst.at(num++) = "AUTOUIC";
119 if (rcc) {
120 lst.at(num++) = "AUTORCC";
122 switch (num) {
123 case 1:
124 return std::string(lst[0]);
125 case 2:
126 return cmStrCat(lst[0], " and ", lst[1]);
127 case 3:
128 return cmStrCat(lst[0], ", ", lst[1], " and ", lst[2]);
129 default:
130 break;
132 return std::string();
135 std::string cmQtAutoGen::Quoted(cm::string_view text)
137 static std::initializer_list<std::pair<const char*, const char*>> const
138 replacements = { { "\\", "\\\\" }, { "\"", "\\\"" }, { "\a", "\\a" },
139 { "\b", "\\b" }, { "\f", "\\f" }, { "\n", "\\n" },
140 { "\r", "\\r" }, { "\t", "\\t" }, { "\v", "\\v" } };
142 std::string res(text);
143 for (auto const& pair : replacements) {
144 cmSystemTools::ReplaceString(res, pair.first, pair.second);
146 return cmStrCat('"', res, '"');
149 std::string cmQtAutoGen::QuotedCommand(std::vector<std::string> const& command)
151 std::string res;
152 for (std::string const& item : command) {
153 if (!res.empty()) {
154 res.push_back(' ');
156 std::string const cesc = cmQtAutoGen::Quoted(item);
157 if (item.empty() || (cesc.size() > (item.size() + 2)) ||
158 (cesc.find(' ') != std::string::npos)) {
159 res += cesc;
160 } else {
161 res += item;
164 return res;
167 std::string cmQtAutoGen::FileNameWithoutLastExtension(cm::string_view filename)
169 auto slashPos = filename.rfind('/');
170 if (slashPos != cm::string_view::npos) {
171 filename.remove_prefix(slashPos + 1);
173 auto dotPos = filename.rfind('.');
174 return std::string(filename.substr(0, dotPos));
177 std::string cmQtAutoGen::ParentDir(cm::string_view filename)
179 auto slashPos = filename.rfind('/');
180 if (slashPos == cm::string_view::npos) {
181 return std::string();
183 return std::string(filename.substr(0, slashPos));
186 std::string cmQtAutoGen::SubDirPrefix(cm::string_view filename)
188 auto slashPos = filename.rfind('/');
189 if (slashPos == cm::string_view::npos) {
190 return std::string();
192 return std::string(filename.substr(0, slashPos + 1));
195 std::string cmQtAutoGen::AppendFilenameSuffix(cm::string_view filename,
196 cm::string_view suffix)
198 auto dotPos = filename.rfind('.');
199 if (dotPos == cm::string_view::npos) {
200 return cmStrCat(filename, suffix);
202 return cmStrCat(filename.substr(0, dotPos), suffix,
203 filename.substr(dotPos, filename.size() - dotPos));
206 void cmQtAutoGen::UicMergeOptions(std::vector<std::string>& baseOpts,
207 std::vector<std::string> const& newOpts,
208 bool isQt5OrLater)
210 static std::initializer_list<cm::string_view> const valueOpts = {
211 "tr", "translate", "postfix", "generator",
212 "include", // Since Qt 5.3
215 MergeOptions(baseOpts, newOpts, valueOpts, isQt5OrLater);
218 void cmQtAutoGen::RccMergeOptions(std::vector<std::string>& baseOpts,
219 std::vector<std::string> const& newOpts,
220 bool isQt5OrLater)
222 static std::initializer_list<cm::string_view> const valueOpts = {
223 "name", "root", "compress", "threshold"
225 MergeOptions(baseOpts, newOpts, valueOpts, isQt5OrLater);
228 static void RccListParseContent(std::string const& content,
229 std::vector<std::string>& files)
231 cmsys::RegularExpression fileMatchRegex("(<file[^<]+)");
232 cmsys::RegularExpression fileReplaceRegex("(^<file[^>]*>)");
234 const char* contentChars = content.c_str();
235 while (fileMatchRegex.find(contentChars)) {
236 std::string const qrcEntry = fileMatchRegex.match(1);
237 contentChars += qrcEntry.size();
239 fileReplaceRegex.find(qrcEntry);
240 std::string const tag = fileReplaceRegex.match(1);
241 files.push_back(qrcEntry.substr(tag.size()));
246 static bool RccListParseOutput(std::string const& rccStdOut,
247 std::string const& rccStdErr,
248 std::vector<std::string>& files,
249 std::string& error)
251 // Lambda to strip CR characters
252 auto StripCR = [](std::string& line) {
253 std::string::size_type cr = line.find('\r');
254 if (cr != std::string::npos) {
255 line = line.substr(0, cr);
259 // Parse rcc std output
261 std::istringstream ostr(rccStdOut);
262 std::string oline;
263 while (std::getline(ostr, oline)) {
264 StripCR(oline);
265 if (!oline.empty()) {
266 files.push_back(oline);
270 // Parse rcc error output
272 std::istringstream estr(rccStdErr);
273 std::string eline;
274 while (std::getline(estr, eline)) {
275 StripCR(eline);
276 if (cmHasLiteralPrefix(eline, "RCC: Error in")) {
277 static std::string const searchString = "Cannot find file '";
279 std::string::size_type pos = eline.find(searchString);
280 if (pos == std::string::npos) {
281 error = cmStrCat("rcc lists unparsable output:\n",
282 cmQtAutoGen::Quoted(eline), '\n');
283 return false;
285 pos += searchString.length();
286 std::string::size_type sz = eline.size() - pos - 1;
287 files.push_back(eline.substr(pos, sz));
292 return true;
295 cmQtAutoGen::RccLister::RccLister() = default;
297 cmQtAutoGen::RccLister::RccLister(std::string rccExecutable,
298 std::vector<std::string> listOptions)
299 : RccExcutable_(std::move(rccExecutable))
300 , ListOptions_(std::move(listOptions))
304 bool cmQtAutoGen::RccLister::list(std::string const& qrcFile,
305 std::vector<std::string>& files,
306 std::string& error, bool verbose) const
308 error.clear();
310 if (!cmSystemTools::FileExists(qrcFile, true)) {
311 error =
312 cmStrCat("The resource file ", Quoted(qrcFile), " does not exist.");
313 return false;
316 // Run rcc list command in the directory of the qrc file with the pathless
317 // qrc file name argument. This way rcc prints relative paths.
318 // This avoids issues on Windows when the qrc file is in a path that
319 // contains non-ASCII characters.
320 std::string const fileDir = cmSystemTools::GetFilenamePath(qrcFile);
322 if (!this->RccExcutable_.empty() &&
323 cmSystemTools::FileExists(this->RccExcutable_, true) &&
324 !this->ListOptions_.empty()) {
326 bool result = false;
327 int retVal = 0;
328 std::string rccStdOut;
329 std::string rccStdErr;
331 std::vector<std::string> cmd;
332 cmd.emplace_back(this->RccExcutable_);
333 cm::append(cmd, this->ListOptions_);
334 cmd.emplace_back(cmSystemTools::GetFilenameName(qrcFile));
336 // Log command
337 if (verbose) {
338 cmSystemTools::Stdout(
339 cmStrCat("Running command:\n", QuotedCommand(cmd), '\n'));
342 result = cmSystemTools::RunSingleCommand(
343 cmd, &rccStdOut, &rccStdErr, &retVal, fileDir.c_str(),
344 cmSystemTools::OUTPUT_NONE, cmDuration::zero(), cmProcessOutput::Auto);
346 if (!result || retVal) {
347 error =
348 cmStrCat("The rcc list process failed for ", Quoted(qrcFile), '\n');
349 if (!rccStdOut.empty()) {
350 error += cmStrCat(rccStdOut, '\n');
352 if (!rccStdErr.empty()) {
353 error += cmStrCat(rccStdErr, '\n');
355 return false;
357 if (!RccListParseOutput(rccStdOut, rccStdErr, files, error)) {
358 return false;
360 } else {
361 // We can't use rcc for the file listing.
362 // Read the qrc file content into string and parse it.
364 std::string qrcContents;
366 cmsys::ifstream ifs(qrcFile.c_str());
367 if (ifs) {
368 std::ostringstream osst;
369 osst << ifs.rdbuf();
370 qrcContents = osst.str();
371 } else {
372 error = cmStrCat("The resource file ", Quoted(qrcFile),
373 " is not readable\n");
374 return false;
377 // Parse string content
378 RccListParseContent(qrcContents, files);
382 // Convert relative paths to absolute paths
383 for (std::string& entry : files) {
384 entry = cmSystemTools::CollapseFullPath(entry, fileDir);
386 return true;