116 " -no-ui-lines\n" |
115 " -no-ui-lines\n" |
117 " Do not record line numbers in references to UI files.\n" |
116 " Do not record line numbers in references to UI files.\n" |
118 " -disable-heuristic {sametext|similartext|number}\n" |
117 " -disable-heuristic {sametext|similartext|number}\n" |
119 " Disable the named merge heuristic. Can be specified multiple times.\n" |
118 " Disable the named merge heuristic. Can be specified multiple times.\n" |
120 " -pro <filename>\n" |
119 " -pro <filename>\n" |
121 " Name of a .pro file. Useful for files with .pro\n" |
120 " Name of a .pro file. Useful for files with .pro file syntax but\n" |
122 " file syntax but different file suffix\n" |
121 " different file suffix. Projects are recursed into and merged.\n" |
123 " -source-language <language>[_<region>]\n" |
122 " -source-language <language>[_<region>]\n" |
124 " Specify the language of the source strings for new files.\n" |
123 " Specify the language of the source strings for new files.\n" |
125 " Defaults to POSIX if not specified.\n" |
124 " Defaults to POSIX if not specified.\n" |
126 " -target-language <language>[_<region>]\n" |
125 " -target-language <language>[_<region>]\n" |
127 " Specify the language of the translations for new files.\n" |
126 " Specify the language of the translations for new files.\n" |
128 " Guessed from the file name if not specified.\n" |
127 " Guessed from the file name if not specified.\n" |
|
128 " -ts <ts-file>...\n" |
|
129 " Specify the output file(s). This will override the TRANSLATIONS\n" |
|
130 " and nullify the CODECFORTR from possibly specified project files.\n" |
|
131 " -codecfortr <codec>\n" |
|
132 " Specify the codec assumed for tr() calls. Effective only with -ts.\n" |
129 " -version\n" |
133 " -version\n" |
130 " Display the version of lupdate and exit.\n" |
134 " Display the version of lupdate and exit.\n" |
131 ).arg(m_defaultExtensions)); |
135 ).arg(m_defaultExtensions)); |
132 } |
136 } |
133 |
137 |
134 static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFileNames, |
138 static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFileNames, |
135 const QByteArray &codecForTr, const QString &sourceLanguage, const QString &targetLanguage, |
139 bool setCodec, const QString &sourceLanguage, const QString &targetLanguage, |
136 UpdateOptions options, bool *fail) |
140 UpdateOptions options, bool *fail) |
137 { |
141 { |
138 QDir dir; |
142 QDir dir; |
139 QString err; |
143 QString err; |
140 foreach (const QString &fileName, tsFileNames) { |
144 foreach (const QString &fileName, tsFileNames) { |
212 *fail = true; |
216 *fail = true; |
213 } |
217 } |
214 } |
218 } |
215 } |
219 } |
216 |
220 |
|
221 static QStringList getSources(const char *var, const char *vvar, const QStringList &baseVPaths, |
|
222 const QString &projectDir, const ProFileEvaluator &visitor) |
|
223 { |
|
224 QStringList vPaths = visitor.absolutePathValues(QLatin1String(vvar), projectDir); |
|
225 vPaths += baseVPaths; |
|
226 vPaths.removeDuplicates(); |
|
227 return visitor.absoluteFileValues(QLatin1String(var), projectDir, vPaths, 0); |
|
228 } |
|
229 |
|
230 static QStringList getSources(const ProFileEvaluator &visitor, const QString &projectDir) |
|
231 { |
|
232 QStringList baseVPaths; |
|
233 baseVPaths += visitor.absolutePathValues(QLatin1String("VPATH"), projectDir); |
|
234 baseVPaths << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH |
|
235 baseVPaths += visitor.absolutePathValues(QLatin1String("DEPENDPATH"), projectDir); |
|
236 baseVPaths.removeDuplicates(); |
|
237 |
|
238 QStringList sourceFiles; |
|
239 |
|
240 // app/lib template |
|
241 sourceFiles += getSources("SOURCES", "VPATH_SOURCES", baseVPaths, projectDir, visitor); |
|
242 |
|
243 sourceFiles += getSources("FORMS", "VPATH_FORMS", baseVPaths, projectDir, visitor); |
|
244 sourceFiles += getSources("FORMS3", "VPATH_FORMS3", baseVPaths, projectDir, visitor); |
|
245 |
|
246 QStringList vPathsInc = baseVPaths; |
|
247 vPathsInc += visitor.absolutePathValues(QLatin1String("INCLUDEPATH"), projectDir); |
|
248 vPathsInc.removeDuplicates(); |
|
249 sourceFiles += visitor.absoluteFileValues(QLatin1String("HEADERS"), projectDir, vPathsInc, 0); |
|
250 |
|
251 sourceFiles.removeDuplicates(); |
|
252 sourceFiles.sort(); |
|
253 |
|
254 return sourceFiles; |
|
255 } |
|
256 |
|
257 static void processSources(Translator &fetchedTor, |
|
258 const QStringList &sourceFiles, ConversionData &cd) |
|
259 { |
|
260 QStringList sourceFilesCpp; |
|
261 for (QStringList::const_iterator it = sourceFiles.begin(); it != sourceFiles.end(); ++it) { |
|
262 if (it->endsWith(QLatin1String(".java"), Qt::CaseInsensitive)) |
|
263 loadJava(fetchedTor, *it, cd); |
|
264 else if (it->endsWith(QLatin1String(".ui"), Qt::CaseInsensitive) |
|
265 || it->endsWith(QLatin1String(".jui"), Qt::CaseInsensitive)) |
|
266 loadUI(fetchedTor, *it, cd); |
|
267 else if (it->endsWith(QLatin1String(".js"), Qt::CaseInsensitive) |
|
268 || it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive)) |
|
269 loadQScript(fetchedTor, *it, cd); |
|
270 else |
|
271 sourceFilesCpp << *it; |
|
272 } |
|
273 loadCPP(fetchedTor, sourceFilesCpp, cd); |
|
274 if (!cd.error().isEmpty()) |
|
275 printOut(cd.error()); |
|
276 } |
|
277 |
|
278 static void processProjects( |
|
279 bool topLevel, bool nestComplain, const QStringList &proFiles, |
|
280 UpdateOptions options, const QByteArray &codecForSource, |
|
281 const QString &targetLanguage, const QString &sourceLanguage, |
|
282 Translator *parentTor, bool *fail); |
|
283 |
|
284 static void processProject( |
|
285 bool nestComplain, const QFileInfo &pfi, ProFileEvaluator &visitor, |
|
286 UpdateOptions options, const QByteArray &_codecForSource, |
|
287 const QString &targetLanguage, const QString &sourceLanguage, |
|
288 Translator *fetchedTor, bool *fail) |
|
289 { |
|
290 QByteArray codecForSource = _codecForSource; |
|
291 QStringList tmp = visitor.values(QLatin1String("CODECFORSRC")); |
|
292 if (!tmp.isEmpty()) { |
|
293 codecForSource = tmp.last().toLatin1(); |
|
294 if (!QTextCodec::codecForName(codecForSource)) { |
|
295 qWarning("lupdate warning: Codec for source '%s' is invalid. " |
|
296 "Falling back to codec for tr().", codecForSource.constData()); |
|
297 codecForSource.clear(); |
|
298 } |
|
299 } |
|
300 if (visitor.templateType() == ProFileEvaluator::TT_Subdirs) { |
|
301 QStringList subProFiles; |
|
302 QDir proDir(pfi.absoluteDir()); |
|
303 foreach (const QString &subdir, visitor.values(QLatin1String("SUBDIRS"))) { |
|
304 QString subPro = QDir::cleanPath(proDir.absoluteFilePath(subdir)); |
|
305 QFileInfo subInfo(subPro); |
|
306 if (subInfo.isDir()) |
|
307 subProFiles << (subPro + QLatin1Char('/') |
|
308 + subInfo.fileName() + QLatin1String(".pro")); |
|
309 else |
|
310 subProFiles << subPro; |
|
311 } |
|
312 processProjects(false, nestComplain, subProFiles, options, codecForSource, |
|
313 targetLanguage, sourceLanguage, fetchedTor, fail); |
|
314 } else { |
|
315 ConversionData cd; |
|
316 cd.m_noUiLines = options & NoUiLines; |
|
317 cd.m_codecForSource = codecForSource; |
|
318 cd.m_includePath = visitor.values(QLatin1String("INCLUDEPATH")); |
|
319 QStringList sourceFiles = getSources(visitor, pfi.absolutePath()); |
|
320 QSet<QString> sourceDirs; |
|
321 sourceDirs.insert(QDir::cleanPath(pfi.absolutePath()) + QLatin1Char('/')); |
|
322 foreach (const QString &sf, sourceFiles) |
|
323 sourceDirs.insert(sf.left(sf.lastIndexOf(QLatin1Char('/')) + 1)); |
|
324 QStringList rootList = sourceDirs.toList(); |
|
325 rootList.sort(); |
|
326 for (int prev = 0, curr = 1; curr < rootList.length(); ) |
|
327 if (rootList.at(curr).startsWith(rootList.at(prev))) |
|
328 rootList.removeAt(curr); |
|
329 else |
|
330 prev = curr++; |
|
331 cd.m_projectRoots = QSet<QString>::fromList(rootList); |
|
332 processSources(*fetchedTor, sourceFiles, cd); |
|
333 } |
|
334 } |
|
335 |
|
336 static void processProjects( |
|
337 bool topLevel, bool nestComplain, const QStringList &proFiles, |
|
338 UpdateOptions options, const QByteArray &codecForSource, |
|
339 const QString &targetLanguage, const QString &sourceLanguage, |
|
340 Translator *parentTor, bool *fail) |
|
341 { |
|
342 foreach (const QString &proFile, proFiles) { |
|
343 ProFileEvaluator visitor; |
|
344 visitor.setVerbose(options & Verbose); |
|
345 |
|
346 QFileInfo pfi(proFile); |
|
347 ProFile pro(pfi.absoluteFilePath()); |
|
348 if (!visitor.queryProFile(&pro) || !visitor.accept(&pro)) { |
|
349 if (topLevel) |
|
350 *fail = true; |
|
351 continue; |
|
352 } |
|
353 |
|
354 if (visitor.contains(QLatin1String("TRANSLATIONS"))) { |
|
355 if (parentTor) { |
|
356 if (topLevel) { |
|
357 std::cerr << "lupdate warning: TS files from command line " |
|
358 "will override TRANSLATIONS in " << qPrintable(proFile) << ".\n"; |
|
359 goto noTrans; |
|
360 } else if (nestComplain) { |
|
361 std::cerr << "lupdate warning: TS files from command line " |
|
362 "prevent recursing into " << qPrintable(proFile) << ".\n"; |
|
363 continue; |
|
364 } |
|
365 } |
|
366 QStringList tsFiles; |
|
367 QDir proDir(pfi.absolutePath()); |
|
368 foreach (const QString &tsFile, visitor.values(QLatin1String("TRANSLATIONS"))) |
|
369 tsFiles << QFileInfo(proDir, tsFile).filePath(); |
|
370 if (tsFiles.isEmpty()) { |
|
371 // This might mean either a buggy PRO file or an intentional detach - |
|
372 // we can't know without seeing the actual RHS of the assignment ... |
|
373 // Just assume correctness and be silent. |
|
374 continue; |
|
375 } |
|
376 Translator tor; |
|
377 bool setCodec = false; |
|
378 QStringList tmp = visitor.values(QLatin1String("CODEC")) |
|
379 + visitor.values(QLatin1String("DEFAULTCODEC")) |
|
380 + visitor.values(QLatin1String("CODECFORTR")); |
|
381 if (!tmp.isEmpty()) { |
|
382 tor.setCodecName(tmp.last().toLatin1()); |
|
383 setCodec = true; |
|
384 } |
|
385 processProject(false, pfi, visitor, options, codecForSource, |
|
386 targetLanguage, sourceLanguage, &tor, fail); |
|
387 updateTsFiles(tor, tsFiles, setCodec, sourceLanguage, targetLanguage, options, fail); |
|
388 continue; |
|
389 } |
|
390 noTrans: |
|
391 if (!parentTor) { |
|
392 if (topLevel) |
|
393 std::cerr << "lupdate warning: no TS files specified. Only diagnostics " |
|
394 "will be produced for '" << qPrintable(proFile) << "'.\n"; |
|
395 Translator tor; |
|
396 processProject(nestComplain, pfi, visitor, options, codecForSource, |
|
397 targetLanguage, sourceLanguage, &tor, fail); |
|
398 } else { |
|
399 processProject(nestComplain, pfi, visitor, options, codecForSource, |
|
400 targetLanguage, sourceLanguage, parentTor, fail); |
|
401 } |
|
402 } |
|
403 } |
|
404 |
217 int main(int argc, char **argv) |
405 int main(int argc, char **argv) |
218 { |
406 { |
219 QCoreApplication app(argc, argv); |
407 QCoreApplication app(argc, argv); |
220 m_defaultExtensions = QLatin1String("ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx"); |
408 m_defaultExtensions = QLatin1String("ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx"); |
221 |
409 |
222 QStringList args = app.arguments(); |
410 QStringList args = app.arguments(); |
223 QString defaultContext; // This was QLatin1String("@default") before. |
|
224 Translator fetchedTor; |
|
225 QByteArray codecForTr; |
|
226 QByteArray codecForSource; |
|
227 QStringList tsFileNames; |
411 QStringList tsFileNames; |
228 QStringList proFiles; |
412 QStringList proFiles; |
229 QMultiHash<QString, QString> allCSources; |
413 QMultiHash<QString, QString> allCSources; |
230 QSet<QString> projectRoots; |
414 QSet<QString> projectRoots; |
231 QStringList sourceFiles; |
415 QStringList sourceFiles; |
232 QStringList includePath; |
416 QStringList includePath; |
233 QString targetLanguage; |
417 QString targetLanguage; |
234 QString sourceLanguage; |
418 QString sourceLanguage; |
|
419 QByteArray codecForTr; |
235 |
420 |
236 UpdateOptions options = |
421 UpdateOptions options = |
237 Verbose | // verbose is on by default starting with Qt 4.2 |
422 Verbose | // verbose is on by default starting with Qt 4.2 |
238 HeuristicSameText | HeuristicSimilarText | HeuristicNumber; |
423 HeuristicSameText | HeuristicSimilarText | HeuristicNumber; |
239 int numFiles = 0; |
424 int numFiles = 0; |
240 bool standardSyntax = true; |
|
241 bool metTsFlag = false; |
425 bool metTsFlag = false; |
242 bool recursiveScan = true; |
426 bool recursiveScan = true; |
243 |
427 |
244 QString extensions = m_defaultExtensions; |
428 QString extensions = m_defaultExtensions; |
245 QSet<QString> extensionsNameFilters; |
429 QSet<QString> extensionsNameFilters; |
246 |
|
247 for (int i = 1; i < argc; ++i) { |
|
248 if (args.at(i) == QLatin1String("-ts")) |
|
249 standardSyntax = false; |
|
250 } |
|
251 |
430 |
252 for (int i = 1; i < argc; ++i) { |
431 for (int i = 1; i < argc; ++i) { |
253 QString arg = args.at(i); |
432 QString arg = args.at(i); |
254 if (arg == QLatin1String("-help") |
433 if (arg == QLatin1String("-help") |
255 || arg == QLatin1String("--help") |
434 || arg == QLatin1String("--help") |
445 } |
626 } |
446 } |
627 } |
447 } else { |
628 } else { |
448 sourceFiles << QDir::cleanPath(fi.absoluteFilePath());; |
629 sourceFiles << QDir::cleanPath(fi.absoluteFilePath());; |
449 } |
630 } |
|
631 numFiles++; |
450 } |
632 } |
451 } // for args |
633 } // for args |
452 |
634 |
453 foreach (const QString &proFile, proFiles) |
635 if (numFiles == 0) { |
454 projectRoots.insert(QDir::cleanPath(QFileInfo(proFile).absolutePath()) + QLatin1Char('/')); |
636 printUsage(); |
455 |
637 return 1; |
456 bool firstPass = true; |
638 } |
|
639 |
|
640 if (!targetLanguage.isEmpty() && tsFileNames.count() != 1) |
|
641 std::cerr << "lupdate warning: -target-language usually only " |
|
642 "makes sense with exactly one TS file.\n"; |
|
643 if (!codecForTr.isEmpty() && tsFileNames.isEmpty()) |
|
644 std::cerr << "lupdate warning: -codecfortr has no effect without -ts.\n"; |
|
645 |
457 bool fail = false; |
646 bool fail = false; |
458 while (firstPass || !proFiles.isEmpty()) { |
647 if (proFiles.isEmpty()) { |
|
648 if (tsFileNames.isEmpty()) |
|
649 std::cerr << "lupdate warning: no TS files specified. " |
|
650 "Only diagnostics will be produced.\n"; |
|
651 |
|
652 Translator fetchedTor; |
459 ConversionData cd; |
653 ConversionData cd; |
460 cd.m_defaultContext = defaultContext; |
|
461 cd.m_noUiLines = options & NoUiLines; |
654 cd.m_noUiLines = options & NoUiLines; |
462 cd.m_projectRoots = projectRoots; |
655 cd.m_projectRoots = projectRoots; |
463 cd.m_includePath = includePath; |
656 cd.m_includePath = includePath; |
464 cd.m_allCSources = allCSources; |
657 cd.m_allCSources = allCSources; |
465 |
658 fetchedTor.setCodecName(codecForTr); |
466 QStringList tsFiles = tsFileNames; |
659 processSources(fetchedTor, sourceFiles, cd); |
467 if (proFiles.count() > 0) { |
660 updateTsFiles(fetchedTor, tsFileNames, !codecForTr.isEmpty(), |
468 QFileInfo pfi(proFiles.takeFirst()); |
661 sourceLanguage, targetLanguage, options, &fail); |
469 QHash<QByteArray, QStringList> variables; |
662 } else { |
470 |
663 if (!sourceFiles.isEmpty() || !includePath.isEmpty()) { |
471 ProFileEvaluator visitor; |
664 qWarning("lupdate error: Both project and source files / include paths specified.\n"); |
472 visitor.setVerbose(options & Verbose); |
665 return 1; |
473 |
666 } |
474 ProFile pro(pfi.absoluteFilePath()); |
667 if (!tsFileNames.isEmpty()) { |
475 if (!visitor.queryProFile(&pro)) |
668 Translator fetchedTor; |
476 return 2; |
669 fetchedTor.setCodecName(codecForTr); |
477 if (!visitor.accept(&pro)) |
670 processProjects(true, true, proFiles, options, QByteArray(), |
478 return 2; |
671 targetLanguage, sourceLanguage, &fetchedTor, &fail); |
479 |
672 updateTsFiles(fetchedTor, tsFileNames, !codecForTr.isEmpty(), |
480 if (visitor.templateType() == ProFileEvaluator::TT_Subdirs) { |
673 sourceLanguage, targetLanguage, options, &fail); |
481 QDir proDir(pfi.absoluteDir()); |
674 } else { |
482 foreach (const QString &subdir, visitor.values(QLatin1String("SUBDIRS"))) { |
675 processProjects(true, false, proFiles, options, QByteArray(), |
483 QString subPro = QDir::cleanPath(proDir.absoluteFilePath(subdir)); |
676 targetLanguage, sourceLanguage, 0, &fail); |
484 QFileInfo subInfo(subPro); |
677 } |
485 if (subInfo.isDir()) |
|
486 proFiles << (subPro + QLatin1Char('/') |
|
487 + subInfo.fileName() + QLatin1String(".pro")); |
|
488 else |
|
489 proFiles << subPro; |
|
490 } |
|
491 continue; |
|
492 } |
|
493 |
|
494 cd.m_includePath += visitor.values(QLatin1String("INCLUDEPATH")); |
|
495 |
|
496 evaluateProFile(visitor, &variables, pfi.absolutePath()); |
|
497 |
|
498 sourceFiles = variables.value("SOURCES"); |
|
499 |
|
500 QStringList tmp = variables.value("CODECFORTR"); |
|
501 if (!tmp.isEmpty() && !tmp.first().isEmpty()) { |
|
502 codecForTr = tmp.first().toLatin1(); |
|
503 fetchedTor.setCodecName(codecForTr); |
|
504 cd.m_outputCodec = codecForTr; |
|
505 } |
|
506 tmp = variables.value("CODECFORSRC"); |
|
507 if (!tmp.isEmpty() && !tmp.first().isEmpty()) { |
|
508 codecForSource = tmp.first().toLatin1(); |
|
509 if (!QTextCodec::codecForName(codecForSource)) |
|
510 qWarning("lupdate warning: Codec for source '%s' is invalid. Falling back to codec for tr().", |
|
511 codecForSource.constData()); |
|
512 else |
|
513 cd.m_codecForSource = codecForSource; |
|
514 } |
|
515 |
|
516 tsFiles += variables.value("TRANSLATIONS"); |
|
517 } |
|
518 |
|
519 QStringList sourceFilesCpp; |
|
520 for (QStringList::iterator it = sourceFiles.begin(); it != sourceFiles.end(); ++it) { |
|
521 if (it->endsWith(QLatin1String(".java"), Qt::CaseInsensitive)) |
|
522 loadJava(fetchedTor, *it, cd); |
|
523 else if (it->endsWith(QLatin1String(".ui"), Qt::CaseInsensitive) |
|
524 || it->endsWith(QLatin1String(".jui"), Qt::CaseInsensitive)) |
|
525 loadUI(fetchedTor, *it, cd); |
|
526 else if (it->endsWith(QLatin1String(".js"), Qt::CaseInsensitive) |
|
527 || it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive)) |
|
528 loadQScript(fetchedTor, *it, cd); |
|
529 else |
|
530 sourceFilesCpp << *it; |
|
531 } |
|
532 loadCPP(fetchedTor, sourceFilesCpp, cd); |
|
533 if (!cd.error().isEmpty()) |
|
534 printOut(cd.error()); |
|
535 |
|
536 tsFiles.sort(); |
|
537 tsFiles.removeDuplicates(); |
|
538 |
|
539 if (!tsFiles.isEmpty()) |
|
540 updateTsFiles(fetchedTor, tsFiles, codecForTr, sourceLanguage, targetLanguage, options, &fail); |
|
541 |
|
542 firstPass = false; |
|
543 } |
678 } |
544 |
|
545 if (numFiles == 0) { |
|
546 printUsage(); |
|
547 return 1; |
|
548 } |
|
549 |
|
550 return fail ? 1 : 0; |
679 return fail ? 1 : 0; |
551 } |
680 } |