Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cpp_clang.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "cpp_clang.h"
8#include "synchronized.h"
9#include "translator.h"
10
11#include <QLibraryInfo>
12#include <QtCore/qdir.h>
13#include <QtCore/qfileinfo.h>
14#include <QtCore/qjsonarray.h>
15#include <QtCore/qjsondocument.h>
16#include <QtCore/qjsonobject.h>
17#include <QtCore/qscopeguard.h>
18#include <QtCore/QProcess>
19#include <QStandardPaths>
20#include <QtTools/private/qttools-config_p.h>
21
22#include <clang/Tooling/CompilationDatabase.h>
23
24#include <algorithm>
25#include <limits>
26#include <thread>
27#include <iostream>
28#include <cstdlib>
29
30#include <cstdio>
31#include <memory>
32#include <stdexcept>
33#include <string>
34#include <array>
35
36using clang::tooling::CompilationDatabase;
37
38QT_BEGIN_NAMESPACE
39
40using namespace Qt::StringLiterals;
41
42Q_LOGGING_CATEGORY(lcClang, "qt.lupdate.clang");
43
45{
46 QStringList candidates;
47 if (const char* local_compiler = std::getenv("CXX")) {
48 candidates.push_back(QLatin1String(local_compiler));
49 } else {
50 candidates = {
51#ifdef Q_OS_WIN
52 QStringLiteral("cl"),
53#endif
54 QStringLiteral("clang++"),
55 QStringLiteral("gcc")
56 };
57 }
58 QString sysCompiler;
59 for (const QString &comp : candidates) {
60
61 sysCompiler = QStandardPaths::findExecutable(comp);
62 if (!sysCompiler.isEmpty())
63 break;
64 }
65 return sysCompiler;
66}
67
69{
70 QList<QByteArray> pathList;
71 if (const char* includeEnv = std::getenv("INCLUDE")) {
72 QByteArray includeList = QByteArray::fromRawData(includeEnv, strlen(includeEnv));
73 pathList = includeList.split(';');
74 }
75 for (auto it = pathList.begin(); it != pathList.end(); ++it) {
76 it->prepend("-isystem");
77 }
78 return pathList;
79}
80
82{
83 QList<QByteArray> dirList;
84 QStringList rootdirs;
85 if (const char* includeEnv = std::getenv("LUPDATE_ROOT_DIRS")) {
86 QByteArray includeList = QByteArray::fromRawData(includeEnv, strlen(includeEnv));
87 dirList = includeList.split(';');
88
89 for (auto dir : dirList) {
90 rootdirs.append(QString::fromStdString(dir.toStdString()));
91 }
92 }
93 return rootdirs;
94}
95
96
98{
99 return QByteArrayLiteral(" (framework directory)");
100}
101
103{
104
105 QList<QByteArray> pathList;
106 QString compiler = getSysCompiler();
107 if (compiler.isEmpty()) {
108 qWarning("lupdate: Could not determine system compiler.");
109 return pathList;
110 }
111
112 const QFileInfo fiCompiler(compiler);
113 const QString compilerName
114
115#ifdef Q_OS_WIN
116 = fiCompiler.completeBaseName();
117#else
118 = fiCompiler.fileName();
119#endif
120
121 if (compilerName == QLatin1String("cl"))
122 return getMSVCIncludePathsFromEnvironment();
123
124 if (compilerName != QLatin1String("gcc") && compilerName != QLatin1String("clang++")) {
125 qWarning("lupdate: Unknown compiler %s", qPrintable(compiler));
126 return pathList;
127 }
128
129 const QStringList compilerFlags = {
130 QStringLiteral("-E"), QStringLiteral("-x"), QStringLiteral("c++"),
131 QStringLiteral("-"), QStringLiteral("-v")
132 };
133
134 QProcess proc;
135 proc.setStandardInputFile(proc.nullDevice());
136 proc.start(compiler, compilerFlags);
137 proc.waitForFinished(30000);
138 QByteArray buffer = proc.readAllStandardError();
139 proc.kill();
140
141 // ### TODO: Merge this with qdoc's getInternalIncludePaths()
142 const QByteArrayList stdErrLines = buffer.split('\n');
143 bool isIncludeDir = false;
144 for (const QByteArray &line : stdErrLines) {
145 if (isIncludeDir) {
146 if (line.startsWith(QByteArrayLiteral("End of search list"))) {
147 isIncludeDir = false;
148 } else {
149 QByteArray prefix("-isystem");
150 QByteArray headerPath{line.trimmed()};
151 if (headerPath.endsWith(frameworkSuffix())) {
152 headerPath.truncate(headerPath.size() - frameworkSuffix().size());
153 prefix = QByteArrayLiteral("-F");
154 }
155 pathList.append(prefix + headerPath);
156 }
157 } else if (line.startsWith(QByteArrayLiteral("#include <...> search starts here"))) {
158 isIncludeDir = true;
159 }
160 }
161
162 return pathList;
163}
164
166{
167 QStringList aliases = trFunctionAliasManager.listAliases();
168 std::vector<std::string> results;
169 for (QString alias : aliases) {
170 std::string definition = "-D" + alias.toStdString();
171 switch (trFunctionAliasManager.trFunctionByName(alias)) {
172 case TrFunctionAliasManager::Function_QT_TR_N_NOOP:
173 definition += "(x)=QT_TR_N_NOOP(x)";
174 results.push_back(definition);
175 break;
176 case TrFunctionAliasManager::Function_trUtf8:
177 case TrFunctionAliasManager::Function_tr:
178 definition += "=tr";
179 results.push_back(definition);
180 break;
181 case TrFunctionAliasManager::Function_QT_TR_NOOP:
182 definition += "(x)=QT_TR_NOOP(x)";
183 results.push_back(definition);
184 break;
185 case TrFunctionAliasManager::Function_QT_TR_NOOP_UTF8:
186 definition += "(x)=QT_TR_NOOP_UTF8(x)";
187 results.push_back(definition);
188 break;
189 case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP:
190 definition += "(scope,x)=QT_TRANSLATE_N_NOOP(scope,x)";
191 results.push_back(definition);
192 break;
193 case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP3:
194 definition += "(scope, x, comment)=QT_TRANSLATE_N_NOOP3(scope, x, comment)";
195 results.push_back(definition);
196 break;
197 case TrFunctionAliasManager::Function_translate:
198 definition += "=QCoreApplication::translate";
199 results.push_back(definition);
200 break;
201 case TrFunctionAliasManager::Function_findMessage:
202 definition += "=findMessage";
203 results.push_back(definition);
204 break;
205 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP:
206 definition += "(scope,x)=QT_TRANSLATE_NOOP(scope,x)";
207 results.push_back(definition);
208 break;
209 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP_UTF8:
210 definition += "(scope,x)=QT_TRANSLATE_NOOP_UTF8(scope,x)";
211 results.push_back(definition);
212 break;
213 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3:
214 definition += "(scope, x, comment)=QT_TRANSLATE_NOOP3(scope, x, comment)";
215 results.push_back(definition);
216 break;
217 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3_UTF8:
218 definition += "(scope, x, comment)=QT_TRANSLATE_NOOP3_UTF8(scope, x, comment)";
219 results.push_back(definition);
220 break;
221 case TrFunctionAliasManager::Function_qtTrId:
222 definition += "=qtTrId";
223 results.push_back(definition);
224 break;
225 case TrFunctionAliasManager::Function_QT_TRID_N_NOOP:
226 case TrFunctionAliasManager::Function_QT_TRID_NOOP:
227 definition += "(id)=QT_TRID_NOOP(id)";
228 results.push_back(definition);
229 break;
230 case TrFunctionAliasManager::Function_Q_DECLARE_TR_FUNCTIONS:
231 definition += "(context)=Q_DECLARE_TR_FUNCTIONS(context)";
232 results.push_back(definition);
233 break;
234 default:
235 break;
236 }
237 }
238 return results;
239}
240
242
244{
245 const QByteArrayList compilerIncludeFlags = getIncludePathsFromCompiler();
246 return [=](const clang::tooling::CommandLineArguments &args, llvm::StringRef /*unused*/) {
247 clang::tooling::CommandLineArguments adjustedArgs(args);
248 clang::tooling::CommandLineArguments adjustedArgsTemp;
249
250 adjustedArgsTemp.push_back("-fparse-all-comments");
251 adjustedArgsTemp.push_back("-nostdinc");
252
253#if defined(Q_PROCESSOR_X86) || defined(Q_OS_MACOS)
254 // Turn off SSE support to avoid usage of gcc builtins.
255 // TODO: Look into what Qt Creator does.
256 // Pointers: HeaderPathFilter::removeGccInternalIncludePaths()
257 // and gccInstallDir() in gcctoolchain.cpp
258 // Also needed for Macs (and if libclang is built for X86)
259 // No need for CLANG_RESOURCE_DIR when this is part of the argument.
260 adjustedArgsTemp.push_back("-mno-sse");
261#endif
262
263#ifdef Q_OS_WIN
264 adjustedArgsTemp.push_back("-fms-compatibility-version=19");
265 adjustedArgsTemp.push_back("-DQ_COMPILER_UNIFORM_INIT"); // qtbase + clang-cl hack
266 // avoid constexpr error connected with offsetof (QTBUG-97380)
267 adjustedArgsTemp.push_back("-D_CRT_USE_BUILTIN_OFFSETOF");
268#endif
269 adjustedArgsTemp.push_back("-Wno-everything");
270
271 for (const QByteArray &flag : compilerIncludeFlags)
272 adjustedArgsTemp.push_back(flag.data());
273
274 for (auto alias : aliasDefinition) {
275 adjustedArgsTemp.push_back(alias);
276 }
277
278 clang::tooling::CommandLineArguments::iterator it = llvm::find(adjustedArgs, "--");
279 adjustedArgs.insert(it, adjustedArgsTemp.begin(), adjustedArgsTemp.end());
280 return adjustedArgs;
281 };
282}
283
285{
286 // pre-process the files by a simple text search if there is any occurrence
287 // of things we are interested in
288 constexpr llvm::StringLiteral qDeclareTrFunction("Q_DECLARE_TR_FUNCTIONS(");
289 constexpr llvm::StringLiteral qtTrNoop("QT_TR_NOOP(");
290 constexpr llvm::StringLiteral qtTrNoopUTF8("QT_TR_NOOP)UTF8(");
291 constexpr llvm::StringLiteral qtTrNNoop("QT_TR_N_NOOP(");
292 constexpr llvm::StringLiteral qtTrIdNoop("QT_TRID_NOOP(");
293 constexpr llvm::StringLiteral qtTrIdNNoop("QT_TRID_N_NOOP(");
294 constexpr llvm::StringLiteral qtTranslateNoop("QT_TRANSLATE_NOOP(");
295 constexpr llvm::StringLiteral qtTranslateNoopUTF8("QT_TRANSLATE_NOOP_UTF8(");
296 constexpr llvm::StringLiteral qtTranslateNNoop("QT_TRANSLATE_N_NOOP(");
297 constexpr llvm::StringLiteral qtTranslateNoop3("QT_TRANSLATE_NOOP3(");
298 constexpr llvm::StringLiteral qtTranslateNoop3UTF8("QT_TRANSLATE_NOOP3_UTF8(");
299 constexpr llvm::StringLiteral qtTranslateNNoop3("QT_TRANSLATE_N_NOOP3(");
300 constexpr llvm::StringLiteral translatorComment("TRANSLATOR ");
301 constexpr llvm::StringLiteral qtTrId("qtTrId(");
302 constexpr llvm::StringLiteral tr("tr(");
303 constexpr llvm::StringLiteral trUtf8("trUtf8(");
304 constexpr llvm::StringLiteral translate("translate(");
305
306 const size_t pos = ba.find_first_of("QT_TR");
307 const auto baSliced = ba.slice(pos, llvm::StringRef::npos);
308 if (pos != llvm::StringRef::npos) {
309 if (baSliced.contains(qtTrNoop) || baSliced.contains(qtTrNoopUTF8) || baSliced.contains(qtTrNNoop)
310 || baSliced.contains(qtTrIdNoop) || baSliced.contains(qtTrIdNNoop)
311 || baSliced.contains(qtTranslateNoop) || baSliced.contains(qtTranslateNoopUTF8)
312 || baSliced.contains(qtTranslateNNoop) || baSliced.contains(qtTranslateNoop3)
313 || baSliced.contains(qtTranslateNoop3UTF8) || baSliced.contains(qtTranslateNNoop3))
314 return true;
315 }
316
317 if (ba.contains(qDeclareTrFunction) || ba.contains(translatorComment) || ba.contains(qtTrId) || ba.contains(tr)
318 || ba.contains(trUtf8) || ba.contains(translate))
319 return true;
320
321 for (QString alias : trFunctionAliasManager.listAliases()) {
322 if (ba.contains(qPrintable(alias)))
323 return true;
324 }
325
326
327 return false;
328}
329
330static bool generateCompilationDatabase(const QString &outputFilePath, const ConversionData &cd)
331{
332 QJsonArray commandObjects;
333 const QString buildDir = QDir::currentPath();
334 const QString fakefileName = QLatin1String("dummmy.cpp");
335 QJsonObject obj;
336 obj[QLatin1String("file")] = fakefileName;
337 obj[QLatin1String("directory")] = buildDir;
338 QJsonArray args = {
339 QLatin1String("clang++"),
340 QLatin1String("-std=gnu++17"),
341 #ifndef Q_OS_WIN
342 QLatin1String("-fPIC"),
343 #endif
344 };
345
346#if defined(Q_OS_MACOS) && QT_CONFIG(framework)
347 const QString installPath = QLibraryInfo::path(QLibraryInfo::LibrariesPath);
348 QString arg = QLatin1String("-F") + installPath;
349 args.push_back(arg);
350#endif
351
352 for (const QString &path : cd.m_includePath) {
353 QString arg = QLatin1String("-I") + path;
354 args.push_back(std::move(arg));
355 }
356
357 obj[QLatin1String("arguments")] = args;
358 commandObjects.append(obj);
359
360 QJsonDocument doc(commandObjects);
361 QFile file(outputFilePath);
362 if (!file.open(QIODevice::WriteOnly))
363 return false;
364 file.write(doc.toJson());
365 return true;
366}
367
368// Sort messages in such a way that they appear in the same order like in the given file list.
369static void sortMessagesByFileOrder(ClangCppParser::TranslatorMessageVector &messages,
370 const QStringList &files)
371{
372 // first sort messages by line number
373 std::stable_sort(messages.begin(), messages.end(),
374 [&](const TranslatorMessage &lhs, const TranslatorMessage &rhs) {
375 auto i = lhs.lineNumber();
376 auto k = rhs.lineNumber();
377 return i < k;
378 });
379
380 QHash<QString, QStringList::size_type> indexByPath;
381 for (const TranslatorMessage &m : messages)
382 indexByPath[m.fileName()] = std::numeric_limits<QStringList::size_type>::max();
383
384 for (QStringList::size_type i = 0; i < files.size(); ++i)
385 indexByPath[files[i]] = i;
386
387 std::stable_sort(messages.begin(), messages.end(),
388 [&](const TranslatorMessage &lhs, const TranslatorMessage &rhs) {
389 auto i = indexByPath.value(lhs.fileName());
390 auto k = indexByPath.value(rhs.fileName());
391 return i < k;
392 });
393}
394
396{
397 QStringList listAlias = trFunctionAliasManager.listAliases();
398 if (listAlias.size() > 0)
399 return true;
400 return false;
401}
402
403void ClangCppParser::loadCPP(Translator &translator, const QStringList &files, ConversionData &cd,
404 bool *fail)
405{
406 FileSignificanceCheck::create();
407 auto cleanup = qScopeGuard(FileSignificanceCheck::destroy);
408 FileSignificanceCheck::the()->setExclusionRegExes(cd.m_excludes);
409 if (cd.m_rootDirs.size() > 0)
410 FileSignificanceCheck::the()->setRootDirectories(cd.m_rootDirs);
411 else
412 FileSignificanceCheck::the()->setRootDirectories(getProjectDirsFromEnvironment());
413
414 if (hasAliases())
415 aliasDefinition = getAliasFunctionDefinition();
416
417 // pre-process the files by a simple text search if there is any occurrence
418 // of things we are interested in
419 qCDebug(lcClang) << "Load CPP \n";
420 std::vector<std::string> sources;
421 for (const QString &filename : files) {
422 qCDebug(lcClang) << "File: " << filename << " \n";
423 sources.emplace_back(filename.toStdString());
424 }
425
426 std::string errorMessage;
427 std::unique_ptr<CompilationDatabase> db;
428 if (cd.m_compilationDatabaseDir.isEmpty()) {
429 db = CompilationDatabase::autoDetectFromDirectory(".", errorMessage);
430 if (!db && !files.isEmpty()) {
431 db = CompilationDatabase::autoDetectFromSource(files.first().toStdString(),
432 errorMessage);
433 }
434 } else {
435 db = CompilationDatabase::autoDetectFromDirectory(cd.m_compilationDatabaseDir.toStdString(),
436 errorMessage);
437 }
438
439 if (!db) {
440 const QString dbFilePath = QStringLiteral("compile_commands.json");
441 qCDebug(lcClang) << "Generating compilation database" << dbFilePath;
442 if (!generateCompilationDatabase(dbFilePath, cd)) {
443 *fail = true;
444 cd.appendError(u"Cannot generate compilation database."_s);
445 return;
446 }
447 errorMessage.clear();
448 db = CompilationDatabase::loadFromDirectory(".", errorMessage);
449 }
450
451 if (!db) {
452 *fail = true;
453 cd.appendError(QString::fromStdString(errorMessage));
454 return;
455 }
456
457 TranslationStores ast, qdecl, qnoop;
458 Stores stores(ast, qdecl, qnoop);
459
460 std::vector<std::thread> producers;
461 ReadSynchronizedRef<std::string> ppSources(sources);
462 WriteSynchronizedRef<TranslationRelatedStore> ppStore(stores.Preprocessor);
463 size_t idealProducerCount = std::min(ppSources.size(), size_t(std::thread::hardware_concurrency()));
464 clang::tooling::ArgumentsAdjuster argumentsAdjusterSyntaxOnly =
465 clang::tooling::getClangSyntaxOnlyAdjuster();
466 clang::tooling::ArgumentsAdjuster argumentsAdjusterLocal = getClangArgumentAdjuster();
467 clang::tooling::ArgumentsAdjuster argumentsAdjuster =
468 clang::tooling::combineAdjusters(argumentsAdjusterLocal, argumentsAdjusterSyntaxOnly);
469
470 for (size_t i = 0; i < idealProducerCount; ++i) {
471 std::thread producer([&ppSources, &db, &ppStore, &argumentsAdjuster]() {
472 std::string file;
473 while (ppSources.next(&file)) {
474 clang::tooling::ClangTool tool(*db, file);
475 tool.appendArgumentsAdjuster(argumentsAdjuster);
476 tool.run(new LupdatePreprocessorActionFactory(&ppStore));
477 }
478 });
479 producers.emplace_back(std::move(producer));
480 }
481 for (auto &producer : producers)
482 producer.join();
483 producers.clear();
484
485 ReadSynchronizedRef<std::string> astSources(sources);
486 idealProducerCount = std::min(astSources.size(), size_t(std::thread::hardware_concurrency()));
487 for (size_t i = 0; i < idealProducerCount; ++i) {
488 std::thread producer([&astSources, &db, &stores, &argumentsAdjuster]() {
489 std::string file;
490 while (astSources.next(&file)) {
491 clang::tooling::ClangTool tool(*db, file);
492 tool.appendArgumentsAdjuster(argumentsAdjuster);
493 tool.run(new LupdateToolActionFactory(&stores));
494 }
495 });
496 producers.emplace_back(std::move(producer));
497 }
498 for (auto &producer : producers)
499 producer.join();
500 producers.clear();
501
502 TranslationStores finalStores;
503 WriteSynchronizedRef<TranslationRelatedStore> wsv(finalStores);
504
506 ClangCppParser::correctAstTranslationContext(rsv, wsv, qdecl);
507
509 //unlike ast translation context, qnoop context don't need to be corrected
510 //(because Q_DECLARE_TR_FUNCTION context is already applied).
511 ClangCppParser::finalize(rsvQNoop, wsv);
512
513 TranslatorMessageVector messages;
514 for (auto &store : finalStores)
515 ClangCppParser::collectMessages(messages, store);
516
517 sortMessagesByFileOrder(messages, files);
518
519 for (TranslatorMessage &msg : messages) {
520 if (!msg.warning().isEmpty()) {
521 std::cerr << qPrintable(msg.warning());
522 if (msg.warningOnly() == true)
523 continue;
524 }
525 translator.extend(std::move(msg), cd);
526 }
527}
528
529void ClangCppParser::collectMessages(TranslatorMessageVector &result,
531{
532 if (!store.isValid(true)) {
533 if (store.lupdateWarning.isEmpty())
534 return;
535 // The message needs to be added to the results so that the warning can be ordered
536 // and printed in a consistent way.
537 // the message won't appear in the .ts file
538 result.push_back(translatorMessage(store, store.lupdateIdMetaData, false, false, true));
539 return;
540 }
541
542 qCDebug(lcClang) << "---------------------------------------------------------------Filling translator for " << store.funcName;
543 qCDebug(lcClang) << " contextRetrieved " << store.contextRetrieved;
544 qCDebug(lcClang) << " source " << store.lupdateSource;
545
546 bool plural = false;
547 switch (trFunctionAliasManager.trFunctionByName(store.funcName)) {
548 // handle tr
549 case TrFunctionAliasManager::Function_QT_TR_N_NOOP:
550 plural = true;
551 Q_FALLTHROUGH();
552 case TrFunctionAliasManager::Function_tr:
553 case TrFunctionAliasManager::Function_trUtf8:
554 case TrFunctionAliasManager::Function_QT_TR_NOOP:
555 case TrFunctionAliasManager::Function_QT_TR_NOOP_UTF8:
556 if (!store.lupdateSourceWhenId.isEmpty()) {
557 std::stringstream warning;
558 warning << qPrintable(store.lupdateLocationFile) << ":"
559 << store.lupdateLocationLine << ":"
560 << store.locationCol << ": "
561 << "//% cannot be used with tr() / QT_TR_NOOP(). Ignoring\n";
562 store.lupdateWarning.append(QString::fromStdString(warning.str()));
563 qCDebug(lcClang) << "//% is ignored when using tr function\n";
564 }
565 if (store.contextRetrieved.isEmpty() && store.contextArg.isEmpty()) {
566 std::stringstream warning;
567 warning << qPrintable(store.lupdateLocationFile) << ":"
568 << store.lupdateLocationLine << ":"
569 << store.locationCol << ": "
570 << qPrintable(store.funcName) << " cannot be called without context."
571 << " The call is ignored (missing Q_OBJECT maybe?)\n";
572 store.lupdateWarning.append(QString::fromStdString(warning.str()));
573 qCDebug(lcClang) << "tr() cannot be called without context \n";
574 // The message need to be added to the results so that the warning can be ordered
575 // and printed in a consistent way.
576 // the message won't appear in the .ts file
577 result.push_back(translatorMessage(store, store.lupdateIdMetaData, plural, false, true));
578 } else
579 result.push_back(translatorMessage(store, store.lupdateIdMetaData, plural, false));
580 break;
581
582 // handle translate and findMessage
583 case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP:
584 case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP3:
585 plural = true;
586 Q_FALLTHROUGH();
587 case TrFunctionAliasManager::Function_translate:
588 case TrFunctionAliasManager::Function_findMessage:
589 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP:
590 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP_UTF8:
591 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3:
592 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3_UTF8:
593 if (!store.lupdateSourceWhenId.isEmpty()) {
594 std::stringstream warning;
595 warning << qPrintable(store.lupdateLocationFile) << ":"
596 << store.lupdateLocationLine << ":"
597 << store.locationCol << ": "
598 << "//% cannot be used with translate() / QT_TRANSLATE_NOOP(). Ignoring\n";
599 store.lupdateWarning.append(QString::fromStdString(warning.str()));
600 qCDebug(lcClang) << "//% is ignored when using translate function\n";
601 }
602 result.push_back(translatorMessage(store, store.lupdateIdMetaData, plural, false));
603 break;
604
605 // handle qtTrId
606 case TrFunctionAliasManager::Function_QT_TRID_N_NOOP:
607 plural = true;
608 Q_FALLTHROUGH();
609 case TrFunctionAliasManager::Function_qtTrId:
610 case TrFunctionAliasManager::Function_QT_TRID_NOOP:
611 if (!store.lupdateIdMetaData.isEmpty()) {
612 std::stringstream warning;
613 warning << qPrintable(store.lupdateLocationFile) << ":"
614 << store.lupdateLocationLine << ":"
615 << store.locationCol << ": "
616 << "//= cannot be used with qtTrId() / QT_TRID_NOOP(). Ignoring\n";
617 store.lupdateWarning.append(QString::fromStdString(warning.str()));
618 qCDebug(lcClang) << "//= is ignored when using qtTrId function \n";
619 }
620 result.push_back(translatorMessage(store, store.lupdateId, plural, true));
621 break;
622 default:
623 if (store.funcName == QStringLiteral("TRANSLATOR"))
624 result.push_back(translatorMessage(store, store.lupdateIdMetaData, plural, false));
625 }
626}
627
628static QString ensureCanonicalPath(const QString &filePath)
629{
630 QFileInfo fi(filePath);
631 if (fi.isRelative())
632 fi.setFile(QDir::current().absoluteFilePath(filePath));
633 return fi.canonicalFilePath();
634}
635
637 const QString &id, bool plural, bool isId, bool isWarningOnly)
638{
639 if (isWarningOnly) {
641 // msg filled with file name and line number should be enough for the message ordering
642 msg.setFileName(ensureCanonicalPath(store.lupdateLocationFile));
643 msg.setLineNumber(store.lupdateLocationLine);
644 msg.setWarning(store.lupdateWarning);
645 msg.setWarningOnly(isWarningOnly);
646 return msg;
647 }
648
649 QString context;
650 if (!isId) {
651 context = ParserTool::transcode(store.contextArg.isEmpty() ? store.contextRetrieved
652 : store.contextArg);
653 }
654
655 TranslatorMessage msg(context,
656 ParserTool::transcode(isId ? store.lupdateSourceWhenId
657 : store.lupdateSource),
658 ParserTool::transcode(store.lupdateComment),
659 QString(),
660 ensureCanonicalPath(store.lupdateLocationFile),
661 store.lupdateLocationLine,
662 QStringList(),
663 TranslatorMessage::Type::Unfinished,
664 (plural ? plural : !store.lupdatePlural.isEmpty()));
665
666 if (!store.lupdateAllMagicMetaData.empty())
667 msg.setExtras(store.lupdateAllMagicMetaData);
668 msg.setExtraComment(ParserTool::transcode(store.lupdateExtraComment));
669 msg.setId(ParserTool::transcode(id));
670 if (!store.lupdateWarning.isEmpty())
671 msg.setWarning(store.lupdateWarning);
672 return msg;
673}
674
675#define START_THREADS(RSV, WSV)
676 std::vector<std::thread> producers;
677 const size_t idealProducerCount = std::min(RSV.size(), size_t(std::thread::hardware_concurrency()));
678
679 for (size_t i = 0; i < idealProducerCount; ++i) {
680 std::thread producer([&]() {
681 TranslationRelatedStore store;
682 while (RSV.next(&store)) {
683 if (!store.contextArg.isEmpty()) {
684 WSV.emplace_back(std::move(store));
685 continue;
686 }
687
688#define JOIN_THREADS(WSV)
689 WSV.emplace_back(std::move(store));
690 }
691 });
692 producers.emplace_back(std::move(producer));
693 }
694
695 for (auto &producer : producers)
696 producer.join();
697
699 WriteSynchronizedRef<TranslationRelatedStore> &newAst)
700{
701 START_THREADS(ast, newAst)
702 JOIN_THREADS(newAst)
703}
704
706 WriteSynchronizedRef<TranslationRelatedStore> &newAst, const TranslationStores &qDecl)
707{
708 START_THREADS(ast, newAst)
709
710 // If there is a Q_DECLARE_TR_FUNCTION the context given there takes
711 // priority over the retrieved context. The retrieved context for
712 // Q_DECLARE_TR_FUNCTION (where the macro was) has to fit the retrieved
713 // context of the tr function if there is already a argument giving the
714 // context, it has priority
715 for (const auto &declareStore : qDecl) {
716 qCDebug(lcClang) << "----------------------------";
717 qCDebug(lcClang) << "Tr call context retrieved " << store.contextRetrieved;
718 qCDebug(lcClang) << "Tr call source " << store.lupdateSource;
719 qCDebug(lcClang) << "- DECLARE context retrieved " << declareStore.contextRetrieved;
720 qCDebug(lcClang) << "- DECLARE context Arg " << declareStore.contextArg;
721 if (declareStore.contextRetrieved.isEmpty())
722 continue;
723 if (!declareStore.contextRetrieved.startsWith(store.contextRetrieved))
724 continue;
725 if (store.contextRetrieved == declareStore.contextRetrieved) {
726 qCDebug(lcClang) << "* Tr call context retrieved " << store.contextRetrieved;
727 qCDebug(lcClang) << "* Tr call source " << store.lupdateSource;
728 qCDebug(lcClang) << "* DECLARE context retrieved " << declareStore.contextRetrieved;
729 qCDebug(lcClang) << "* DECLARE context Arg " << declareStore.contextArg;
730 store.contextRetrieved = declareStore.contextArg;
731 // store.contextArg should never be overwritten.
732 break;
733 }
734 }
735
736 JOIN_THREADS(newAst)
737}
738
739#undef START_THREADS
740#undef JOIN_THREADS
741
742QT_END_NAMESPACE
LupdateToolActionFactory(Stores *stores)
void setWarningOnly(bool isWarningOnly)
void setExtras(const ExtraData &extras)
void setLineNumber(int lineNumber)
static clang::tooling::ArgumentsAdjuster getClangArgumentAdjuster()
#define START_THREADS(RSV, WSV)
QByteArrayList getIncludePathsFromCompiler()
static bool generateCompilationDatabase(const QString &outputFilePath, const ConversionData &cd)
static void sortMessagesByFileOrder(ClangCppParser::TranslatorMessageVector &messages, const QStringList &files)
static QString getSysCompiler()
Definition cpp_clang.cpp:44
static QString ensureCanonicalPath(const QString &filePath)
static QByteArray frameworkSuffix()
Definition cpp_clang.cpp:97
static std::vector< std::string > aliasDefinition
#define JOIN_THREADS(WSV)
static QStringList getProjectDirsFromEnvironment()
Definition cpp_clang.cpp:81
static QByteArrayList getMSVCIncludePathsFromEnvironment()
Definition cpp_clang.cpp:68
std::vector< std::string > getAliasFunctionDefinition()
void finalize(ReadSynchronizedRef< TranslationRelatedStore > &ast, WriteSynchronizedRef< TranslationRelatedStore > &newAst)
bool stringContainsTranslationInformation(llvm::StringRef ba)
void correctAstTranslationContext(ReadSynchronizedRef< TranslationRelatedStore > &ast, WriteSynchronizedRef< TranslationRelatedStore > &newAst, const TranslationStores &qDecl)
void collectMessages(TranslatorMessageVector &result, TranslationRelatedStore &store)
TranslatorMessage translatorMessage(const TranslationRelatedStore &store, const QString &id, bool plural, bool isID, bool isWarningOnly=false)
void loadCPP(Translator &translator, const QStringList &filenames, ConversionData &cd, bool *fail)
QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
bool isValid(bool printwarning=false)
Definition cpp_clang.h:69