105 QList<QByteArray> pathList;
106 QString compiler = getSysCompiler();
107 if (compiler.isEmpty()) {
108 qWarning(
"lupdate: Could not determine system compiler.");
112 const QFileInfo fiCompiler(compiler);
113 const QString compilerName
116 = fiCompiler.completeBaseName();
118 = fiCompiler.fileName();
121 if (compilerName == QLatin1String(
"cl"))
122 return getMSVCIncludePathsFromEnvironment();
124 if (compilerName != QLatin1String(
"gcc") && compilerName != QLatin1String(
"clang++")) {
125 qWarning(
"lupdate: Unknown compiler %s", qPrintable(compiler));
129 const QStringList compilerFlags = {
130 QStringLiteral(
"-E"), QStringLiteral(
"-x"), QStringLiteral(
"c++"),
131 QStringLiteral(
"-"), QStringLiteral(
"-v")
135 proc.setStandardInputFile(proc.nullDevice());
136 proc.start(compiler, compilerFlags);
137 proc.waitForFinished(30000);
138 QByteArray buffer = proc.readAllStandardError();
142 const QByteArrayList stdErrLines = buffer.split(
'\n');
143 bool isIncludeDir =
false;
144 for (
const QByteArray &line : stdErrLines) {
146 if (line.startsWith(QByteArrayLiteral(
"End of search list"))) {
147 isIncludeDir =
false;
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");
155 pathList.append(prefix + headerPath);
157 }
else if (line.startsWith(QByteArrayLiteral(
"#include <...> search starts here"))) {
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);
176 case TrFunctionAliasManager::Function_trUtf8:
177 case TrFunctionAliasManager::Function_tr:
179 results.push_back(definition);
181 case TrFunctionAliasManager::Function_QT_TR_NOOP:
182 definition +=
"(x)=QT_TR_NOOP(x)";
183 results.push_back(definition);
185 case TrFunctionAliasManager::Function_QT_TR_NOOP_UTF8:
186 definition +=
"(x)=QT_TR_NOOP_UTF8(x)";
187 results.push_back(definition);
189 case TrFunctionAliasManager::Function_QT_TRANSLATE_N_NOOP:
190 definition +=
"(scope,x)=QT_TRANSLATE_N_NOOP(scope,x)";
191 results.push_back(definition);
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);
197 case TrFunctionAliasManager::Function_translate:
198 definition +=
"=QCoreApplication::translate";
199 results.push_back(definition);
201 case TrFunctionAliasManager::Function_findMessage:
202 definition +=
"=findMessage";
203 results.push_back(definition);
205 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP:
206 definition +=
"(scope,x)=QT_TRANSLATE_NOOP(scope,x)";
207 results.push_back(definition);
209 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP_UTF8:
210 definition +=
"(scope,x)=QT_TRANSLATE_NOOP_UTF8(scope,x)";
211 results.push_back(definition);
213 case TrFunctionAliasManager::Function_QT_TRANSLATE_NOOP3:
214 definition +=
"(scope, x, comment)=QT_TRANSLATE_NOOP3(scope, x, comment)";
215 results.push_back(definition);
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);
221 case TrFunctionAliasManager::Function_qtTrId:
222 definition +=
"=qtTrId";
223 results.push_back(definition);
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);
230 case TrFunctionAliasManager::Function_Q_DECLARE_TR_FUNCTIONS:
231 definition +=
"(context)=Q_DECLARE_TR_FUNCTIONS(context)";
232 results.push_back(definition);
245 const QByteArrayList compilerIncludeFlags = getIncludePathsFromCompiler();
246 return [=](
const clang::tooling::CommandLineArguments &args, llvm::StringRef ) {
247 clang::tooling::CommandLineArguments adjustedArgs(args);
248 clang::tooling::CommandLineArguments adjustedArgsTemp;
250 adjustedArgsTemp.push_back(
"-fparse-all-comments");
251 adjustedArgsTemp.push_back(
"-nostdinc");
253#if defined(Q_PROCESSOR_X86) || defined(Q_OS_MACOS)
260 adjustedArgsTemp.push_back(
"-mno-sse");
264 adjustedArgsTemp.push_back(
"-fms-compatibility-version=19");
265 adjustedArgsTemp.push_back(
"-DQ_COMPILER_UNIFORM_INIT");
267 adjustedArgsTemp.push_back(
"-D_CRT_USE_BUILTIN_OFFSETOF");
269 adjustedArgsTemp.push_back(
"-Wno-everything");
271 for (
const QByteArray &flag : compilerIncludeFlags)
272 adjustedArgsTemp.push_back(flag.data());
274 for (
auto alias : aliasDefinition) {
275 adjustedArgsTemp.push_back(alias);
278 clang::tooling::CommandLineArguments::iterator it = llvm::find(adjustedArgs,
"--");
279 adjustedArgs.insert(it, adjustedArgsTemp.begin(), adjustedArgsTemp.end());
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(");
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))
317 if (ba.contains(qDeclareTrFunction) || ba.contains(translatorComment) || ba.contains(qtTrId) || ba.contains(tr)
318 || ba.contains(trUtf8) || ba.contains(translate))
321 for (QString alias : trFunctionAliasManager.listAliases()) {
322 if (ba.contains(qPrintable(alias)))
332 QJsonArray commandObjects;
333 const QString buildDir = QDir::currentPath();
334 const QString fakefileName = QLatin1String(
"dummmy.cpp");
336 obj[QLatin1String(
"file")] = fakefileName;
337 obj[QLatin1String(
"directory")] = buildDir;
339 QLatin1String(
"clang++"),
340 QLatin1String(
"-std=gnu++17"),
342 QLatin1String(
"-fPIC"),
346#if defined(Q_OS_MACOS) && QT_CONFIG(framework)
347 const QString installPath = QLibraryInfo::path(QLibraryInfo::LibrariesPath);
348 QString arg = QLatin1String(
"-F") + installPath;
352 for (
const QString &path : cd.m_includePath) {
353 QString arg = QLatin1String(
"-I") + path;
354 args.push_back(std::move(arg));
357 obj[QLatin1String(
"arguments")] = args;
358 commandObjects.append(obj);
360 QJsonDocument doc(commandObjects);
361 QFile file(outputFilePath);
362 if (!file.open(QIODevice::WriteOnly))
364 file.write(doc.toJson());
370 const QStringList &files)
373 std::stable_sort(messages.begin(), messages.end(),
380 QHash<QString, QStringList::size_type> indexByPath;
381 for (
const TranslatorMessage &m : messages)
382 indexByPath[m.fileName()] = std::numeric_limits<QStringList::size_type>::max();
384 for (QStringList::size_type i = 0; i < files.size(); ++i)
385 indexByPath[files[i]] = i;
387 std::stable_sort(messages.begin(), messages.end(),
389 auto i = indexByPath.value(lhs.fileName());
390 auto k = indexByPath.value(rhs.fileName());
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);
412 FileSignificanceCheck::the()->setRootDirectories(getProjectDirsFromEnvironment());
415 aliasDefinition = getAliasFunctionDefinition();
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());
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(),
435 db = CompilationDatabase::autoDetectFromDirectory(cd.m_compilationDatabaseDir.toStdString(),
440 const QString dbFilePath = QStringLiteral(
"compile_commands.json");
441 qCDebug(lcClang) <<
"Generating compilation database" << dbFilePath;
442 if (!generateCompilationDatabase(dbFilePath, cd)) {
444 cd.appendError(u"Cannot generate compilation database."_s);
447 errorMessage.clear();
448 db = CompilationDatabase::loadFromDirectory(
".", errorMessage);
453 cd.appendError(QString::fromStdString(errorMessage));
457 TranslationStores ast, qdecl, qnoop;
458 Stores stores(ast, qdecl, qnoop);
460 std::vector<std::thread> producers;
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);
470 for (size_t i = 0; i < idealProducerCount; ++i) {
471 std::thread producer([&ppSources, &db, &ppStore, &argumentsAdjuster]() {
473 while (ppSources.next(&file)) {
474 clang::tooling::ClangTool tool(*db, file);
475 tool.appendArgumentsAdjuster(argumentsAdjuster);
476 tool.run(
new LupdatePreprocessorActionFactory(&ppStore));
479 producers.emplace_back(
std::move(producer));
481 for (
auto &producer : producers)
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]() {
490 while (astSources.next(&file)) {
491 clang::tooling::ClangTool tool(*db, file);
492 tool.appendArgumentsAdjuster(argumentsAdjuster);
496 producers.emplace_back(
std::move(producer));
498 for (
auto &producer : producers)
502 TranslationStores finalStores;
503 WriteSynchronizedRef<TranslationRelatedStore> wsv(finalStores);
506 ClangCppParser::correctAstTranslationContext(rsv, wsv, qdecl);
511 ClangCppParser::finalize(rsvQNoop, wsv);
513 TranslatorMessageVector messages;
514 for (
auto &store : finalStores)
515 ClangCppParser::collectMessages(messages, store);
517 sortMessagesByFileOrder(messages, files);
519 for (TranslatorMessage &msg : messages) {
520 if (!msg.warning().isEmpty()) {
521 std::cerr << qPrintable(msg.warning());
522 if (msg.warningOnly() ==
true)
525 translator.extend(std::move(msg), cd);
533 if (store.lupdateWarning.isEmpty())
538 result.push_back(translatorMessage(store, store.lupdateIdMetaData,
false,
false,
true));
542 qCDebug(lcClang) <<
"---------------------------------------------------------------Filling translator for " << store.funcName;
543 qCDebug(lcClang) <<
" contextRetrieved " << store.contextRetrieved;
544 qCDebug(lcClang) <<
" source " << store.lupdateSource;
547 switch (trFunctionAliasManager.trFunctionByName(store.funcName)) {
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";
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";
577 result.push_back(translatorMessage(store, store.lupdateIdMetaData, plural,
false,
true));
579 result.push_back(translatorMessage(store, store.lupdateIdMetaData, plural,
false));
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";
602 result.push_back(translatorMessage(store, store.lupdateIdMetaData, plural,
false));
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";
620 result.push_back(translatorMessage(store, store.lupdateId, plural,
true));
623 if (store.funcName == QStringLiteral(
"TRANSLATOR"))
624 result.push_back(translatorMessage(store, store.lupdateIdMetaData, plural,
false));
637 const QString &id,
bool plural,
bool isId,
bool isWarningOnly)
642 msg.setFileName(ensureCanonicalPath(store.lupdateLocationFile));
644 msg.setWarning(store.lupdateWarning);
651 context = ParserTool::transcode(store.contextArg.isEmpty() ? store.contextRetrieved
656 ParserTool::transcode(isId ? store.lupdateSourceWhenId
657 : store.lupdateSource),
658 ParserTool::transcode(store.lupdateComment),
660 ensureCanonicalPath(store.lupdateLocationFile),
661 store.lupdateLocationLine,
663 TranslatorMessage::Type::Unfinished,
664 (plural ? plural : !store.lupdatePlural.isEmpty()));
666 if (!store.lupdateAllMagicMetaData.empty())
668 msg.setExtraComment(ParserTool::transcode(store.lupdateExtraComment));
669 msg.setId(ParserTool::transcode(id));
670 if (!store.lupdateWarning.isEmpty())
671 msg.setWarning(store.lupdateWarning);
706 WriteSynchronizedRef<TranslationRelatedStore> &newAst,
const TranslationStores &qDecl)
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())
723 if (!declareStore.contextRetrieved.startsWith(store.contextRetrieved))
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;