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
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
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**
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.
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**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "lupdate.h"
43
44#include <translator.h>
45#include <profileevaluator.h>
46
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>
55#include <QtCore/QTranslator>
56#include <QtCore/QLibraryInfo>
57
58#include <iostream>
59
60static QString m_defaultExtensions;
61
62static void printOut(const QString & out)
63{
64 std::cerr << qPrintable(out);
65}
66
67class LU {
68 Q_DECLARE_TR_FUNCTIONS(LUpdate)
69};
70
71static void recursiveFileInfoList(const QDir &dir,
72 const QSet<QString> &nameFilters, QDir::Filters filter,
73 QFileInfoList *fileinfolist)
74{
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);
80}
81
82static void printUsage()
83{
84 printOut(LU::tr(
85 "Usage:\n"
86 " lupdate [options] [project-file]...\n"
87 " lupdate [options] [source-file|path|@lst-file]... -ts ts-files|@lst-file\n\n"
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"
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"
105 " Do not sort contexts in TS files.\n"
106 " -no-recursive\n"
107 " Do not recursively scan the following directories.\n"
108 " -recursive\n"
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"
113 " -locations {absolute|relative|none}\n"
114 " Specify/override how source code references are saved in TS files.\n"
115 " Default is absolute.\n"
116 " -no-ui-lines\n"
117 " Do not record line numbers in references to UI files.\n"
118 " -disable-heuristic {sametext|similartext|number}\n"
119 " Disable the named merge heuristic. Can be specified multiple times.\n"
120 " -pro <filename>\n"
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"
123 " -source-language <language>[_<region>]\n"
124 " Specify the language of the source strings for new files.\n"
125 " Defaults to POSIX if not specified.\n"
126 " -target-language <language>[_<region>]\n"
127 " Specify the language of the translations for new files.\n"
128 " Guessed from the file name if not specified.\n"
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"
134 " -version\n"
135 " Display the version of lupdate and exit.\n"
136 " @lst-file\n"
137 " Read additional file names (one per line) from lst-file.\n"
138 ).arg(m_defaultExtensions));
139}
140
141static void updateTsFiles(const Translator &fetchedTor, const QStringList &tsFileNames,
142 bool setCodec, const QString &sourceLanguage, const QString &targetLanguage,
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"))) {
154 printOut(cd.error());
155 *fail = true;
156 continue;
157 }
158 tor.resolveDuplicates();
159 cd.clearErrors();
160 if (setCodec && fetchedTor.codec() != tor.codec())
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())));
165 if (!targetLanguage.isEmpty() && targetLanguage != tor.languageCode())
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()));
169 if (!sourceLanguage.isEmpty() && sourceLanguage != tor.sourceLanguageCode())
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()));
173 } else {
174 if (setCodec)
175 tor.setCodec(fetchedTor.codec());
176 if (!targetLanguage.isEmpty())
177 tor.setLanguageCode(targetLanguage);
178 else
179 tor.setLanguageCode(Translator::guessLanguageCodeFromFileName(fileName));
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)
191 printOut(LU::tr("Updating '%1'...\n").arg(fn));
192
193 UpdateOptions theseOptions = options;
194 if (tor.locationsType() == Translator::NoLocations) // Could be set from file
195 theseOptions |= NoLocations;
196 Translator out = merge(tor, fetchedTor, theseOptions, err);
197 if (setCodec)
198 out.setCodec(fetchedTor.codec());
199
200 if ((options & Verbose) && !err.isEmpty()) {
201 printOut(err);
202 err.clear();
203 }
204 if (options & PluralOnly) {
205 if (options & Verbose)
206 printOut(LU::tr("Stripping non plural forms in '%1'...\n").arg(fn));
207 out.stripNonPluralForms();
208 }
209 if (options & NoObsolete)
210 out.stripObsoleteMessages();
211 out.stripEmptyContexts();
212
213 out.normalizeTranslations(cd);
214 if (!cd.errors().isEmpty()) {
215 printOut(cd.error());
216 cd.clearErrors();
217 }
218 if (!out.save(fileName, cd, QLatin1String("auto"))) {
219 printOut(cd.error());
220 *fail = true;
221 }
222 }
223}
224
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);
274 else if (it->endsWith(QLatin1String(".qml"), Qt::CaseInsensitive))
275 loadQml(fetchedTor, *it, cd);
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)) {
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)));
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
353 QHash<QString, QStringList> lupdateConfig;
354 lupdateConfig.insert(QLatin1String("CONFIG"), QStringList(QLatin1String("lupdate_run")));
355 visitor.addVariables(lupdateConfig);
356
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) {
368 std::cerr << qPrintable(LU::tr("lupdate warning: TS files from command line "
369 "will override TRANSLATIONS in %1.\n").arg(proFile));
370 goto noTrans;
371 } else if (nestComplain) {
372 std::cerr << qPrintable(LU::tr("lupdate warning: TS files from command line "
373 "prevent recursing into %1.\n").arg(proFile));
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;
388 bool setCodec = false;
389 QStringList tmp = visitor.values(QLatin1String("CODEC"))
390 + visitor.values(QLatin1String("DEFAULTCODEC"))
391 + visitor.values(QLatin1String("CODECFORTR"));
392 if (!tmp.isEmpty()) {
393 tor.setCodecName(tmp.last().toLatin1());
394 setCodec = true;
395 }
396 processProject(false, pfi, visitor, options, codecForSource,
397 targetLanguage, sourceLanguage, &tor, fail);
398 updateTsFiles(tor, tsFiles, setCodec, sourceLanguage, targetLanguage, options, fail);
399 continue;
400 }
401 noTrans:
402 if (!parentTor) {
403 if (topLevel)
404 std::cerr << qPrintable(LU::tr("lupdate warning: no TS files specified. Only diagnostics "
405 "will be produced for '%1'.\n").arg(proFile));
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
416int main(int argc, char **argv)
417{
418 QCoreApplication app(argc, argv);
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
430
431 m_defaultExtensions = QLatin1String("java,jui,ui,c,c++,cc,cpp,cxx,ch,h,h++,hh,hpp,hxx,js,qs,qml");
432
433 QStringList args = app.arguments();
434 QStringList tsFileNames;
435 QStringList proFiles;
436 QMultiHash<QString, QString> allCSources;
437 QSet<QString> projectRoots;
438 QStringList sourceFiles;
439 QStringList includePath;
440 QString targetLanguage;
441 QString sourceLanguage;
442 QByteArray codecForTr;
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;
452 QSet<QString> extensionsNameFilters;
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) {
474 printOut(LU::tr("The option -target-language requires a parameter.\n"));
475 return 1;
476 }
477 targetLanguage = args[i];
478 continue;
479 } else if (arg == QLatin1String("-source-language")) {
480 ++i;
481 if (i == argc) {
482 printOut(LU::tr("The option -source-language requires a parameter.\n"));
483 return 1;
484 }
485 sourceLanguage = args[i];
486 continue;
487 } else if (arg == QLatin1String("-disable-heuristic")) {
488 ++i;
489 if (i == argc) {
490 printOut(LU::tr("The option -disable-heuristic requires a parameter.\n"));
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 {
501 printOut(LU::tr("Invalid heuristic name passed to -disable-heuristic.\n"));
502 return 1;
503 }
504 continue;
505 } else if (arg == QLatin1String("-locations")) {
506 ++i;
507 if (i == argc) {
508 printOut(LU::tr("The option -locations requires a parameter.\n"));
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 {
518 printOut(LU::tr("Invalid parameter passed to -locations.\n"));
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;
541 } else if (arg == QLatin1String("-codecfortr")) {
542 ++i;
543 if (i == argc) {
544 printOut(LU::tr("The -codecfortr option should be followed by a codec name.\n"));
545 return 1;
546 }
547 codecForTr = args[i].toLatin1();
548 continue;
549 } else if (arg == QLatin1String("-ts")) {
550 metTsFlag = true;
551 continue;
552 } else if (arg == QLatin1String("-extensions")) {
553 ++i;
554 if (i == argc) {
555 printOut(LU::tr("The -extensions option should be followed by an extension list.\n"));
556 return 1;
557 }
558 extensions = args[i];
559 continue;
560 } else if (arg == QLatin1String("-pro")) {
561 ++i;
562 if (i == argc) {
563 printOut(LU::tr("The -pro option should be followed by a filename of .pro file.\n"));
564 return 1;
565 }
566 proFiles += args[i];
567 numFiles++;
568 continue;
569 } else if (arg.startsWith(QLatin1String("-I"))) {
570 if (arg.length() == 2) {
571 ++i;
572 if (i == argc) {
573 printOut(LU::tr("The -I option should be followed by a path.\n"));
574 return 1;
575 }
576 includePath += args[i];
577 } else {
578 includePath += args[i].mid(2);
579 }
580 continue;
581 } else if (arg.startsWith(QLatin1String("-")) && arg != QLatin1String("-")) {
582 printOut(LU::tr("Unrecognized option '%1'.\n").arg(arg));
583 return 1;
584 }
585
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 }
599 if (metTsFlag) {
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;
613 }
614 }
615 if (!found) {
616 printOut(LU::tr("lupdate error: File '%1' has no recognized extension.\n")
617 .arg(file));
618 return 1;
619 }
620 }
621 numFiles++;
622 } else {
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 }
644 }
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;
654
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 }
669 }
670 } else {
671 sourceFiles << QDir::cleanPath(fi.absoluteFilePath());;
672 projectRoots.insert(fi.absolutePath() + QLatin1Char('/'));
673 }
674 }
675 numFiles++;
676 }
677 } // for args
678
679 if (numFiles == 0) {
680 printUsage();
681 return 1;
682 }
683
684 if (!targetLanguage.isEmpty() && tsFileNames.count() != 1)
685 printOut(LU::tr("lupdate warning: -target-language usually only"
686 " makes sense with exactly one TS file.\n"));
687 if (!codecForTr.isEmpty() && tsFileNames.isEmpty())
688 printOut(LU::tr("lupdate warning: -codecfortr has no effect without -ts.\n"));
689
690 bool fail = false;
691 if (proFiles.isEmpty()) {
692 if (tsFileNames.isEmpty())
693 printOut(LU::tr("lupdate warning:"
694 " no TS files specified. Only diagnostics will be produced.\n"));
695
696 Translator fetchedTor;
697 ConversionData cd;
698 cd.m_noUiLines = options & NoUiLines;
699 cd.m_projectRoots = projectRoots;
700 cd.m_includePath = includePath;
701 cd.m_allCSources = allCSources;
702 fetchedTor.setCodecName(codecForTr);
703 processSources(fetchedTor, sourceFiles, cd);
704 updateTsFiles(fetchedTor, tsFileNames, !codecForTr.isEmpty(),
705 sourceLanguage, targetLanguage, options, &fail);
706 } else {
707 if (!sourceFiles.isEmpty() || !includePath.isEmpty()) {
708 printOut(LU::tr("lupdate error:"
709 " Both project and source files / include paths specified.\n"));
710 return 1;
711 }
712 if (!tsFileNames.isEmpty()) {
713 Translator fetchedTor;
714 fetchedTor.setCodecName(codecForTr);
715 processProjects(true, true, proFiles, options, QByteArray(),
716 targetLanguage, sourceLanguage, &fetchedTor, &fail);
717 updateTsFiles(fetchedTor, tsFileNames, !codecForTr.isEmpty(),
718 sourceLanguage, targetLanguage, options, &fail);
719 } else {
720 processProjects(true, false, proFiles, options, QByteArray(),
721 targetLanguage, sourceLanguage, 0, &fail);
722 }
723 }
724 return fail ? 1 : 0;
725}
Note: See TracBrowser for help on using the repository browser.