Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4/*
5 * The tool generates deployment artifacts for the Qt builds such as:
6 * - CaMeL case header files named by public C++ symbols located in public module header files
7 * - Header file that contains the module version information, and named as <module>Vesion
8 * - LD version script if applicable
9 * - Aliases or copies of the header files sorted by the generic Qt-types: public/private/qpa
10 * and stored in the corresponding directories.
11 * Also the tool executes conformity checks on each header file if applicable, to make sure they
12 * follow rules that are relevant for their header type.
13 * The tool can be run in two modes: with either '-all' or '-headers' options specified. Depending
14 * on the selected mode, the tool either scans the filesystem to find header files or use the
15 * pre-defined list of header files.
16 */
17
18#include <iostream>
19#include <fstream>
20#include <string>
21#include <string_view>
22#include <cstring>
23#include <sstream>
24#include <filesystem>
25#include <unordered_map>
26#include <vector>
27#include <regex>
28#include <map>
29#include <set>
30#include <stdexcept>
31#include <array>
32
38
39// Enum contains the list of checks that can be executed on header files.
42 NamespaceChecks = 1, /* Checks if header file is wrapped with QT_<BEGIN|END>_NAMESPACE macros */
43 PrivateHeaderChecks = 2, /* Checks if the public header includes a private header */
44 IncludeChecks = 4, /* Checks if the real header file but not an alias is included */
45 WeMeantItChecks = 8, /* Checks if private header files contains 'We meant it' disclaimer */
47 /* Checks that lead to the fatal error of the sync process: */
50};
51
53
54static const std::regex GlobalHeaderRegex("^q(.*)global\\.h$");
55
56constexpr std::string_view ErrorMessagePreamble = "ERROR: ";
57constexpr std::string_view WarningMessagePreamble = "WARNING: ";
58
59// This comparator is used to sort include records in master header.
60// It's used to put q.*global.h file to the top of the list and sort all other files alphabetically.
61bool MasterHeaderIncludeComparator(const std::string &a, const std::string &b)
62{
63 std::smatch amatch;
64 std::smatch bmatch;
65
66 if (std::regex_match(a, amatch, GlobalHeaderRegex)) {
67 if (std::regex_match(b, bmatch, GlobalHeaderRegex)) {
68 return amatch[1].str().empty()
69 || (!bmatch[1].str().empty() && amatch[1].str() < bmatch[1].str());
70 }
71 return true;
72 } else if (std::regex_match(b, bmatch, GlobalHeaderRegex)) {
73 return false;
74 }
75
76 return a < b;
77};
78
79namespace utils {
80std::string asciiToLower(std::string s)
81{
82 std::transform(s.begin(), s.end(), s.begin(),
83 [](unsigned char c) { return (c >= 'A' && c <= 'Z') ? c | 0x20 : c; });
84 return s;
85}
86
87std::string asciiToUpper(std::string s)
88{
89 std::transform(s.begin(), s.end(), s.begin(),
90 [](unsigned char c) { return (c >= 'a' && c <= 'z') ? c & 0xdf : c; });
91 return s;
92}
93
94bool parseVersion(const std::string &version, int &major, int &minor)
95{
96 const size_t separatorPos = version.find('.');
97 if (separatorPos == std::string::npos || separatorPos == (version.size() - 1)
98 || separatorPos == 0)
99 return false;
100
101 try {
102 size_t pos = 0;
103 major = std::stoi(version.substr(0, separatorPos), &pos);
104 if (pos != separatorPos)
105 return false;
106
107 const size_t nextPart = separatorPos + 1;
108 pos = 0;
109 minor = std::stoi(version.substr(nextPart), &pos);
110 if (pos != (version.size() - nextPart))
111 return false;
112 } catch (const std::invalid_argument &) {
113 return false;
114 } catch (const std::out_of_range &) {
115 return false;
116 }
117
118 return true;
119}
120
121class DummyOutputStream : public std::ostream
122{
123 struct : public std::streambuf
124 {
125 int overflow(int c) override { return c; }
126 } buff;
127
128public:
129 DummyOutputStream() : std::ostream(&buff) { }
131
133{
134 std::cerr << "Internal error. Please create bugreport at https://bugreports.qt.io "
135 "using 'Build tools: Other component.'"
136 << std::endl;
137}
138
139void printFilesystemError(const std::filesystem::filesystem_error &fserr, std::string_view errorMsg)
140{
141 std::cerr << errorMsg << ": " << fserr.path1() << ".\n"
142 << fserr.what() << "(" << fserr.code().value() << ")" << std::endl;
143}
144
145std::filesystem::path normilizedPath(const std::string &path)
146{
147 try {
148 auto result = std::filesystem::path(std::filesystem::weakly_canonical(path).generic_string());
149 return result;
150 } catch (const std::filesystem::filesystem_error &fserr) {
151 printFilesystemError(fserr, "Unable to normalize path");
152 throw;
153 }
154}
155
156bool createDirectories(const std::string &path, std::string_view errorMsg, bool *exists = nullptr)
157{
158 bool result = true;
159 try {
160 if (!std::filesystem::exists(path)) {
161 if (exists)
162 *exists = false;
163 std::filesystem::create_directories(path);
164 } else {
165 if (exists)
166 *exists = true;
167 }
168 } catch (const std::filesystem::filesystem_error &fserr) {
169 result = false;
170 std::cerr << errorMsg << ": " << path << ".\n"
171 << fserr.code().message() << "(" << fserr.code().value() << "):" << fserr.what()
172 << std::endl;
173 }
174 return result;
175}
176
177} // namespace utils
178
179using FileStamp = std::filesystem::file_time_type;
180
182{
183 template<typename T>
184 struct CommandLineOption
185 {
186 CommandLineOption(T *_value, bool _isOptional = false)
187 : value(_value), isOptional(_isOptional)
188 {
189 }
190
191 T *value;
192 bool isOptional;
193 };
194
195public:
196 CommandLineOptions(int argc, char *argv[]) : m_isValid(parseArguments(argc, argv)) { }
197
198 bool isValid() const { return m_isValid; }
199
200 const std::string &moduleName() const { return m_moduleName; }
201
202 const std::string &sourceDir() const { return m_sourceDir; }
203
204 const std::string &binaryDir() const { return m_binaryDir; }
205
206 const std::string &includeDir() const { return m_includeDir; }
207
208 const std::string &privateIncludeDir() const { return m_privateIncludeDir; }
209
210 const std::string &qpaIncludeDir() const { return m_qpaIncludeDir; }
211
212 const std::string &rhiIncludeDir() const { return m_rhiIncludeDir; }
213
214 const std::string &ssgIncludeDir() const { return m_ssgIncludeDir; }
215
216 const std::string &stagingDir() const { return m_stagingDir; }
217
218 const std::string &versionScriptFile() const { return m_versionScriptFile; }
219
220 const std::set<std::string> &knownModules() const { return m_knownModules; }
221
222 const std::regex &qpaHeadersRegex() const { return m_qpaHeadersRegex; }
223
224 const std::regex &rhiHeadersRegex() const { return m_rhiHeadersRegex; }
225
226 const std::regex &ssgHeadersRegex() const { return m_ssgHeadersRegex; }
227
228 const std::regex &privateHeadersRegex() const { return m_privateHeadersRegex; }
229
230 const std::regex &publicNamespaceRegex() const { return m_publicNamespaceRegex; }
231
232 const std::set<std::string> &headers() const { return m_headers; }
233
234 const std::set<std::string> &generatedHeaders() const { return m_generatedHeaders; }
235
236 bool scanAllMode() const { return m_scanAllMode; }
237
238 bool isInternal() const { return m_isInternal; }
239
240 bool isNonQtModule() const { return m_isNonQtModule; }
241
242 bool printHelpOnly() const { return m_printHelpOnly; }
243
244 bool debug() const { return m_debug; }
245
246 bool copy() const { return m_copy; }
247
248 bool minimal() const { return m_minimal; }
249
250 bool showOnly() const { return m_showOnly; }
251
252 bool warningsAreErrors() const { return m_warningsAreErrors; }
253
254 void printHelp() const
255 {
256 std::cout << "Usage: syncqt -sourceDir <dir> -binaryDir <dir> -module <module name>"
257 " -includeDir <dir> -privateIncludeDir <dir> -qpaIncludeDir <dir> -rhiIncludeDir <dir> -ssgIncludeDir <dir>"
258 " -stagingDir <dir> <-headers <header list>|-all> [-debug]"
259 " [-versionScript <path>] [-qpaHeadersFilter <regex>] [-rhiHeadersFilter <regex>]"
260 " [-knownModules <module1> <module2>... <moduleN>]"
261 " [-nonQt] [-internal] [-copy]\n"
262 ""
263 "Mandatory arguments:\n"
264 " -module Module name.\n"
265 " -headers List of header files.\n"
266 " -all In 'all' mode syncqt scans source\n"
267 " directory for public qt headers and\n"
268 " artifacts not considering CMake source\n"
269 " tree. The main use cases are the \n"
270 " generating of documentation and creating\n"
271 " API review changes.\n"
272 " -sourceDir Module source directory.\n"
273 " -binaryDir Module build directory.\n"
274 " -includeDir Module include directory where the\n"
275 " generated header files will be located.\n"
276 " -privateIncludeDir Module include directory for the\n"
277 " generated private header files.\n"
278 " -qpaIncludeDir Module include directory for the \n"
279 " generated QPA header files.\n"
280 " -rhiIncludeDir Module include directory for the \n"
281 " generated RHI header files.\n"
282 " -ssgIncludeDir Module include directory for the \n"
283 " generated SSG header files.\n"
284 " -stagingDir Temporary staging directory to collect\n"
285 " artifacts that need to be installed.\n"
286 " -knownModules list of known modules. syncqt uses the\n"
287 " list to check the #include macros\n"
288 " consistency.\n"
289 "Optional arguments:\n"
290 " -internal Indicates that the module is internal.\n"
291 " -nonQt Indicates that the module is not a Qt\n"
292 " module.\n"
293 " -privateHeadersFilter Regex that filters private header files\n"
294 " from the list of 'headers'.\n"
295 " -qpaHeadersFilter Regex that filters qpa header files from.\n"
296 " the list of 'headers'.\n"
297 " -rhiHeadersFilter Regex that filters rhi header files from.\n"
298 " the list of 'headers'.\n"
299 " -ssgHeadersFilter Regex that filters ssg files from.\n"
300 " the list of 'headers'.\n"
301 " -publicNamespaceFilter Symbols that are in the specified\n"
302 " namespace.\n"
303 " are treated as public symbols.\n"
304 " -versionScript Generate linker version script by\n"
305 " provided path.\n"
306 " -debug Enable debug output.\n"
307 " -copy Copy header files instead of creating\n"
308 " aliases.\n"
309 " -minimal Do not create CaMeL case headers for the\n"
310 " public C++ symbols.\n"
311 " -showonly Show actions, but not perform them.\n"
312 " -warningsAreErrors Treat all warnings as errors.\n"
313 " -help Print this help.\n";
314 }
315
316private:
317 template<typename T>
318 [[nodiscard]] bool checkRequiredArguments(const std::unordered_map<std::string, T> &arguments)
319 {
320 bool ret = true;
321 for (const auto &argument : arguments) {
322 if (!argument.second.isOptional
323 && (!argument.second.value || argument.second.value->size()) == 0) {
324 std::cerr << "Missing argument: " << argument.first << std::endl;
325 ret = false;
326 }
327 }
328 return ret;
329 }
330
331 [[nodiscard]] bool parseArguments(int argc, char *argv[])
332 {
333 std::string qpaHeadersFilter;
334 std::string rhiHeadersFilter;
335 std::string ssgHeadersFilter;
336 std::string privateHeadersFilter;
337 std::string publicNamespaceFilter;
338 static std::unordered_map<std::string, CommandLineOption<std::string>> stringArgumentMap = {
339 { "-module", { &m_moduleName } },
340 { "-sourceDir", { &m_sourceDir } },
341 { "-binaryDir", { &m_binaryDir } },
342 { "-privateHeadersFilter", { &privateHeadersFilter, true } },
343 { "-qpaHeadersFilter", { &qpaHeadersFilter, true } },
344 { "-rhiHeadersFilter", { &rhiHeadersFilter, true } },
345 { "-ssgHeadersFilter", { &ssgHeadersFilter, true } },
346 { "-includeDir", { &m_includeDir } },
347 { "-privateIncludeDir", { &m_privateIncludeDir } },
348 { "-qpaIncludeDir", { &m_qpaIncludeDir } },
349 { "-rhiIncludeDir", { &m_rhiIncludeDir } },
350 { "-ssgIncludeDir", { &m_ssgIncludeDir } },
351 { "-stagingDir", { &m_stagingDir, true } },
352 { "-versionScript", { &m_versionScriptFile, true } },
353 { "-publicNamespaceFilter", { &publicNamespaceFilter, true } },
354 };
355
356 static const std::unordered_map<std::string, CommandLineOption<std::set<std::string>>>
357 listArgumentMap = {
358 { "-headers", { &m_headers, true } },
359 { "-generatedHeaders", { &m_generatedHeaders, true } },
360 { "-knownModules", { &m_knownModules, true } },
361 };
362
363 static const std::unordered_map<std::string, CommandLineOption<bool>> boolArgumentMap = {
364 { "-nonQt", { &m_isNonQtModule, true } }, { "-debug", { &m_debug, true } },
365 { "-help", { &m_printHelpOnly, true } },
366 { "-internal", { &m_isInternal, true } }, { "-all", { &m_scanAllMode, true } },
367 { "-copy", { &m_copy, true } }, { "-minimal", { &m_minimal, true } },
368 { "-showonly", { &m_showOnly, true } }, { "-showOnly", { &m_showOnly, true } },
369 { "-warningsAreErrors", { &m_warningsAreErrors, true } }
370 };
371
372 std::string *currentValue = nullptr;
373 std::set<std::string> *currentListValue = nullptr;
374
375 auto parseArgument = [&currentValue, &currentListValue](const std::string &arg) -> bool {
376 if (arg[0] == '-') {
377 currentValue = nullptr;
378 currentListValue = nullptr;
379 {
380 auto it = stringArgumentMap.find(arg);
381 if (it != stringArgumentMap.end()) {
382 if (it->second.value == nullptr) {
384 return false;
385 }
386 currentValue = it->second.value;
387 return true;
388 }
389 }
390
391 {
392 auto it = boolArgumentMap.find(arg);
393 if (it != boolArgumentMap.end()) {
394 if (it->second.value == nullptr) {
396 return false;
397 }
398 *(it->second.value) = true;
399 return true;
400 }
401 }
402
403 {
404 auto it = listArgumentMap.find(arg);
405 if (it != listArgumentMap.end()) {
406 if (it->second.value == nullptr) {
408 return false;
409 }
410 currentListValue = it->second.value;
411 currentListValue->insert(""); // Indicate that argument is provided
412 return true;
413 }
414 }
415
416 std::cerr << "Unknown argument: " << arg << std::endl;
417 return false;
418 }
419
420 if (currentValue != nullptr) {
421 *currentValue = arg;
422 currentValue = nullptr;
423 } else if (currentListValue != nullptr) {
424 currentListValue->insert(arg);
425 } else {
426 std::cerr << "Unknown argument: " << arg << std::endl;
427 return false;
428 }
429 return true;
430 };
431
432 for (int i = 1; i < argc; ++i) {
433 std::string arg(argv[i]);
434 if (arg.empty())
435 continue;
436
437 if (arg[0] == '@') {
438 std::ifstream ifs(arg.substr(1), std::ifstream::in);
439 if (!ifs.is_open()) {
440 std::cerr << "Unable to open rsp file: " << arg[0] << std::endl;
441 return false;
442 }
443 std::string argFromFile;
444 while (std::getline(ifs, argFromFile)) {
445 if (argFromFile.empty())
446 continue;
447 if (!parseArgument(argFromFile))
448 return false;
449 }
450 ifs.close();
451 continue;
452 }
453
454 if (!parseArgument(arg))
455 return false;
456 }
457
458 if (m_printHelpOnly)
459 return true;
460
461 if (!qpaHeadersFilter.empty())
462 m_qpaHeadersRegex = std::regex(qpaHeadersFilter);
463
464 if (!rhiHeadersFilter.empty())
465 m_rhiHeadersRegex = std::regex(rhiHeadersFilter);
466
467 if (!ssgHeadersFilter.empty())
468 m_ssgHeadersRegex = std::regex(ssgHeadersFilter);
469
470 if (!privateHeadersFilter.empty())
471 m_privateHeadersRegex = std::regex(privateHeadersFilter);
472
473 if (!publicNamespaceFilter.empty())
474 m_publicNamespaceRegex = std::regex(publicNamespaceFilter);
475
476 if (m_headers.empty() && !m_scanAllMode) {
477 std::cerr << "You need to specify either -headers or -all option." << std::endl;
478 return false;
479 }
480
481 if (!m_headers.empty() && m_scanAllMode) {
482 std::cerr << "Both -headers and -all are specified. Need to choose only one"
483 "operational mode." << std::endl;
484 return false;
485 }
486
487 for (const auto &argument : listArgumentMap)
488 argument.second.value->erase("");
489
490 bool ret = true;
491 ret &= checkRequiredArguments(stringArgumentMap);
492 ret &= checkRequiredArguments(listArgumentMap);
493
494 normilizePaths();
495
496 return ret;
497 }
498
499 // Convert all paths from command line to a generic one.
500 void normilizePaths()
501 {
502 static std::array<std::string *, 8> paths = {
503 &m_sourceDir, &m_binaryDir, &m_includeDir, &m_privateIncludeDir,
504 &m_qpaIncludeDir, &m_rhiIncludeDir, &m_stagingDir,
505 &m_versionScriptFile,
506 };
507 for (auto path : paths) {
508 if (!path->empty())
509 *path = utils::normilizedPath(*path).generic_string();
510 }
511 }
512
513 std::string m_moduleName;
514 std::string m_sourceDir;
515 std::string m_binaryDir;
516 std::string m_includeDir;
517 std::string m_privateIncludeDir;
518 std::string m_qpaIncludeDir;
519 std::string m_rhiIncludeDir;
520 std::string m_ssgIncludeDir;
521 std::string m_stagingDir;
522 std::string m_versionScriptFile;
523 std::set<std::string> m_knownModules;
524 std::set<std::string> m_headers;
525 std::set<std::string> m_generatedHeaders;
526 bool m_scanAllMode = false;
527 bool m_copy = false;
528 bool m_isNonQtModule = false;
529 bool m_isInternal = false;
530 bool m_printHelpOnly = false;
531 bool m_debug = false;
532 bool m_minimal = false;
533 bool m_showOnly = false;
534 bool m_warningsAreErrors = false;
535 std::regex m_qpaHeadersRegex;
536 std::regex m_rhiHeadersRegex;
537 std::regex m_ssgHeadersRegex;
538 std::regex m_privateHeadersRegex;
539 std::regex m_publicNamespaceRegex;
540
541 bool m_isValid;
542};
543
545{
546 class SymbolDescriptor
547 {
548 public:
549 // Where the symbol comes from
550 enum SourceType {
551 Pragma = 0, // pragma qt_class is mentioned a header file
552 Declaration, // The symbol declaration inside a header file
553 MaxSourceType
554 };
555
556 void update(const std::string &file, SourceType type)
557 {
558 if (type < m_type) {
559 m_file = file;
560 m_type = type;
561 }
562 }
563
564 // The file that contains a symbol.
565 const std::string &file() const { return m_file; }
566
567 private:
568 SourceType m_type = MaxSourceType;
569 std::string m_file;
570 };
571 using SymbolContainer = std::unordered_map<std::string, SymbolDescriptor>;
572
573 struct ParsingResult
574 {
575 std::vector<std::string> versionScriptContent;
576 std::string requireConfig;
577 bool masterInclude = true;
578 };
579
580 CommandLineOptions *m_commandLineArgs = nullptr;
581
582 std::map<std::string /* header file name */, std::string /* header feature guard name */,
584 m_masterHeaderContents;
585
586 std::unordered_map<std::string /* the deprecated header name*/,
587 std::string /* the replacement */>
588 m_deprecatedHeaders;
589 std::vector<std::string> m_versionScriptContents;
590 std::set<std::string> m_producedHeaders;
591 std::vector<std::string> m_headerCheckExceptions;
592 SymbolContainer m_symbols;
593 std::ostream &scannerDebug() const
594 {
595 if (m_commandLineArgs->debug())
596 return std::cout;
597 return utils::DummyOutput;
598 }
599
600 enum { Active, Stopped, IgnoreNext, Ignore } m_versionScriptGeneratorState = Active;
601
602 std::filesystem::path m_outputRootName;
603 std::filesystem::path m_currentFile;
604 std::string m_currentFilename;
605 std::string m_currentFileString;
606 size_t m_currentFileLineNumber = 0;
607 bool m_currentFileInSourceDir = false;
608
609 enum FileType { PublicHeader = 0, PrivateHeader = 1, QpaHeader = 2, ExportHeader = 4, RhiHeader = 8, SsgHeader = 16 };
610 unsigned int m_currentFileType = PublicHeader;
611
612 int m_criticalChecks = CriticalChecks;
613 std::string_view m_warningMessagePreamble;
614
615public:
617 : m_commandLineArgs(commandLineArgs),
618 m_masterHeaderContents(MasterHeaderIncludeComparator),
619 m_outputRootName(
620 std::filesystem::weakly_canonical(m_commandLineArgs->includeDir()).root_name()),
621 m_warningMessagePreamble(WarningMessagePreamble)
622 {
623 }
624
625 // The function converts the relative path to a header files to the absolute. It also makes the
626 // path canonical(removes '..' and '.' parts of the path). The source directory passed in
627 // '-sourceDir' command line argument is used as base path for relative paths to create the
628 // absolute path.
629 [[nodiscard]] std::filesystem::path makeHeaderAbsolute(const std::string &filename) const;
630
632 {
633 if (m_commandLineArgs->warningsAreErrors()) {
634 m_criticalChecks = AllChecks;
635 m_warningMessagePreamble = ErrorMessagePreamble;
636 }
637
638 m_versionScriptGeneratorState =
639 m_commandLineArgs->versionScriptFile().empty() ? Stopped : Active;
640 auto error = NoError;
641
642 // In the scan all mode we ingore the list of header files that is specified in the
643 // '-headers' argument, and collect header files from the source directory tree.
644 if (m_commandLineArgs->scanAllMode()) {
645 for (auto const &entry :
646 std::filesystem::recursive_directory_iterator(m_commandLineArgs->sourceDir())) {
647
648 const bool isRegularFile = entry.is_regular_file();
649 const bool isHeaderFlag = isHeader(entry);
650 const bool isDocFileHeuristicFlag =
651 isDocFileHeuristic(entry.path().generic_string());
652 const bool shouldProcessHeader =
653 isRegularFile && isHeaderFlag && !isDocFileHeuristicFlag;
654 const std::string filePath = entry.path().generic_string();
655
656 if (shouldProcessHeader) {
657 scannerDebug() << "Processing header: " << filePath << std::endl;
658 if (!processHeader(makeHeaderAbsolute(filePath)))
660 } else {
661 scannerDebug()
662 << "Skipping processing header: " << filePath
663 << " isRegularFile: " << isRegularFile
664 << " isHeaderFlag: " << isHeaderFlag
665 << " isDocFileHeuristicFlag: " << isDocFileHeuristicFlag
666 << std::endl;
667 }
668 }
669 } else {
670 // Since the list of header file is quite big syncqt supports response files to avoid
671 // the issues with long command lines.
672 std::set<std::string> rspHeaders;
673 const auto &headers = m_commandLineArgs->headers();
674 for (auto it = headers.begin(); it != headers.end(); ++it) {
675 const auto &header = *it;
676 scannerDebug() << "Processing header: " << header << std::endl;
679 }
680 }
681 for (const auto &header : rspHeaders) {
682 scannerDebug() << "Processing header: " << header << std::endl;
685 }
686 }
687
688 // No further processing in minimal mode.
689 if (m_commandLineArgs->minimal())
690 return error;
691
692 // Generate aliases for all unique symbols collected during the header files parsing.
693 for (auto it = m_symbols.begin(); it != m_symbols.end(); ++it) {
694 const std::string &filename = it->second.file();
695 if (!filename.empty()) {
697 m_commandLineArgs->includeDir() + '/' + it->first, filename)) {
698 m_producedHeaders.insert(it->first);
699 } else {
701 }
702 }
703 }
704
705 // Generate the header file containing version information.
706 if (!m_commandLineArgs->isNonQtModule()) {
707 std::string moduleNameLower = utils::asciiToLower(m_commandLineArgs->moduleName());
708 std::string versionHeaderFilename(moduleNameLower + "version.h");
709 std::string versionHeaderCamel(m_commandLineArgs->moduleName() + "Version");
710 std::string versionFile = m_commandLineArgs->includeDir() + '/' + versionHeaderFilename;
711
712 std::error_code ec;
713 FileStamp originalStamp = std::filesystem::last_write_time(versionFile, ec);
714 if (ec)
715 originalStamp = FileStamp::clock::now();
716
717 if (generateVersionHeader(versionFile)) {
719 m_commandLineArgs->includeDir() + '/' + versionHeaderCamel,
720 versionHeaderFilename, originalStamp)) {
722 }
723 m_masterHeaderContents[versionHeaderFilename] = {};
724 m_producedHeaders.insert(versionHeaderFilename);
725 m_producedHeaders.insert(versionHeaderCamel);
726 } else {
728 }
729 }
730
731 if (!m_commandLineArgs->scanAllMode()) {
732 if (!m_commandLineArgs->isNonQtModule()) {
735
738 }
739
740 if (!m_commandLineArgs->versionScriptFile().empty()) {
743 }
744 }
745
746 if (!m_commandLineArgs->isNonQtModule()) {
749 }
750
751 if (!m_commandLineArgs->scanAllMode() && !m_commandLineArgs->stagingDir().empty()) {
752 // Copy the generated files to a spearate staging directory to make the installation
753 // process eaiser.
754 if (!copyGeneratedHeadersToStagingDirectory(m_commandLineArgs->stagingDir()))
756 }
757 return error;
758 }
759
760 // The function copies files, that were generated while the sync procedure to a staging
761 // directory. This is necessary to simplify the installation of the generated files.
762 [[nodiscard]] bool copyGeneratedHeadersToStagingDirectory(const std::string &outputDirectory,
763 bool skipCleanup = false)
764 {
765 bool result = true;
766 bool outDirExists = false;
767 if (!utils::createDirectories(outputDirectory, "Unable to create staging directory",
768 &outDirExists))
769 return false;
770
771 if (outDirExists && !skipCleanup) {
772 try {
773 for (const auto &entry :
774 std::filesystem::recursive_directory_iterator(outputDirectory)) {
775 if (m_producedHeaders.find(entry.path().filename().generic_string())
776 == m_producedHeaders.end()) {
777 // Check if header file came from another module as result of the
778 // cross-module deprecation before removing it.
779 std::string firstLine;
780 {
781 std::ifstream input(entry.path(), std::ifstream::in);
782 if (input.is_open()) {
783 std::getline(input, firstLine);
784 input.close();
785 }
786 }
787 if (firstLine.find("#ifndef DEPRECATED_HEADER_"
788 + m_commandLineArgs->moduleName())
789 == 0
790 || firstLine.find("#ifndef DEPRECATED_HEADER_") != 0)
791 std::filesystem::remove(entry.path());
792 }
793 }
794 } catch (const std::filesystem::filesystem_error &fserr) {
795 utils::printFilesystemError(fserr, "Unable to clean the staging directory");
796 return false;
797 }
798 }
799
800 for (const auto &header : m_producedHeaders) {
801 std::filesystem::path src(m_commandLineArgs->includeDir() + '/' + header);
802 std::filesystem::path dst(outputDirectory + '/' + header);
803 if (!m_commandLineArgs->showOnly())
805 }
806 return result;
807 }
808
809 void resetCurrentFileInfoData(const std::filesystem::path &headerFile)
810 {
811 // This regex filters the generated '*exports.h' and '*exports_p.h' header files.
812 static const std::regex ExportsHeaderRegex("^q(.*)exports(_p)?\\.h$");
813
814 m_currentFile = headerFile;
815 m_currentFileLineNumber = 0;
816 m_currentFilename = m_currentFile.filename().generic_string();
817 m_currentFileType = PublicHeader;
818 m_currentFileString = m_currentFile.generic_string();
819 m_currentFileInSourceDir = m_currentFileString.find(m_commandLineArgs->sourceDir()) == 0;
820
821 if (isHeaderPrivate(m_currentFilename))
822 m_currentFileType = PrivateHeader;
823
824 if (isHeaderQpa(m_currentFilename))
825 m_currentFileType = QpaHeader | PrivateHeader;
826
827 if (isHeaderRhi(m_currentFilename))
828 m_currentFileType = RhiHeader | PrivateHeader;
829
830 if (isHeaderSsg(m_currentFilename))
831 m_currentFileType = SsgHeader | PrivateHeader;
832
833 if (std::regex_match(m_currentFilename, ExportsHeaderRegex))
834 m_currentFileType |= ExportHeader;
835 }
836
837 [[nodiscard]] bool processHeader(const std::filesystem::path &headerFile)
838 {
839 // This regex filters any paths that contain the '3rdparty' directory.
840 static const std::regex ThirdPartyFolderRegex("(^|.+/)3rdparty/.+");
841
842 // This regex filters '-config.h' and '-config_p.h' header files.
843 static const std::regex ConfigHeaderRegex("^(q|.+-)config(_p)?\\.h");
844
845 resetCurrentFileInfoData(headerFile);
846 // We assume that header files ouside of the module source or build directories do not
847 // belong to the module. Skip any processing.
848 if (!m_currentFileInSourceDir
849 && m_currentFileString.find(m_commandLineArgs->binaryDir()) != 0) {
850 scannerDebug() << "Header file: " << headerFile
851 << " is outside the sync directories. Skipping." << std::endl;
852 m_headerCheckExceptions.push_back(m_currentFileString);
853 return true;
854 }
855
856 // Check if a directory is passed as argument. That shouldn't happen, print error and exit.
857 if (m_currentFilename.empty()) {
858 std::cerr << "Header file name of " << m_currentFileString << "is empty" << std::endl;
859 return false;
860 }
861
862 std::error_code ec;
863 FileStamp originalStamp = std::filesystem::last_write_time(headerFile, ec);
864 if (ec)
865 originalStamp = FileStamp::clock::now();
866 ec.clear();
867
868 bool isPrivate = m_currentFileType & PrivateHeader;
869 bool isQpa = m_currentFileType & QpaHeader;
870 bool isRhi = m_currentFileType & RhiHeader;
871 bool isSsg = m_currentFileType & SsgHeader;
872 bool isExport = m_currentFileType & ExportHeader;
873 scannerDebug()
874 << "processHeader:start: " << headerFile
875 << " m_currentFilename: " << m_currentFilename
876 << " isPrivate: " << isPrivate
877 << " isQpa: " << isQpa
878 << " isRhi: " << isRhi
879 << " isSsg: " << isSsg
880 << std::endl;
881
882 // Chose the directory where to generate the header aliases or to copy header file if
883 // the '-copy' argument is passed.
884 std::string outputDir = m_commandLineArgs->includeDir();
885 if (isQpa)
886 outputDir = m_commandLineArgs->qpaIncludeDir();
887 else if (isRhi)
888 outputDir = m_commandLineArgs->rhiIncludeDir();
889 else if (isSsg)
890 outputDir = m_commandLineArgs->ssgIncludeDir();
891 else if (isPrivate)
892 outputDir = m_commandLineArgs->privateIncludeDir();
893
894 if (!utils::createDirectories(outputDir, "Unable to create output directory"))
895 return false;
896
897 bool headerFileExists = std::filesystem::exists(headerFile);
898
899 std::string aliasedFilepath = headerFile.generic_string();
900
901 std::string aliasPath = outputDir + '/' + m_currentFilename;
902
903 // If the '-copy' argument is passed, we copy the original file to a corresponding output
904 // directory otherwise we only create a header file alias that contains relative path to
905 // the original header file in the module source or build tree.
906 if (m_commandLineArgs->copy() && headerFileExists) {
907 if (!updateOrCopy(headerFile, aliasPath))
908 return false;
909 } else {
910 if (!generateAliasedHeaderFileIfTimestampChanged(aliasPath, aliasedFilepath,
911 originalStamp))
912 return false;
913 }
914
915 // No further processing in minimal mode.
916 if (m_commandLineArgs->minimal())
917 return true;
918
919 // Stop processing if header files doesn't exist. This happens at configure time, since
920 // either header files are generated later than syncqt is running or header files only
921 // generated at build time. These files will be processed at build time, if CMake files
922 // contain the correct dependencies between the missing header files and the module
923 // 'sync_headers' targets.
924 if (!headerFileExists) {
925 scannerDebug() << "Header file: " << headerFile
926 << " doesn't exist, but is added to syncqt scanning. Skipping.";
927 return true;
928 }
929
930 bool isGenerated = isHeaderGenerated(m_currentFileString);
931
932 // Make sure that we detect the '3rdparty' directory inside the source directory only,
933 // since full path to the Qt sources might contain '/3rdparty/' too.
934 bool is3rdParty = std::regex_match(
935 std::filesystem::relative(headerFile, m_commandLineArgs->sourceDir())
936 .generic_string(),
937 ThirdPartyFolderRegex);
938
939 // No processing of generated Qt config header files.
940 if (!std::regex_match(m_currentFilename, ConfigHeaderRegex)) {
941 unsigned int skipChecks = m_commandLineArgs->scanAllMode() ? AllChecks : NoChecks;
942
943 // Collect checks that should skipped for the header file.
944 if (m_commandLineArgs->isNonQtModule() || is3rdParty || isQpa || isRhi || isSsg
945 || !m_currentFileInSourceDir || isGenerated) {
946 skipChecks = AllChecks;
947 } else {
948 if (std::regex_match(m_currentFilename, GlobalHeaderRegex) || isExport)
949 skipChecks |= NamespaceChecks;
950
951 if (isHeaderPCH(m_currentFilename))
952 skipChecks |= WeMeantItChecks;
953
954 if (isPrivate) {
955 skipChecks |= NamespaceChecks;
956 skipChecks |= PrivateHeaderChecks;
957 skipChecks |= IncludeChecks;
958 } else {
959 skipChecks |= WeMeantItChecks;
960 }
961 }
962
963 ParsingResult parsingResult;
964 parsingResult.masterInclude = m_currentFileInSourceDir && !isExport && !is3rdParty
965 && !isQpa && !isRhi && !isSsg && !isPrivate && !isGenerated;
966 if (!parseHeader(headerFile, parsingResult, skipChecks)) {
967 scannerDebug() << "parseHeader failed: " << headerFile << std::endl;
968 return false;
969 }
970
971 // Record the private header file inside the version script content.
972 if (isPrivate && !m_commandLineArgs->versionScriptFile().empty()
973 && !parsingResult.versionScriptContent.empty()) {
974 m_versionScriptContents.insert(m_versionScriptContents.end(),
975 parsingResult.versionScriptContent.begin(),
976 parsingResult.versionScriptContent.end());
977 }
978
979 // Add the '#if QT_CONFIG(<feature>)' check for header files that supposed to be
980 // included into the module master header only if corresponding feature is enabled.
981 bool willBeInModuleMasterHeader = false;
982 if (!isQpa && !isRhi && !isSsg && !isPrivate) {
983 if (m_currentFilename.find('_') == std::string::npos
984 && parsingResult.masterInclude) {
985 m_masterHeaderContents[m_currentFilename] = parsingResult.requireConfig;
986 willBeInModuleMasterHeader = true;
987 }
988 }
989
990 scannerDebug()
991 << "processHeader:end: " << headerFile
992 << " is3rdParty: " << is3rdParty
993 << " isGenerated: " << isGenerated
994 << " m_currentFileInSourceDir: " << m_currentFileInSourceDir
995 << " willBeInModuleMasterHeader: " << willBeInModuleMasterHeader
996 << std::endl;
997 } else if (m_currentFilename == "qconfig.h") {
998 // Hardcode generating of QtConfig alias
999 updateSymbolDescriptor("QtConfig", "qconfig.h", SyncScanner::SymbolDescriptor::Pragma);
1000 }
1001
1002 return true;
1003 }
1004
1005 void parseVersionScriptContent(const std::string buffer, ParsingResult &result)
1006 {
1007 // This regex looks for the symbols that needs to be placed into linker version script.
1008 static const std::regex VersionScriptSymbolRegex(
1009 "^(?:struct|class)(?:\\s+Q_\\w*_EXPORT)?\\s+([\\w:]+)[^;]*(;$)?");
1010
1011 // This regex looks for the namespaces that needs to be placed into linker version script.
1012 static const std::regex VersionScriptNamespaceRegex(
1013 "^namespace\\s+Q_\\w+_EXPORT\\s+([\\w:]+).*");
1014
1015 // This regex filters the tailing colon from the symbol name.
1016 static const std::regex TrailingColonRegex("([\\w]+):$");
1017
1018 switch (m_versionScriptGeneratorState) {
1019 case Ignore:
1020 scannerDebug() << "line ignored: " << buffer << std::endl;
1021 m_versionScriptGeneratorState = Active;
1022 return;
1023 case Stopped:
1024 return;
1025 case IgnoreNext:
1026 m_versionScriptGeneratorState = Ignore;
1027 break;
1028 case Active:
1029 break;
1030 }
1031
1032 if (buffer.empty())
1033 return;
1034
1035 std::smatch match;
1036 std::string symbol;
1037 if (std::regex_match(buffer, match, VersionScriptSymbolRegex) && match[2].str().empty())
1038 symbol = match[1].str();
1039 else if (std::regex_match(buffer, match, VersionScriptNamespaceRegex))
1040 symbol = match[1].str();
1041
1042 if (std::regex_match(symbol, match, TrailingColonRegex))
1043 symbol = match[1].str();
1044
1045 // checkLineForSymbols(buffer, symbol);
1046 if (!symbol.empty() && symbol[symbol.size() - 1] != ';') {
1047 std::string relPath = m_currentFileInSourceDir
1048 ? std::filesystem::relative(m_currentFile, m_commandLineArgs->sourceDir())
1049 .string()
1050 : std::filesystem::relative(m_currentFile, m_commandLineArgs->binaryDir())
1051 .string();
1052
1053 std::string versionStringRecord = " *";
1054 size_t startPos = 0;
1055 size_t endPos = 0;
1056 while (endPos != std::string::npos) {
1057 endPos = symbol.find("::", startPos);
1058 size_t length = endPos != std::string::npos ? (endPos - startPos)
1059 : (symbol.size() - startPos);
1060 if (length > 0) {
1061 std::string symbolPart = symbol.substr(startPos, length);
1062 versionStringRecord += std::to_string(symbolPart.size());
1063 versionStringRecord += symbolPart;
1064 }
1065 startPos = endPos + 2;
1066 }
1067 versionStringRecord += "*;";
1068 if (versionStringRecord.size() < LinkerScriptCommentAlignment)
1069 versionStringRecord +=
1070 std::string(LinkerScriptCommentAlignment - versionStringRecord.size(), ' ');
1071 versionStringRecord += " # ";
1072 versionStringRecord += relPath;
1073 versionStringRecord += ":";
1074 versionStringRecord += std::to_string(m_currentFileLineNumber);
1075 versionStringRecord += "\n";
1076 result.versionScriptContent.push_back(versionStringRecord);
1077 }
1078 }
1079
1080 // The function parses 'headerFile' and collect artifacts that are used at generating step.
1081 // 'timeStamp' is saved in internal structures to compare it when generating files.
1082 // 'result' the function output value that stores the result of parsing.
1083 // 'skipChecks' checks that are not applicable for the header file.
1084 [[nodiscard]] bool parseHeader(const std::filesystem::path &headerFile,
1085 ParsingResult &result,
1086 unsigned int skipChecks)
1087 {
1088 if (m_commandLineArgs->showOnly())
1089 std::cout << headerFile << " [" << m_commandLineArgs->moduleName() << "]" << std::endl;
1090 // This regex checks if line contains a macro.
1091 static const std::regex MacroRegex("^\\s*#.*");
1092
1093 // The regex's bellow check line for known pragmas:
1094 //
1095 // - 'once' is not allowed in installed headers, so error out.
1096 //
1097 // - 'qt_sync_skip_header_check' avoid any header checks.
1098 //
1099 // - 'qt_sync_stop_processing' stops the header proccesing from a moment when pragma is
1100 // found. Important note: All the parsing artifacts were found before this point are
1101 // stored for further processing.
1102 //
1103 // - 'qt_sync_suspend_processing' pauses processing and skip lines inside a header until
1104 // 'qt_sync_resume_processing' is found. 'qt_sync_stop_processing' stops processing if
1105 // it's found before the 'qt_sync_resume_processing'.
1106 //
1107 // - 'qt_sync_resume_processing' resumes processing after 'qt_sync_suspend_processing'.
1108 //
1109 // - 'qt_class(<symbol>)' manually declares the 'symbol' that should be used to generate
1110 // the CaMeL case header alias.
1111 //
1112 // - 'qt_deprecates([module/]<deprecated header file>[,<major.minor>])' indicates that
1113 // this header file replaces the 'deprecated header file'. syncqt will create the
1114 // deprecated header file' with the special deprecation content. Pragma optionally
1115 // accepts the Qt version where file should be removed. If the current Qt version is
1116 // higher than the deprecation version, syncqt displays deprecation warning and skips
1117 // generating the deprecated header. If the module is specified and is different from
1118 // the one this header file belongs to, syncqt attempts to generate header files
1119 // for the specified module. Cross-module deprecation only works within the same repo.
1120 // See the 'generateDeprecatedHeaders' function for details.
1121 //
1122 // - 'qt_no_master_include' indicates that syncqt should avoid including this header
1123 // files into the module master header file.
1124 static const std::regex OnceRegex(R"(^#\s*pragma\s+once$)");
1125 static const std::regex SkipHeaderCheckRegex("^#\\s*pragma qt_sync_skip_header_check$");
1126 static const std::regex StopProcessingRegex("^#\\s*pragma qt_sync_stop_processing$");
1127 static const std::regex SuspendProcessingRegex("^#\\s*pragma qt_sync_suspend_processing$");
1128 static const std::regex ResumeProcessingRegex("^#\\s*pragma qt_sync_resume_processing$");
1129 static const std::regex ExplixitClassPragmaRegex("^#\\s*pragma qt_class\\(([^\\)]+)\\)$");
1130 static const std::regex DeprecatesPragmaRegex("^#\\s*pragma qt_deprecates\\(([^\\)]+)\\)$");
1131 static const std::regex NoMasterIncludePragmaRegex("^#\\s*pragma qt_no_master_include$");
1132
1133 // This regex checks if header contains 'We mean it' disclaimer. All private headers should
1134 // contain them.
1135 static const std::string_view WeMeantItString("We mean it.");
1136
1137 // The regex's check if the content of header files is wrapped with the Qt namespace macros.
1138 static const std::regex BeginNamespaceRegex("^QT_BEGIN_NAMESPACE(_[A-Z_]+)?$");
1139 static const std::regex EndNamespaceRegex("^QT_END_NAMESPACE(_[A-Z_]+)?$");
1140
1141 // This regex checks if line contains the include macro of the following formats:
1142 // - #include <file>
1143 // - #include "file"
1144 // - # include <file>
1145 static const std::regex IncludeRegex("^#\\s*include\\s*[<\"](.+)[>\"]");
1146
1147 // This regex checks if line contains namespace definition.
1148 static const std::regex NamespaceRegex("\\s*namespace ([^ ]*)\\s+");
1149
1150 // This regex checks if line contains the Qt iterator declaration, that need to have
1151 // CaMel case header alias.
1152 static const std::regex DeclareIteratorRegex("^ *Q_DECLARE_\\w*ITERATOR\\((\\w+)\\);?$");
1153
1154 // This regex checks if header file contains the QT_REQUIRE_CONFIG call.
1155 // The macro argument is used to wrap an include of the header file inside the module master
1156 // header file with the '#if QT_CONFIG(<feature>)' guard.
1157 static const std::regex RequireConfigRegex("^ *QT_REQUIRE_CONFIG\\((\\w+)\\);?$");
1158
1159 // This regex looks for the ELFVERSION tag this is control key-word for the version script
1160 // content processing.
1161 // ELFVERSION tag accepts the following values:
1162 // - stop - stops the symbols lookup for a version script starting from this line.
1163 // - ignore-next - ignores the line followed by the current one.
1164 // - ignore - ignores the current line.
1165 static const std::regex ElfVersionTagRegex(".*ELFVERSION:(stop|ignore-next|ignore).*");
1166
1167 std::ifstream input(headerFile, std::ifstream::in);
1168 if (!input.is_open()) {
1169 std::cerr << "Unable to open " << headerFile << std::endl;
1170 return false;
1171 }
1172
1173 bool hasQtBeginNamespace = false;
1174 std::string qtBeginNamespace;
1175 std::string qtEndNamespace;
1176 bool hasWeMeantIt = false;
1177 bool isSuspended = false;
1178 bool isMultiLineComment = false;
1179 std::size_t bracesDepth = 0;
1180 std::size_t namespaceCount = 0;
1181 std::string namespaceString;
1182
1183 std::smatch match;
1184
1185 std::string buffer;
1186 std::string line;
1187 std::string tmpLine;
1188 std::size_t linesProcessed = 0;
1189 int faults = NoChecks;
1190
1191 const auto error = [&] () -> decltype(auto) {
1192 return std::cerr << ErrorMessagePreamble << m_currentFileString
1193 << ":" << m_currentFileLineNumber << " ";
1194 };
1195
1196 // Read file line by line
1197 while (std::getline(input, tmpLine)) {
1198 ++m_currentFileLineNumber;
1199 line.append(tmpLine);
1200 if (line.empty() || line.at(line.size() - 1) == '\\') {
1201 continue;
1202 }
1203 buffer.clear();
1204 buffer.reserve(line.size());
1205 // Optimize processing by looking for a special sequences such as:
1206 // - start-end of comments
1207 // - start-end of class/structures
1208 // And avoid processing of the the data inside these blocks.
1209 for (std::size_t i = 0; i < line.size(); ++i) {
1210 if (line[i] == '\r')
1211 continue;
1212 if (bracesDepth == namespaceCount) {
1213 if (line[i] == '/') {
1214 if ((i + 1) < line.size()) {
1215 if (line[i + 1] == '*') {
1216 isMultiLineComment = true;
1217 continue;
1218 } else if (line[i + 1] == '/') { // Single line comment
1219 if (!(skipChecks & WeMeantItChecks)
1220 && line.find(WeMeantItString) != std::string::npos) {
1221 hasWeMeantIt = true;
1222 continue;
1223 }
1224 if (m_versionScriptGeneratorState != Stopped
1225 && std::regex_match(line, match, ElfVersionTagRegex)) {
1226 if (match[1].str() == "ignore")
1227 m_versionScriptGeneratorState = Ignore;
1228 else if (match[1].str() == "ignore-next")
1229 m_versionScriptGeneratorState = IgnoreNext;
1230 else if (match[1].str() == "stop")
1231 m_versionScriptGeneratorState = Stopped;
1232 }
1233 break;
1234 }
1235 }
1236 } else if (line[i] == '*' && (i + 1) < line.size() && line[i + 1] == '/') {
1237 ++i;
1238 isMultiLineComment = false;
1239 continue;
1240 }
1241 }
1242
1243 if (isMultiLineComment) {
1244 if (!(skipChecks & WeMeantItChecks) &&
1245 line.find(WeMeantItString) != std::string::npos) {
1246 hasWeMeantIt = true;
1247 continue;
1248 }
1249 continue;
1250 }
1251
1252 if (line[i] == '{') {
1253 if (std::regex_match(buffer, match, NamespaceRegex)) {
1254 ++namespaceCount;
1255 namespaceString += "::";
1256 namespaceString += match[1].str();
1257 }
1258 ++bracesDepth;
1259 continue;
1260 } else if (line[i] == '}') {
1261 if (namespaceCount > 0 && bracesDepth == namespaceCount) {
1262 namespaceString.resize(namespaceString.rfind("::"));
1263 --namespaceCount;
1264 }
1265 --bracesDepth;
1266 } else if (bracesDepth == namespaceCount) {
1267 buffer += line[i];
1268 }
1269 }
1270 line.clear();
1271
1272 scannerDebug() << m_currentFilename << ": " << buffer << std::endl;
1273
1274 if (m_currentFileType & PrivateHeader) {
1276 }
1277
1278 if (buffer.empty())
1279 continue;
1280
1281 ++linesProcessed;
1282
1283 bool skipSymbols =
1284 (m_currentFileType & PrivateHeader) || (m_currentFileType & QpaHeader) || (m_currentFileType & RhiHeader)
1285 || (m_currentFileType & SsgHeader);
1286
1287 // Parse pragmas
1288 if (std::regex_match(buffer, MacroRegex)) {
1289 if (std::regex_match(buffer, SkipHeaderCheckRegex)) {
1290 skipChecks = AllChecks;
1291 faults = NoChecks;
1292 } else if (std::regex_match(buffer, StopProcessingRegex)) {
1293 if (skipChecks == AllChecks)
1294 m_headerCheckExceptions.push_back(m_currentFileString);
1295 return true;
1296 } else if (std::regex_match(buffer, SuspendProcessingRegex)) {
1297 isSuspended = true;
1298 } else if (std::regex_match(buffer, ResumeProcessingRegex)) {
1299 isSuspended = false;
1300 } else if (std::regex_match(buffer, match, ExplixitClassPragmaRegex)) {
1301 if (!skipSymbols) {
1302 updateSymbolDescriptor(match[1].str(), m_currentFilename,
1303 SymbolDescriptor::Pragma);
1304 } else {
1305 // TODO: warn about skipping symbols that are defined explicitly
1306 }
1307 } else if (std::regex_match(buffer, NoMasterIncludePragmaRegex)) {
1308 result.masterInclude = false;
1309 } else if (std::regex_match(buffer, match, DeprecatesPragmaRegex)) {
1310 m_deprecatedHeaders[match[1].str()] =
1311 m_commandLineArgs->moduleName() + '/' + m_currentFilename;
1312 } else if (std::regex_match(buffer, OnceRegex)) {
1313 if (!(skipChecks & PragmaOnceChecks)) {
1314 faults |= PragmaOnceChecks;
1315 error() << "\"#pragma once\" is not allowed in installed header files: "
1316 "https://lists.qt-project.org/pipermail/development/2022-October/043121.html"
1317 << std::endl;
1318 }
1319 } else if (std::regex_match(buffer, match, IncludeRegex) && !isSuspended) {
1320 if (!(skipChecks & IncludeChecks)) {
1321 std::string includedHeader = match[1].str();
1322 if (!(skipChecks & PrivateHeaderChecks)
1323 && isHeaderPrivate(std::filesystem::path(includedHeader)
1324 .filename()
1325 .generic_string())) {
1326 faults |= PrivateHeaderChecks;
1327 error() << "includes private header " << includedHeader << std::endl;
1328 }
1329 for (const auto &module : m_commandLineArgs->knownModules()) {
1330 std::string suggestedHeader = "Qt" + module + '/' + includedHeader;
1331 if (std::filesystem::exists(m_commandLineArgs->includeDir() + "/../"
1332 + suggestedHeader)) {
1333 faults |= IncludeChecks;
1334 std::cerr << m_warningMessagePreamble << m_currentFileString
1335 << ":" << m_currentFileLineNumber
1336 << " includes " << includedHeader
1337 << " when it should include "
1338 << suggestedHeader << std::endl;
1339 }
1340 }
1341 }
1342 }
1343 continue;
1344 }
1345
1346 // Logic below this line is affected by the 'qt_sync_suspend_processing' and
1347 // 'qt_sync_resume_processing' pragmas.
1348 if (isSuspended)
1349 continue;
1350
1351 // Look for the symbols in header file.
1352 if (!skipSymbols) {
1353 std::string symbol;
1354 if (checkLineForSymbols(buffer, symbol)) {
1355 if (namespaceCount == 0
1356 || std::regex_match(namespaceString,
1357 m_commandLineArgs->publicNamespaceRegex())) {
1358 updateSymbolDescriptor(symbol, m_currentFilename,
1359 SymbolDescriptor::Declaration);
1360 }
1361 continue;
1362 } else if (std::regex_match(buffer, match, DeclareIteratorRegex)) {
1363 std::string iteratorSymbol = match[1].str() + "Iterator";
1364 updateSymbolDescriptor(std::string("Q") + iteratorSymbol, m_currentFilename,
1365 SymbolDescriptor::Declaration);
1366 updateSymbolDescriptor(std::string("QMutable") + iteratorSymbol,
1367 m_currentFilename, SymbolDescriptor::Declaration);
1368 continue;
1369 } else if (std::regex_match(buffer, match, RequireConfigRegex)) {
1370 result.requireConfig = match[1].str();
1371 continue;
1372 }
1373 }
1374
1375 // Check for both QT_BEGIN_NAMESPACE and QT_END_NAMESPACE macros are present in the
1376 // header file.
1377 if (!(skipChecks & NamespaceChecks)) {
1378 if (std::regex_match(buffer, match, BeginNamespaceRegex)) {
1379 qtBeginNamespace = match[1].str();
1380 hasQtBeginNamespace = true;
1381 } else if (std::regex_match(buffer, match, EndNamespaceRegex)) {
1382 qtEndNamespace = match[1].str();
1383 }
1384 }
1385 }
1386 input.close();
1387
1388 // Error out if namespace checks are failed.
1389 if (!(skipChecks & NamespaceChecks)) {
1390 if (hasQtBeginNamespace) {
1391 if (qtBeginNamespace != qtEndNamespace) {
1392 faults |= NamespaceChecks;
1393 std::cerr << m_warningMessagePreamble << m_currentFileString
1394 << " the begin namespace macro QT_BEGIN_NAMESPACE" << qtBeginNamespace
1395 << " doesn't match the end namespace macro QT_END_NAMESPACE"
1396 << qtEndNamespace << std::endl;
1397 }
1398 } else {
1399 faults |= NamespaceChecks;
1400 std::cerr << m_warningMessagePreamble << m_currentFileString
1401 << " does not include QT_BEGIN_NAMESPACE" << std::endl;
1402 }
1403 }
1404
1405 if (!(skipChecks & WeMeantItChecks) && !hasWeMeantIt) {
1406 faults |= WeMeantItChecks;
1407 std::cerr << m_warningMessagePreamble << m_currentFileString
1408 << " does not have the \"We mean it.\" warning"
1409 << std::endl;
1410 }
1411
1412 scannerDebug() << "linesTotal: " << m_currentFileLineNumber
1413 << " linesProcessed: " << linesProcessed << std::endl;
1414
1415 if (skipChecks == AllChecks)
1416 m_headerCheckExceptions.push_back(m_currentFileString);
1417
1418 // Exit with an error if any of critical checks are present.
1419 return !(faults & m_criticalChecks);
1420 }
1421
1422 // The function checks if line contains the symbol that needs to have a CaMeL-style alias.
1423 [[nodiscard]] bool checkLineForSymbols(const std::string &line, std::string &symbol)
1424 {
1425 scannerDebug() << "checkLineForSymbols: " << line << std::endl;
1426
1427 // This regex checks if line contains class or structure declaration like:
1428 // - <class|stuct> StructName
1429 // - template <> class ClassName
1430 // - class ClassName : [public|protected|private] BaseClassName
1431 // - class ClassName [final|Q_DECL_FINAL|sealed]
1432 // And possible combinations of the above variants.
1433 static const std::regex ClassRegex(
1434 "^ *(template *<.*> *)?(class|struct) +([^ <>]* "
1435 "+)?((?!Q_DECL_FINAL|final|sealed)[^<\\s\\:]+) ?(<[^>\\:]*> "
1436 "?)?\\s*(?:Q_DECL_FINAL|final|sealed)?\\s*((,|:)\\s*(public|protected|private)? "
1437 "*.*)? *$");
1438
1439 // This regex checks if line contains function pointer typedef declaration like:
1440 // - typedef void (* QFunctionPointerType)(int, char);
1441 static const std::regex FunctionPointerRegex(
1442 "^ *typedef *.*\\(\\*(Q[^\\)]+)\\)\\(.*\\); *");
1443
1444 // This regex checks if line contains class or structure typedef declaration like:
1445 // - typedef AnySymbol<char> QAnySymbolType;
1446 static const std::regex TypedefRegex("^ *typedef\\s+(.*)\\s+(Q\\w+); *$");
1447
1448 // This regex checks if symbols is the Qt public symbol. Assume that Qt public symbols start
1449 // with the capital 'Q'.
1450 static const std::regex QtClassRegex("^Q\\w+$");
1451
1452 std::smatch match;
1453 if (std::regex_match(line, match, FunctionPointerRegex)) {
1454 symbol = match[1].str();
1455 } else if (std::regex_match(line, match, TypedefRegex)) {
1456 symbol = match[2].str();
1457 } else if (std::regex_match(line, match, ClassRegex)) {
1458 symbol = match[4].str();
1459 if (!std::regex_match(symbol, QtClassRegex))
1460 symbol.clear();
1461 } else {
1462 return false;
1463 }
1464 return !symbol.empty();
1465 }
1466
1467 [[nodiscard]] bool isHeaderQpa(const std::string &headerFileName)
1468 {
1469 return std::regex_match(headerFileName, m_commandLineArgs->qpaHeadersRegex());
1470 }
1471
1472 [[nodiscard]] bool isHeaderRhi(const std::string &headerFileName)
1473 {
1474 return std::regex_match(headerFileName, m_commandLineArgs->rhiHeadersRegex());
1475 }
1476
1477 [[nodiscard]] bool isHeaderSsg(const std::string &headerFileName)
1478 {
1479 return std::regex_match(headerFileName, m_commandLineArgs->ssgHeadersRegex());
1480 }
1481
1482 [[nodiscard]] bool isHeaderPrivate(const std::string &headerFile)
1483 {
1484 return std::regex_match(headerFile, m_commandLineArgs->privateHeadersRegex());
1485 }
1486
1487 [[nodiscard]] bool isHeaderPCH(const std::string &headerFilename)
1488 {
1489 static const std::string pchSuffix("_pch.h");
1490 return headerFilename.find(pchSuffix, headerFilename.size() - pchSuffix.size())
1491 != std::string::npos;
1492 }
1493
1494 [[nodiscard]] bool isHeader(const std::filesystem::path &path)
1495 {
1496 return path.extension().string() == ".h";
1497 }
1498
1499 [[nodiscard]] bool isDocFileHeuristic(const std::string &headerFilePath)
1500 {
1501 return headerFilePath.find("/doc/") != std::string::npos;
1502 }
1503
1504 [[nodiscard]] bool isHeaderGenerated(const std::string &header)
1505 {
1506 return m_commandLineArgs->generatedHeaders().find(header)
1507 != m_commandLineArgs->generatedHeaders().end();
1508 }
1509
1510 [[nodiscard]] bool generateQtCamelCaseFileIfContentChanged(const std::string &outputFilePath,
1511 const std::string &aliasedFilePath);
1512
1514 const std::string &outputFilePath, const std::string &aliasedFilePath,
1515 const FileStamp &originalStamp = FileStamp::clock::now());
1516
1517 bool writeIfDifferent(const std::string &outputFile, const std::string &buffer);
1518
1519 [[nodiscard]] bool generateMasterHeader()
1520 {
1521 if (m_masterHeaderContents.empty())
1522 return true;
1523
1524 std::string outputFile =
1525 m_commandLineArgs->includeDir() + '/' + m_commandLineArgs->moduleName();
1526
1527 std::string moduleUpper = utils::asciiToUpper(m_commandLineArgs->moduleName());
1528 std::stringstream buffer;
1529 buffer << "#ifndef QT_" << moduleUpper << "_MODULE_H\n"
1530 << "#define QT_" << moduleUpper << "_MODULE_H\n"
1531 << "#include <" << m_commandLineArgs->moduleName() << "/"
1532 << m_commandLineArgs->moduleName() << "Depends>\n";
1533 for (const auto &headerContents : m_masterHeaderContents) {
1534 if (!headerContents.second.empty()) {
1535 buffer << "#if QT_CONFIG(" << headerContents.second << ")\n"
1536 << "#include \"" << headerContents.first << "\"\n"
1537 << "#endif\n";
1538 } else {
1539 buffer << "#include \"" << headerContents.first << "\"\n";
1540 }
1541 }
1542 buffer << "#endif\n";
1543
1544 m_producedHeaders.insert(m_commandLineArgs->moduleName());
1545 return writeIfDifferent(outputFile, buffer.str());
1546 }
1547
1548 [[nodiscard]] bool generateVersionHeader(const std::string &outputFile)
1549 {
1550 std::string moduleNameUpper = utils::asciiToUpper( m_commandLineArgs->moduleName());
1551
1552 std::stringstream buffer;
1553 buffer << "/* This file was generated by syncqt. */\n"
1554 << "#ifndef QT_" << moduleNameUpper << "_VERSION_H\n"
1555 << "#define QT_" << moduleNameUpper << "_VERSION_H\n\n"
1556 << "#define " << moduleNameUpper << "_VERSION_STR \"" << QT_VERSION_STR << "\"\n\n"
1557 << "#define " << moduleNameUpper << "_VERSION "
1558 << "0x0" << QT_VERSION_MAJOR << "0" << QT_VERSION_MINOR << "0" << QT_VERSION_PATCH
1559 << "\n\n"
1560 << "#endif // QT_" << moduleNameUpper << "_VERSION_H\n";
1561
1562 return writeIfDifferent(outputFile, buffer.str());
1563 }
1564
1565 [[nodiscard]] bool generateDeprecatedHeaders()
1566 {
1567 static std::regex cIdentifierSymbolsRegex("[^a-zA-Z0-9_]");
1568 static std::string guard_base = "DEPRECATED_HEADER_" + m_commandLineArgs->moduleName();
1569 bool result = true;
1570 for (auto it = m_deprecatedHeaders.begin(); it != m_deprecatedHeaders.end(); ++it) {
1571 const std::string &descriptor = it->first;
1572 const std::string &replacement = it->second;
1573
1574 const auto separatorPos = descriptor.find(',');
1575 std::string headerPath = descriptor.substr(0, separatorPos);
1576 std::string versionDisclaimer;
1577 if (separatorPos != std::string::npos) {
1578 std::string version = descriptor.substr(separatorPos + 1);
1579 versionDisclaimer = " and will be removed in Qt " + version;
1580 int minor = 0;
1581 int major = 0;
1582 if (!utils::parseVersion(version, major, minor)) {
1583 std::cerr << ErrorMessagePreamble
1584 << "Invalid version format specified for the deprecated header file "
1585 << headerPath << ": '" << version
1586 << "'. Expected format: 'major.minor'.\n";
1587 result = false;
1588 continue;
1589 }
1590
1591 if (QT_VERSION_MAJOR > major
1592 || (QT_VERSION_MAJOR == major && QT_VERSION_MINOR >= minor)) {
1593 std::cerr << WarningMessagePreamble << headerPath
1594 << " is marked as deprecated and will not be generated in Qt "
1595 << QT_VERSION_STR
1596 << ". The respective qt_deprecates pragma needs to be removed.\n";
1597 continue;
1598 }
1599 }
1600
1601 const auto moduleSeparatorPos = headerPath.find('/');
1602 std::string headerName = moduleSeparatorPos != std::string::npos
1603 ? headerPath.substr(moduleSeparatorPos + 1)
1604 : headerPath;
1605 const std::string moduleName = moduleSeparatorPos != std::string::npos
1606 ? headerPath.substr(0, moduleSeparatorPos)
1607 : m_commandLineArgs->moduleName();
1608
1609 bool isCrossModuleDeprecation = moduleName != m_commandLineArgs->moduleName();
1610
1611 std::string qualifiedHeaderName =
1612 std::regex_replace(headerName, cIdentifierSymbolsRegex, "_");
1613 std::string guard = guard_base + "_" + qualifiedHeaderName;
1614 std::string warningText = "Header <" + moduleName + "/" + headerName + "> is deprecated"
1615 + versionDisclaimer + ". Please include <" + replacement + "> instead.";
1616 std::stringstream buffer;
1617 buffer << "#ifndef " << guard << "\n"
1618 << "#define " << guard << "\n"
1619 << "#if defined(__GNUC__)\n"
1620 << "# warning " << warningText << "\n"
1621 << "#elif defined(_MSC_VER)\n"
1622 << "# pragma message (\"" << warningText << "\")\n"
1623 << "#endif\n"
1624 << "#include <" << replacement << ">\n"
1625 << "#endif\n";
1626
1627 const std::string outputDir = isCrossModuleDeprecation
1628 ? m_commandLineArgs->includeDir() + "/../" + moduleName
1629 : m_commandLineArgs->includeDir();
1630 writeIfDifferent(outputDir + '/' + headerName, buffer.str());
1631
1632 // Add header file to staging installation directory for cross-module deprecation case.
1633 if (isCrossModuleDeprecation) {
1634 const std::string stagingDir = outputDir + "/.syncqt_staging/";
1635 writeIfDifferent(stagingDir + headerName, buffer.str());
1636 }
1637 m_producedHeaders.insert(headerName);
1638 }
1639 return result;
1640 }
1641
1643 {
1644 std::stringstream buffer;
1645 for (const auto &header : m_headerCheckExceptions)
1646 buffer << header << ";";
1647 return writeIfDifferent(m_commandLineArgs->binaryDir() + '/'
1648 + m_commandLineArgs->moduleName()
1649 + "_header_check_exceptions",
1650 buffer.str());
1651 }
1652
1653 [[nodiscard]] bool generateLinkerVersionScript()
1654 {
1655 std::stringstream buffer;
1656 for (const auto &content : m_versionScriptContents)
1657 buffer << content;
1658 return writeIfDifferent(m_commandLineArgs->versionScriptFile(), buffer.str());
1659 }
1660
1661 bool updateOrCopy(const std::filesystem::path &src, const std::filesystem::path &dst) noexcept;
1662 void updateSymbolDescriptor(const std::string &symbol, const std::string &file,
1663 SymbolDescriptor::SourceType type);
1664};
1665
1666// The function updates information about the symbol:
1667// - The path and modification time of the file where the symbol was found.
1668// - The source of finding
1669// Also displays a short info about a symbol in show only mode.
1670void SyncScanner::updateSymbolDescriptor(const std::string &symbol, const std::string &file,
1671 SymbolDescriptor::SourceType type)
1672{
1673 if (m_commandLineArgs->showOnly())
1674 std::cout << " SYMBOL: " << symbol << std::endl;
1675 m_symbols[symbol].update(file, type);
1676}
1677
1678[[nodiscard]] std::filesystem::path
1679SyncScanner::makeHeaderAbsolute(const std::string &filename) const
1680{
1681 if (std::filesystem::path(filename).is_relative())
1682 return utils::normilizedPath(m_commandLineArgs->sourceDir() + '/' + filename);
1683
1684 return utils::normilizedPath(filename);
1685}
1686
1687bool SyncScanner::updateOrCopy(const std::filesystem::path &src,
1688 const std::filesystem::path &dst) noexcept
1689{
1690 if (m_commandLineArgs->showOnly())
1691 return true;
1692
1693 if (src == dst) {
1694 std::cout << "Source and destination paths are same when copying " << src.string()
1695 << ". Skipping." << std::endl;
1696 return true;
1697 }
1698
1699 std::error_code ec;
1700 std::filesystem::copy(src, dst, std::filesystem::copy_options::update_existing, ec);
1701 if (ec) {
1702 ec.clear();
1703 std::filesystem::remove(dst, ec);
1704 if (ec) {
1705 // On some file systems(e.g. vboxfs) the std::filesystem::copy doesn't support
1706 // std::filesystem::copy_options::overwrite_existing remove file first and then copy.
1707 std::cerr << "Unable to remove file: " << src << " to " << dst << " error: ("
1708 << ec.value() << ")" << ec.message() << std::endl;
1709 return false;
1710 }
1711
1712 std::filesystem::copy(src, dst, std::filesystem::copy_options::overwrite_existing, ec);
1713 if (ec) {
1714 std::cerr << "Unable to copy file: " << src << " to " << dst << " error: ("
1715 << ec.value() << ")" << ec.message() << std::endl;
1716 return false;
1717 }
1718 }
1719 return true;
1720}
1721
1722// The function generates Qt CaMeL-case files.
1723// CaMeL-case files can have timestamp that is the same as or newer than timestamp of file that
1724// supposed to included there. It's not an issue when we generate regular aliases because the
1725// content of aliases is always the same, but we only want to "touch" them when content of original
1726// is changed.
1727bool SyncScanner::generateQtCamelCaseFileIfContentChanged(const std::string &outputFilePath,
1728 const std::string &aliasedFilePath)
1729{
1730 if (m_commandLineArgs->showOnly())
1731 return true;
1732
1733 std::string buffer = "#include \"";
1734 buffer += aliasedFilePath;
1735 buffer += "\" // IWYU pragma: export\n";
1736
1737 return writeIfDifferent(outputFilePath, buffer);
1738}
1739
1740// The function generates aliases for files in source tree. Since the content of these aliases is
1741// always same, it's ok to check only timestamp and touch files in case if stamp of original is
1742// newer than the timestamp of an alias.
1744 const std::string &aliasedFilePath,
1745 const FileStamp &originalStamp)
1746{
1747 if (m_commandLineArgs->showOnly())
1748 return true;
1749
1750 if (std::filesystem::exists({ outputFilePath })
1751 && std::filesystem::last_write_time({ outputFilePath }) >= originalStamp) {
1752 return true;
1753 }
1754 scannerDebug() << "Rewrite " << outputFilePath << std::endl;
1755
1756 std::ofstream ofs;
1757 ofs.open(outputFilePath, std::ofstream::out | std::ofstream::trunc);
1758 if (!ofs.is_open()) {
1759 std::cerr << "Unable to write header file alias: " << outputFilePath << std::endl;
1760 return false;
1761 }
1762 ofs << "#include \"" << aliasedFilePath << "\" // IWYU pragma: export\n";
1763 ofs.close();
1764 return true;
1765}
1766
1767bool SyncScanner::writeIfDifferent(const std::string &outputFile, const std::string &buffer)
1768{
1769 if (m_commandLineArgs->showOnly())
1770 return true;
1771
1772 static const std::streamsize bufferSize = 1025;
1773 bool differs = false;
1774 std::filesystem::path outputFilePath(outputFile);
1775
1776 std::string outputDirectory = outputFilePath.parent_path().string();
1777
1778 if (!utils::createDirectories(outputDirectory, "Unable to create output directory"))
1779 return false;
1780
1781 auto expectedSize = buffer.size();
1782#ifdef _WINDOWS
1783 // File on disk has \r\n instead of just \n
1784 expectedSize += std::count(buffer.begin(), buffer.end(), '\n');
1785#endif
1786
1787 if (std::filesystem::exists(outputFilePath)
1788 && expectedSize == std::filesystem::file_size(outputFilePath)) {
1789 char rdBuffer[bufferSize];
1790 memset(rdBuffer, 0, bufferSize);
1791
1792 std::ifstream ifs(outputFile, std::fstream::in);
1793 if (!ifs.is_open()) {
1794 std::cerr << "Unable to open " << outputFile << " for comparison." << std::endl;
1795 return false;
1796 }
1797 std::streamsize currentPos = 0;
1798
1799 std::size_t bytesRead = 0;
1800 do {
1801 ifs.read(rdBuffer, bufferSize - 1); // Read by 1K
1802 bytesRead = ifs.gcount();
1803 if (buffer.compare(currentPos, bytesRead, rdBuffer) != 0) {
1804 differs = true;
1805 break;
1806 }
1807 currentPos += bytesRead;
1808 memset(rdBuffer, 0, bufferSize);
1809 } while (bytesRead > 0);
1810
1811 ifs.close();
1812 } else {
1813 differs = true;
1814 }
1815
1816 scannerDebug() << "Update: " << outputFile << " " << differs << std::endl;
1817 if (differs) {
1818 std::ofstream ofs;
1819 ofs.open(outputFilePath, std::fstream::out | std::ofstream::trunc);
1820 if (!ofs.is_open()) {
1821 std::cerr << "Unable to write header content to " << outputFilePath << std::endl;
1822 return false;
1823 }
1824 ofs << buffer;
1825
1826 ofs.close();
1827 }
1828 return true;
1829}
1830
1831int main(int argc, char *argv[])
1832{
1833 CommandLineOptions options(argc, argv);
1834 if (!options.isValid())
1835 return InvalidArguments;
1836
1837 if (options.printHelpOnly()) {
1838 options.printHelp();
1839 return NoError;
1840 }
1841
1842 SyncScanner scanner = SyncScanner(&options);
1843 return scanner.sync();
1844}
const std::string & rhiIncludeDir() const
Definition main.cpp:212
const std::string & privateIncludeDir() const
Definition main.cpp:208
const std::string & sourceDir() const
Definition main.cpp:202
bool copy() const
Definition main.cpp:246
const std::string & binaryDir() const
Definition main.cpp:204
bool minimal() const
Definition main.cpp:248
const std::string & ssgIncludeDir() const
Definition main.cpp:214
const std::set< std::string > & generatedHeaders() const
Definition main.cpp:234
bool isValid() const
Definition main.cpp:198
const std::string & moduleName() const
Definition main.cpp:200
bool isInternal() const
Definition main.cpp:238
const std::string & versionScriptFile() const
Definition main.cpp:218
const std::regex & publicNamespaceRegex() const
Definition main.cpp:230
const std::string & stagingDir() const
Definition main.cpp:216
const std::string & qpaIncludeDir() const
Definition main.cpp:210
const std::set< std::string > & knownModules() const
Definition main.cpp:220
const std::string & includeDir() const
Definition main.cpp:206
bool isNonQtModule() const
Definition main.cpp:240
bool debug() const
Definition main.cpp:244
const std::regex & qpaHeadersRegex() const
Definition main.cpp:222
void printHelp() const
Definition main.cpp:254
CommandLineOptions(int argc, char *argv[])
Definition main.cpp:196
const std::regex & ssgHeadersRegex() const
Definition main.cpp:226
const std::regex & privateHeadersRegex() const
Definition main.cpp:228
bool scanAllMode() const
Definition main.cpp:236
bool printHelpOnly() const
Definition main.cpp:242
bool warningsAreErrors() const
Definition main.cpp:252
const std::regex & rhiHeadersRegex() const
Definition main.cpp:224
bool showOnly() const
Definition main.cpp:250
const std::set< std::string > & headers() const
Definition main.cpp:232
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
iterator find(const T &value)
Definition qset.h:159
iterator insert(const T &value)
Definition qset.h:155
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1252
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1226
QString & append(QChar c)
Definition qstring.cpp:3252
bool generateVersionHeader(const std::string &outputFile)
Definition main.cpp:1548
bool generateLinkerVersionScript()
Definition main.cpp:1653
bool isHeaderQpa(const std::string &headerFileName)
Definition main.cpp:1467
void parseVersionScriptContent(const std::string buffer, ParsingResult &result)
Definition main.cpp:1005
bool updateOrCopy(const std::filesystem::path &src, const std::filesystem::path &dst) noexcept
Definition main.cpp:1687
bool checkLineForSymbols(const std::string &line, std::string &symbol)
Definition main.cpp:1423
void updateSymbolDescriptor(const std::string &symbol, const std::string &file, SymbolDescriptor::SourceType type)
Definition main.cpp:1670
bool isHeaderGenerated(const std::string &header)
Definition main.cpp:1504
std::filesystem::path makeHeaderAbsolute(const std::string &filename) const
Definition main.cpp:1679
bool generateMasterHeader()
Definition main.cpp:1519
bool generateAliasedHeaderFileIfTimestampChanged(const std::string &outputFilePath, const std::string &aliasedFilePath, const FileStamp &originalStamp=FileStamp::clock::now())
Definition main.cpp:1743
ErrorCodes sync()
Definition main.cpp:631
bool isHeader(const std::filesystem::path &path)
Definition main.cpp:1494
bool generateDeprecatedHeaders()
Definition main.cpp:1565
bool copyGeneratedHeadersToStagingDirectory(const std::string &outputDirectory, bool skipCleanup=false)
Definition main.cpp:762
bool writeIfDifferent(const std::string &outputFile, const std::string &buffer)
Definition main.cpp:1767
bool isHeaderRhi(const std::string &headerFileName)
Definition main.cpp:1472
bool isHeaderSsg(const std::string &headerFileName)
Definition main.cpp:1477
bool parseHeader(const std::filesystem::path &headerFile, ParsingResult &result, unsigned int skipChecks)
Definition main.cpp:1084
bool isDocFileHeuristic(const std::string &headerFilePath)
Definition main.cpp:1499
bool isHeaderPCH(const std::string &headerFilename)
Definition main.cpp:1487
bool processHeader(const std::filesystem::path &headerFile)
Definition main.cpp:837
SyncScanner(CommandLineOptions *commandLineArgs)
Definition main.cpp:616
bool generateQtCamelCaseFileIfContentChanged(const std::string &outputFilePath, const std::string &aliasedFilePath)
Definition main.cpp:1727
bool generateHeaderCheckExceptions()
Definition main.cpp:1642
void resetCurrentFileInfoData(const std::filesystem::path &headerFile)
Definition main.cpp:809
bool isHeaderPrivate(const std::string &headerFile)
Definition main.cpp:1482
QString str
[2]
int main()
[0]
QSet< QString >::iterator it
QList< QVariant > arguments
Definition main.cpp:79
bool parseVersion(const std::string &version, int &major, int &minor)
Definition main.cpp:94
std::string asciiToLower(std::string s)
Definition main.cpp:80
std::filesystem::path normilizedPath(const std::string &path)
Definition main.cpp:145
utils::DummyOutputStream DummyOutput
void printFilesystemError(const std::filesystem::filesystem_error &fserr, std::string_view errorMsg)
Definition main.cpp:139
void printInternalError()
Definition main.cpp:132
bool createDirectories(const std::string &path, std::string_view errorMsg, bool *exists=nullptr)
Definition main.cpp:156
std::string asciiToUpper(std::string s)
Definition main.cpp:87
qsizetype erase(QByteArray &ba, const T &t)
Definition qbytearray.h:782
DBusConnection const char DBusError * error
static QString outputFile
static QString header(const QString &name)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
return ret
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLenum GLsizei length
GLenum src
GLenum GLuint buffer
GLenum type
GLsizei const GLuint * paths
GLenum GLenum dst
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLuint entry
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLenum GLenum GLenum input
SSL_CTX int void * arg
static const std::regex GlobalHeaderRegex("^q(.*)global\\.h$")
HeaderChecks
Definition main.cpp:40
@ WeMeantItChecks
Definition main.cpp:45
@ CriticalChecks
Definition main.cpp:48
@ AllChecks
Definition main.cpp:49
@ PrivateHeaderChecks
Definition main.cpp:43
@ PragmaOnceChecks
Definition main.cpp:46
@ NoChecks
Definition main.cpp:41
@ NamespaceChecks
Definition main.cpp:42
@ IncludeChecks
Definition main.cpp:44
ErrorCodes
Definition main.cpp:33
@ InvalidArguments
Definition main.cpp:35
@ NoError
Definition main.cpp:34
@ SyncFailed
Definition main.cpp:36
constexpr std::string_view ErrorMessagePreamble
Definition main.cpp:56
std::filesystem::file_time_type FileStamp
Definition main.cpp:179
constexpr int LinkerScriptCommentAlignment
Definition main.cpp:52
constexpr std::string_view WarningMessagePreamble
Definition main.cpp:57
bool MasterHeaderIncludeComparator(const std::string &a, const std::string &b)
Definition main.cpp:61
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)
QFile file
[0]
QDBusArgument argument