source: trunk/tools/linguist/lupdate/main.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 30.7 KB
RevLine 
[2]1/****************************************************************************
2**
[846]3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
[561]4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
[2]6**
7** This file is part of the Qt Linguist of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
[561]24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
[2]27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
[561]36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
[2]38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
[561]42#include "lupdate.h"
[2]43
[561]44#include <translator.h>
45#include <profileevaluator.h>
46
[2]47#include <QtCore/QCoreApplication>
48#include <QtCore/QDebug>
49#include <QtCore/QDir>
50#include <QtCore/QFile>
51#include <QtCore/QFileInfo>
52#include <QtCore/QString>
53#include <QtCore/QStringList>
54#include <QtCore/QTextCodec>
[846]55#include <QtCore/QTranslator>
56#include <QtCore/QLibraryInfo>
[2]57
[561]58#include <iostream>
59
[2]60static QString m_defaultExtensions;
61
[561]62static void printOut(const QString & out)
63{
64 std::cerr << qPrintable(out);
65}
66
[846]67class LU {
68 Q_DECLARE_TR_FUNCTIONS(LUpdate)
69};
70
[2]71static void recursiveFileInfoList(const QDir &dir,
[561]72 const QSet<QString> &nameFilters, QDir::Filters filter,
[2]73 QFileInfoList *fileinfolist)
74{
[561]75 foreach (const QFileInfo &fi, dir.entryInfoList(filter))
76 if (fi.isDir())
77 recursiveFileInfoList(QDir(fi.absoluteFilePath()), nameFilters, filter, fileinfolist);
78 else if (nameFilters.contains(fi.suffix()))
79 fileinfolist->append(fi);
[2]80}
81
82static void printUsage()
83{
[846]84 printOut(LU::tr(
[2]85 "Usage:\n"
[561]86 " lupdate [options] [project-file]...\n"
[846]87 " lupdate [options] [source-file|path|@lst-file]... -ts ts-files|@lst-file\n\n"
[561]88 "lupdate is part of Qt's Linguist tool chain. It extracts translatable\n"
89 "messages from Qt UI files, C++, Java and JavaScript/QtScript source code.\n"
90 "Extracted messages are stored in textual translation source files (typically\n"
91 "Qt TS XML). New and modified messages can be merged into existing TS files.\n\n"
[2]92 "Options:\n"
93 " -help Display this information and exit.\n"
94 " -no-obsolete\n"
95 " Drop all obsolete strings.\n"
96 " -extensions <ext>[,<ext>]...\n"
97 " Process files with the given extensions only.\n"
98 " The extension list must be separated with commas, not with whitespace.\n"
99 " Default: '%1'.\n"
100 " -pluralonly\n"
101 " Only include plural form messages.\n"
102 " -silent\n"
103 " Do not explain what is being done.\n"
104 " -no-sort\n"
[561]105 " Do not sort contexts in TS files.\n"
[2]106 " -no-recursive\n"
107 " Do not recursively scan the following directories.\n"
108 " -recursive\n"
[561]109 " Recursively scan the following directories (default).\n"
110 " -I <includepath> or -I<includepath>\n"
111 " Additional location to look for include files.\n"
112 " May be specified multiple times.\n"
[2]113 " -locations {absolute|relative|none}\n"
[561]114 " Specify/override how source code references are saved in TS files.\n"
[2]115 " Default is absolute.\n"
116 " -no-ui-lines\n"
[561]117 " Do not record line numbers in references to UI files.\n"
[2]118 " -disable-heuristic {sametext|similartext|number}\n"
119 " Disable the named merge heuristic. Can be specified multiple times.\n"
120 " -pro <filename>\n"
[651]121 " Name of a .pro file. Useful for files with .pro file syntax but\n"
122 " different file suffix. Projects are recursed into and merged.\n"
[2]123 " -source-language <language>[_<region>]\n"
[561]124 " Specify the language of the source strings for new files.\n"
125 " Defaults to POSIX if not specified.\n"
[2]126 " -target-language <language>[_<region>]\n"
[561]127 " Specify the language of the translations for new files.\n"
128 " Guessed from the file name if not specified.\n"
[651]129 " -ts <ts-file>...\n"
130 " Specify the output file(s). This will override the TRANSLATIONS\n"
131 " and nullify the CODECFORTR from possibly specified project files.\n"
132 " -codecfortr <codec>\n"
133 " Specify the codec assumed for tr() calls. Effective only with -ts.\n"
[2]134 " -version\n"
135 " Display the version of lupdate and exit.\n"
[846]136 " @lst-file\n"
137 " Read additional file names (one per line) from lst-file.\n"
[2]138 ).arg(m_defaultExtensions));
139}
140
141static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFileNames,
[769]142 bool setCodec, const QString &sourceLanguage, const QString &targetLanguage,
[2]143 UpdateOptions options, bool *fail)
144{
145 QDir dir;
146 QString err;
147 foreach (const QString &fileName, tsFileNames) {
148 QString fn = dir.relativeFilePath(fileName);
149 ConversionData cd;
150 Translator tor;
151 cd.m_sortContexts = !(options & NoSort);
152 if (QFile(fileName).exists()) {
153 if (!tor.load(fileName, cd, QLatin1String("auto"))) {
[846]154 printOut(cd.error());
[2]155 *fail = true;
156 continue;
157 }
158 tor.resolveDuplicates();
159 cd.clearErrors();
[769]160 if (setCodec && fetchedTor.codec() != tor.codec())
[846]161 printOut(LU::tr("lupdate warning: Codec for tr() '%1' disagrees with"
162 " existing file's codec '%2'. Expect trouble.\n")
163 .arg(QString::fromLatin1(fetchedTor.codecName()),
164 QString::fromLatin1(tor.codecName())));
[2]165 if (!targetLanguage.isEmpty() && targetLanguage != tor.languageCode())
[846]166 printOut(LU::tr("lupdate warning: Specified target language '%1' disagrees with"
167 " existing file's language '%2'. Ignoring.\n")
168 .arg(targetLanguage, tor.languageCode()));
[2]169 if (!sourceLanguage.isEmpty() && sourceLanguage != tor.sourceLanguageCode())
[846]170 printOut(LU::tr("lupdate warning: Specified source language '%1' disagrees with"
171 " existing file's language '%2'. Ignoring.\n")
172 .arg(sourceLanguage, tor.sourceLanguageCode()));
[2]173 } else {
[769]174 if (setCodec)
175 tor.setCodec(fetchedTor.codec());
[2]176 if (!targetLanguage.isEmpty())
177 tor.setLanguageCode(targetLanguage);
[561]178 else
179 tor.setLanguageCode(Translator::guessLanguageCodeFromFileName(fileName));
[2]180 if (!sourceLanguage.isEmpty())
181 tor.setSourceLanguageCode(sourceLanguage);
182 }
183 tor.makeFileNamesAbsolute(QFileInfo(fileName).absoluteDir());
184 if (options & NoLocations)
185 tor.setLocationsType(Translator::NoLocations);
186 else if (options & RelativeLocations)
187 tor.setLocationsType(Translator::RelativeLocations);
188 else if (options & AbsoluteLocations)
189 tor.setLocationsType(Translator::AbsoluteLocations);
190 if (options & Verbose)
[846]191 printOut(LU::tr("Updating '%1'...\n").arg(fn));
[2]192
[561]193 UpdateOptions theseOptions = options;
[2]194 if (tor.locationsType() == Translator::NoLocations) // Could be set from file
[561]195 theseOptions |= NoLocations;
196 Translator out = merge(tor, fetchedTor, theseOptions, err);
[769]197 if (setCodec)
198 out.setCodec(fetchedTor.codec());
[2]199
200 if ((options & Verbose) && !err.isEmpty()) {
201 printOut(err);
202 err.clear();
203 }
204 if (options & PluralOnly) {
205 if (options & Verbose)
[846]206 printOut(LU::tr("Stripping non plural forms in '%1'...\n").arg(fn));
[2]207 out.stripNonPluralForms();
208 }
209 if (options & NoObsolete)
210 out.stripObsoleteMessages();
211 out.stripEmptyContexts();
212
[561]213 out.normalizeTranslations(cd);
214 if (!cd.errors().isEmpty()) {
[846]215 printOut(cd.error());
[561]216 cd.clearErrors();
217 }
[2]218 if (!out.save(fileName, cd, QLatin1String("auto"))) {
[846]219 printOut(cd.error());
[2]220 *fail = true;
221 }
222 }
223}
224
[651]225static QStringList getSources(const char *var, const char *vvar, const QStringList &baseVPaths,
226 const QString &projectDir, const ProFileEvaluator &visitor)
227{
228 QStringList vPaths = visitor.absolutePathValues(QLatin1String(vvar), projectDir);
229 vPaths += baseVPaths;
230 vPaths.removeDuplicates();
231 return visitor.absoluteFileValues(QLatin1String(var), projectDir, vPaths, 0);
232}
233
234static QStringList getSources(const ProFileEvaluator &visitor, const QString &projectDir)
235{
236 QStringList baseVPaths;
237 baseVPaths += visitor.absolutePathValues(QLatin1String("VPATH"), projectDir);
238 baseVPaths << projectDir; // QMAKE_ABSOLUTE_SOURCE_PATH
239 baseVPaths += visitor.absolutePathValues(QLatin1String("DEPENDPATH"), projectDir);
240 baseVPaths.removeDuplicates();
241
242 QStringList sourceFiles;
243
244 // app/lib template
245 sourceFiles += getSources("SOURCES", "VPATH_SOURCES", baseVPaths, projectDir, visitor);
246
247 sourceFiles += getSources("FORMS", "VPATH_FORMS", baseVPaths, projectDir, visitor);
248 sourceFiles += getSources("FORMS3", "VPATH_FORMS3", baseVPaths, projectDir, visitor);
249
250 QStringList vPathsInc = baseVPaths;
251 vPathsInc += visitor.absolutePathValues(QLatin1String("INCLUDEPATH"), projectDir);
252 vPathsInc.removeDuplicates();
253 sourceFiles += visitor.absoluteFileValues(QLatin1String("HEADERS"), projectDir, vPathsInc, 0);
254
255 sourceFiles.removeDuplicates();
256 sourceFiles.sort();
257
258 return sourceFiles;
259}
260
261static void processSources(Translator &fetchedTor,
262 const QStringList &sourceFiles, ConversionData &cd)
263{
264 QStringList sourceFilesCpp;
265 for (QStringList::const_iterator it = sourceFiles.begin(); it != sourceFiles.end(); ++it) {
266 if (it->endsWith(QLatin1String(".java"), Qt::CaseInsensitive))
267 loadJava(fetchedTor, *it, cd);
268 else if (it->endsWith(QLatin1String(".ui"), Qt::CaseInsensitive)
269 || it->endsWith(QLatin1String(".jui"), Qt::CaseInsensitive))
270 loadUI(fetchedTor, *it, cd);
271 else if (it->endsWith(QLatin1String(".js"), Qt::CaseInsensitive)
272 || it->endsWith(QLatin1String(".qs"), Qt::CaseInsensitive))
273 loadQScript(fetchedTor, *it, cd);
[846]274 else if (it->endsWith(QLatin1String(".qml"), Qt::CaseInsensitive))
275 loadQml(fetchedTor, *it, cd);
[651]276 else
277 sourceFilesCpp << *it;
278 }
279 loadCPP(fetchedTor, sourceFilesCpp, cd);
280 if (!cd.error().isEmpty())
281 printOut(cd.error());
282}
283
284static void processProjects(
285 bool topLevel, bool nestComplain, const QStringList &proFiles,
286 UpdateOptions options, const QByteArray &codecForSource,
287 const QString &targetLanguage, const QString &sourceLanguage,
288 Translator *parentTor, bool *fail);
289
290static void processProject(
291 bool nestComplain, const QFileInfo &pfi, ProFileEvaluator &visitor,
292 UpdateOptions options, const QByteArray &_codecForSource,
293 const QString &targetLanguage, const QString &sourceLanguage,
294 Translator *fetchedTor, bool *fail)
295{
296 QByteArray codecForSource = _codecForSource;
297 QStringList tmp = visitor.values(QLatin1String("CODECFORSRC"));
298 if (!tmp.isEmpty()) {
299 codecForSource = tmp.last().toLatin1();
300 if (!QTextCodec::codecForName(codecForSource)) {
[846]301 printOut(LU::tr("lupdate warning: Codec for source '%1' is invalid."
302 " Falling back to codec for tr().\n")
303 .arg(QString::fromLatin1(codecForSource)));
[651]304 codecForSource.clear();
305 }
306 }
307 if (visitor.templateType() == ProFileEvaluator::TT_Subdirs) {
308 QStringList subProFiles;
309 QDir proDir(pfi.absoluteDir());
310 foreach (const QString &subdir, visitor.values(QLatin1String("SUBDIRS"))) {
311 QString subPro = QDir::cleanPath(proDir.absoluteFilePath(subdir));
312 QFileInfo subInfo(subPro);
313 if (subInfo.isDir())
314 subProFiles << (subPro + QLatin1Char('/')
315 + subInfo.fileName() + QLatin1String(".pro"));
316 else
317 subProFiles << subPro;
318 }
319 processProjects(false, nestComplain, subProFiles, options, codecForSource,
320 targetLanguage, sourceLanguage, fetchedTor, fail);
321 } else {
322 ConversionData cd;
323 cd.m_noUiLines = options & NoUiLines;
324 cd.m_codecForSource = codecForSource;
325 cd.m_includePath = visitor.values(QLatin1String("INCLUDEPATH"));
326 QStringList sourceFiles = getSources(visitor, pfi.absolutePath());
327 QSet<QString> sourceDirs;
328 sourceDirs.insert(QDir::cleanPath(pfi.absolutePath()) + QLatin1Char('/'));
329 foreach (const QString &sf, sourceFiles)
330 sourceDirs.insert(sf.left(sf.lastIndexOf(QLatin1Char('/')) + 1));
331 QStringList rootList = sourceDirs.toList();
332 rootList.sort();
333 for (int prev = 0, curr = 1; curr < rootList.length(); )
334 if (rootList.at(curr).startsWith(rootList.at(prev)))
335 rootList.removeAt(curr);
336 else
337 prev = curr++;
338 cd.m_projectRoots = QSet<QString>::fromList(rootList);
339 processSources(*fetchedTor, sourceFiles, cd);
340 }
341}
342
343static void processProjects(
344 bool topLevel, bool nestComplain, const QStringList &proFiles,
345 UpdateOptions options, const QByteArray &codecForSource,
346 const QString &targetLanguage, const QString &sourceLanguage,
347 Translator *parentTor, bool *fail)
348{
349 foreach (const QString &proFile, proFiles) {
350 ProFileEvaluator visitor;
351 visitor.setVerbose(options & Verbose);
352
[846]353 QHash<QString, QStringList> lupdateConfig;
354 lupdateConfig.insert(QLatin1String("CONFIG"), QStringList(QLatin1String("lupdate_run")));
355 visitor.addVariables(lupdateConfig);
356
[651]357 QFileInfo pfi(proFile);
358 ProFile pro(pfi.absoluteFilePath());
359 if (!visitor.queryProFile(&pro) || !visitor.accept(&pro)) {
360 if (topLevel)
361 *fail = true;
362 continue;
363 }
364
365 if (visitor.contains(QLatin1String("TRANSLATIONS"))) {
366 if (parentTor) {
367 if (topLevel) {
[846]368 std::cerr << qPrintable(LU::tr("lupdate warning: TS files from command line "
369 "will override TRANSLATIONS in %1.\n").arg(proFile));
[651]370 goto noTrans;
371 } else if (nestComplain) {
[846]372 std::cerr << qPrintable(LU::tr("lupdate warning: TS files from command line "
373 "prevent recursing into %1.\n").arg(proFile));
[651]374 continue;
375 }
376 }
377 QStringList tsFiles;
378 QDir proDir(pfi.absolutePath());
379 foreach (const QString &tsFile, visitor.values(QLatin1String("TRANSLATIONS")))
380 tsFiles << QFileInfo(proDir, tsFile).filePath();
381 if (tsFiles.isEmpty()) {
382 // This might mean either a buggy PRO file or an intentional detach -
383 // we can't know without seeing the actual RHS of the assignment ...
384 // Just assume correctness and be silent.
385 continue;
386 }
387 Translator tor;
[769]388 bool setCodec = false;
[651]389 QStringList tmp = visitor.values(QLatin1String("CODEC"))
390 + visitor.values(QLatin1String("DEFAULTCODEC"))
391 + visitor.values(QLatin1String("CODECFORTR"));
392 if (!tmp.isEmpty()) {
[769]393 tor.setCodecName(tmp.last().toLatin1());
394 setCodec = true;
[651]395 }
396 processProject(false, pfi, visitor, options, codecForSource,
397 targetLanguage, sourceLanguage, &tor, fail);
[769]398 updateTsFiles(tor, tsFiles, setCodec, sourceLanguage, targetLanguage, options, fail);
[651]399 continue;
400 }
401 noTrans:
402 if (!parentTor) {
403 if (topLevel)
[846]404 std::cerr << qPrintable(LU::tr("lupdate warning: no TS files specified. Only diagnostics "
405 "will be produced for '%1'.\n").arg(proFile));
[651]406 Translator tor;
407 processProject(nestComplain, pfi, visitor, options, codecForSource,
408 targetLanguage, sourceLanguage, &tor, fail);
409 } else {
410 processProject(nestComplain, pfi, visitor, options, codecForSource,
411 targetLanguage, sourceLanguage, parentTor, fail);
412 }
413 }
414}
415
[2]416int main(int argc, char **argv)
417{
418 QCoreApplication app(argc, argv);
[846]419#ifndef Q_OS_WIN32
420 QTranslator translator;
421 QTranslator qtTranslator;
422 QString sysLocale = QLocale::system().name();
423 QString resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
424 if (translator.load(QLatin1String("linguist_") + sysLocale, resourceDir)
425 && qtTranslator.load(QLatin1String("qt_") + sysLocale, resourceDir)) {
426 app.installTranslator(&translator);
427 app.installTranslator(&qtTranslator);
428 }
429#endif // Q_OS_WIN32
[2]430
[846]431 m_defaultExtensions = QLatin1String("java,jui,ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx,js,qs,qml");
432
[2]433 QStringList args = app.arguments();
434 QStringList tsFileNames;
435 QStringList proFiles;
[561]436 QMultiHash<QString, QString> allCSources;
437 QSet<QString> projectRoots;
[2]438 QStringList sourceFiles;
[561]439 QStringList includePath;
[2]440 QString targetLanguage;
441 QString sourceLanguage;
[651]442 QByteArray codecForTr;
[2]443
444 UpdateOptions options =
445 Verbose | // verbose is on by default starting with Qt 4.2
446 HeuristicSameText | HeuristicSimilarText | HeuristicNumber;
447 int numFiles = 0;
448 bool metTsFlag = false;
449 bool recursiveScan = true;
450
451 QString extensions = m_defaultExtensions;
[561]452 QSet<QString> extensionsNameFilters;
[2]453
454 for (int i = 1; i < argc; ++i) {
455 QString arg = args.at(i);
456 if (arg == QLatin1String("-help")
457 || arg == QLatin1String("--help")
458 || arg == QLatin1String("-h")) {
459 printUsage();
460 return 0;
461 } else if (arg == QLatin1String("-pluralonly")) {
462 options |= PluralOnly;
463 continue;
464 } else if (arg == QLatin1String("-noobsolete")
465 || arg == QLatin1String("-no-obsolete")) {
466 options |= NoObsolete;
467 continue;
468 } else if (arg == QLatin1String("-silent")) {
469 options &= ~Verbose;
470 continue;
471 } else if (arg == QLatin1String("-target-language")) {
472 ++i;
473 if (i == argc) {
[846]474 printOut(LU::tr("The option -target-language requires a parameter.\n"));
[2]475 return 1;
476 }
477 targetLanguage = args[i];
478 continue;
479 } else if (arg == QLatin1String("-source-language")) {
480 ++i;
481 if (i == argc) {
[846]482 printOut(LU::tr("The option -source-language requires a parameter.\n"));
[2]483 return 1;
484 }
485 sourceLanguage = args[i];
486 continue;
487 } else if (arg == QLatin1String("-disable-heuristic")) {
488 ++i;
489 if (i == argc) {
[846]490 printOut(LU::tr("The option -disable-heuristic requires a parameter.\n"));
[2]491 return 1;
492 }
493 arg = args[i];
494 if (arg == QLatin1String("sametext")) {
495 options &= ~HeuristicSameText;
496 } else if (arg == QLatin1String("similartext")) {
497 options &= ~HeuristicSimilarText;
498 } else if (arg == QLatin1String("number")) {
499 options &= ~HeuristicNumber;
500 } else {
[846]501 printOut(LU::tr("Invalid heuristic name passed to -disable-heuristic.\n"));
[2]502 return 1;
503 }
504 continue;
505 } else if (arg == QLatin1String("-locations")) {
506 ++i;
507 if (i == argc) {
[846]508 printOut(LU::tr("The option -locations requires a parameter.\n"));
[2]509 return 1;
510 }
511 if (args[i] == QLatin1String("none")) {
512 options |= NoLocations;
513 } else if (args[i] == QLatin1String("relative")) {
514 options |= RelativeLocations;
515 } else if (args[i] == QLatin1String("absolute")) {
516 options |= AbsoluteLocations;
517 } else {
[846]518 printOut(LU::tr("Invalid parameter passed to -locations.\n"));
[2]519 return 1;
520 }
521 continue;
522 } else if (arg == QLatin1String("-no-ui-lines")) {
523 options |= NoUiLines;
524 continue;
525 } else if (arg == QLatin1String("-verbose")) {
526 options |= Verbose;
527 continue;
528 } else if (arg == QLatin1String("-no-recursive")) {
529 recursiveScan = false;
530 continue;
531 } else if (arg == QLatin1String("-recursive")) {
532 recursiveScan = true;
533 continue;
534 } else if (arg == QLatin1String("-no-sort")
535 || arg == QLatin1String("-nosort")) {
536 options |= NoSort;
537 continue;
538 } else if (arg == QLatin1String("-version")) {
539 printOut(QObject::tr("lupdate version %1\n").arg(QLatin1String(QT_VERSION_STR)));
540 return 0;
[651]541 } else if (arg == QLatin1String("-codecfortr")) {
542 ++i;
543 if (i == argc) {
[846]544 printOut(LU::tr("The -codecfortr option should be followed by a codec name.\n"));
[651]545 return 1;
546 }
547 codecForTr = args[i].toLatin1();
548 continue;
[2]549 } else if (arg == QLatin1String("-ts")) {
550 metTsFlag = true;
551 continue;
552 } else if (arg == QLatin1String("-extensions")) {
553 ++i;
554 if (i == argc) {
[846]555 printOut(LU::tr("The -extensions option should be followed by an extension list.\n"));
[2]556 return 1;
557 }
558 extensions = args[i];
559 continue;
560 } else if (arg == QLatin1String("-pro")) {
561 ++i;
562 if (i == argc) {
[846]563 printOut(LU::tr("The -pro option should be followed by a filename of .pro file.\n"));
[2]564 return 1;
565 }
566 proFiles += args[i];
567 numFiles++;
568 continue;
[561]569 } else if (arg.startsWith(QLatin1String("-I"))) {
570 if (arg.length() == 2) {
571 ++i;
572 if (i == argc) {
[846]573 printOut(LU::tr("The -I option should be followed by a path.\n"));
[561]574 return 1;
575 }
576 includePath += args[i];
577 } else {
578 includePath += args[i].mid(2);
579 }
580 continue;
[2]581 } else if (arg.startsWith(QLatin1String("-")) && arg != QLatin1String("-")) {
[846]582 printOut(LU::tr("Unrecognized option '%1'.\n").arg(arg));
[2]583 return 1;
584 }
585
[846]586 QStringList files;
587 if (arg.startsWith(QLatin1String("@"))) {
588 QFile lstFile(arg.mid(1));
589 if (!lstFile.open(QIODevice::ReadOnly)) {
590 printOut(LU::tr("lupdate error: List file '%1' is not readable.\n")
591 .arg(lstFile.fileName()));
592 return 1;
593 }
594 while (!lstFile.atEnd())
595 files << QString::fromLocal8Bit(lstFile.readLine().trimmed());
596 } else {
597 files << arg;
598 }
[2]599 if (metTsFlag) {
[846]600 foreach (const QString &file, files) {
601 bool found = false;
602 foreach (const Translator::FileFormat &fmt, Translator::registeredFileFormats()) {
603 if (file.endsWith(QLatin1Char('.') + fmt.extension, Qt::CaseInsensitive)) {
604 QFileInfo fi(file);
605 if (!fi.exists() || fi.isWritable()) {
606 tsFileNames.append(QFileInfo(file).absoluteFilePath());
607 } else {
608 printOut(LU::tr("lupdate warning: For some reason, '%1' is not writable.\n")
609 .arg(file));
610 }
611 found = true;
612 break;
[2]613 }
614 }
[846]615 if (!found) {
616 printOut(LU::tr("lupdate error: File '%1' has no recognized extension.\n")
617 .arg(file));
618 return 1;
619 }
[2]620 }
[651]621 numFiles++;
[2]622 } else {
[846]623 foreach (const QString &file, files) {
624 QFileInfo fi(file);
625 if (!fi.exists()) {
626 printOut(LU::tr("lupdate error: File '%1' does not exist.\n").arg(file));
627 return 1;
628 }
629 if (file.endsWith(QLatin1String(".pro"), Qt::CaseInsensitive)
630 || file.endsWith(QLatin1String(".pri"), Qt::CaseInsensitive)) {
631 proFiles << file;
632 } else if (fi.isDir()) {
633 if (options & Verbose)
634 printOut(LU::tr("Scanning directory '%1'...\n").arg(file));
635 QDir dir = QDir(fi.filePath());
636 projectRoots.insert(dir.absolutePath() + QLatin1Char('/'));
637 if (extensionsNameFilters.isEmpty()) {
638 foreach (QString ext, extensions.split(QLatin1Char(','))) {
639 ext = ext.trimmed();
640 if (ext.startsWith(QLatin1Char('.')))
641 ext.remove(0, 1);
642 extensionsNameFilters.insert(ext);
643 }
[561]644 }
[846]645 QDir::Filters filters = QDir::Files | QDir::NoSymLinks;
646 if (recursiveScan)
647 filters |= QDir::AllDirs | QDir::NoDotAndDotDot;
648 QFileInfoList fileinfolist;
649 recursiveFileInfoList(dir, extensionsNameFilters, filters, &fileinfolist);
650 int scanRootLen = dir.absolutePath().length();
651 foreach (const QFileInfo &fi, fileinfolist) {
652 QString fn = QDir::cleanPath(fi.absoluteFilePath());
653 sourceFiles << fn;
[561]654
[846]655 if (!fn.endsWith(QLatin1String(".java"))
656 && !fn.endsWith(QLatin1String(".jui"))
657 && !fn.endsWith(QLatin1String(".ui"))
658 && !fn.endsWith(QLatin1String(".js"))
659 && !fn.endsWith(QLatin1String(".qs"))
660 && !fn.endsWith(QLatin1String(".qml"))) {
661 int offset = 0;
662 int depth = 0;
663 do {
664 offset = fn.lastIndexOf(QLatin1Char('/'), offset - 1);
665 QString ffn = fn.mid(offset + 1);
666 allCSources.insert(ffn, fn);
667 } while (++depth < 3 && offset > scanRootLen);
668 }
[561]669 }
[846]670 } else {
671 sourceFiles << QDir::cleanPath(fi.absoluteFilePath());;
672 projectRoots.insert(fi.absolutePath() + QLatin1Char('/'));
[2]673 }
674 }
[651]675 numFiles++;
[2]676 }
677 } // for args
678
[651]679 if (numFiles == 0) {
680 printUsage();
681 return 1;
682 }
[2]683
[651]684 if (!targetLanguage.isEmpty() && tsFileNames.count() != 1)
[846]685 printOut(LU::tr("lupdate warning: -target-language usually only"
686 " makes sense with exactly one TS file.\n"));
[651]687 if (!codecForTr.isEmpty() && tsFileNames.isEmpty())
[846]688 printOut(LU::tr("lupdate warning: -codecfortr has no effect without -ts.\n"));
[651]689
[2]690 bool fail = false;
[651]691 if (proFiles.isEmpty()) {
692 if (tsFileNames.isEmpty())
[846]693 printOut(LU::tr("lupdate warning:"
694 " no TS files specified. Only diagnostics will be produced.\n"));
[651]695
696 Translator fetchedTor;
[2]697 ConversionData cd;
698 cd.m_noUiLines = options & NoUiLines;
[561]699 cd.m_projectRoots = projectRoots;
700 cd.m_includePath = includePath;
701 cd.m_allCSources = allCSources;
[651]702 fetchedTor.setCodecName(codecForTr);
703 processSources(fetchedTor, sourceFiles, cd);
[769]704 updateTsFiles(fetchedTor, tsFileNames, !codecForTr.isEmpty(),
[651]705 sourceLanguage, targetLanguage, options, &fail);
706 } else {
707 if (!sourceFiles.isEmpty() || !includePath.isEmpty()) {
[846]708 printOut(LU::tr("lupdate error:"
709 " Both project and source files / include paths specified.\n"));
[651]710 return 1;
[2]711 }
[651]712 if (!tsFileNames.isEmpty()) {
713 Translator fetchedTor;
714 fetchedTor.setCodecName(codecForTr);
715 processProjects(true, true, proFiles, options, QByteArray(),
716 targetLanguage, sourceLanguage, &fetchedTor, &fail);
[769]717 updateTsFiles(fetchedTor, tsFileNames, !codecForTr.isEmpty(),
[651]718 sourceLanguage, targetLanguage, options, &fail);
719 } else {
720 processProjects(true, false, proFiles, options, QByteArray(),
721 targetLanguage, sourceLanguage, 0, &fail);
[2]722 }
723 }
724 return fail ? 1 : 0;
725}
Note: See TracBrowser for help on using the repository browser.