summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederik Gladhorn <frederik.gladhorn@digia.com>2013-08-27 22:46:13 +0200
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2013-08-27 22:46:13 +0200
commit3e7f3a198c26fce9d19fab38d33444d05ea29c74 (patch)
treed16d5de3df640ca794e383ca332ee8a211111fcf
parent140fa19ad926f15246c8a501f8a3ccdeda4c5420 (diff)
parent262744ac1b6ba95d72bedc7679e2b49c11c74803 (diff)
Merge remote-tracking branch 'origin/stable' into dev
-rw-r--r--src/designer/src/lib/uilib/formbuilder.cpp2
-rw-r--r--src/linguist/lconvert/main.cpp2
-rw-r--r--src/linguist/linguist/mainwindow.cpp102
-rw-r--r--src/linguist/linguist/mainwindow.h4
-rw-r--r--src/linguist/lrelease/main.cpp14
-rw-r--r--src/linguist/lupdate/cpp.cpp6
-rw-r--r--src/linguist/lupdate/main.cpp62
-rw-r--r--src/linguist/shared/ioutils.cpp5
-rw-r--r--src/linguist/shared/ioutils.h1
-rw-r--r--src/linguist/shared/profileevaluator.cpp4
-rw-r--r--src/linguist/shared/profileevaluator.h4
-rw-r--r--src/linguist/shared/proparser.pri2
-rw-r--r--src/linguist/shared/qmakebuiltins.cpp81
-rw-r--r--src/linguist/shared/qmakeevaluator.cpp205
-rw-r--r--src/linguist/shared/qmakeevaluator.h26
-rw-r--r--src/linguist/shared/qmakeglobals.cpp54
-rw-r--r--src/linguist/shared/qmakeparser.cpp24
-rw-r--r--src/linguist/shared/qmakeparser.h4
-rw-r--r--src/linguist/shared/qmakevfs.cpp192
-rw-r--r--src/linguist/shared/qmakevfs.h (renamed from src/linguist/tests/tst_linguist.h)52
-rw-r--r--src/linguist/shared/translator.cpp26
-rw-r--r--src/linguist/shared/translator.h1
-rw-r--r--src/linguist/tests/data/main.cpp75
-rw-r--r--src/linguist/tests/data/test.pro4
-rw-r--r--src/linguist/tests/tests.pro17
-rw-r--r--src/linguist/tests/tst_linguist.cpp45
-rw-r--r--src/linguist/tests/tst_lupdate.cpp192
-rw-r--r--src/linguist/tests/tst_simtexth.cpp73
-rw-r--r--tests/auto/linguist/lupdate/testdata/good/parsecpp/main.cpp3
-rw-r--r--tests/auto/linguist/lupdate/testdata/good/parsecpp/project.pro2
-rw-r--r--tests/auto/linguist/lupdate/testdata/good/proparsingpri/project.pro5
31 files changed, 590 insertions, 699 deletions
diff --git a/src/designer/src/lib/uilib/formbuilder.cpp b/src/designer/src/lib/uilib/formbuilder.cpp
index dfa10807d..90b114b9f 100644
--- a/src/designer/src/lib/uilib/formbuilder.cpp
+++ b/src/designer/src/lib/uilib/formbuilder.cpp
@@ -523,7 +523,7 @@ void QFormBuilder::applyProperties(QObject *o, const QList<DomProperty*> &proper
const DomPropertyList::const_iterator cend = properties.constEnd();
for (DomPropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) {
const QVariant v = toVariant(o->metaObject(), *it);
- if (v.isNull())
+ if (!v.isValid()) // QTBUG-33130, do not fall for QVariant(QString()).isNull() == true.
continue;
const QString attributeName = (*it)->attributeName();
diff --git a/src/linguist/lconvert/main.cpp b/src/linguist/lconvert/main.cpp
index 68c08622f..e576d28f6 100644
--- a/src/linguist/lconvert/main.cpp
+++ b/src/linguist/lconvert/main.cpp
@@ -191,7 +191,7 @@ int main(int argc, char *argv[])
if (++i >= args.size())
return usage(args);
inFormat = args[i];
- } else if (args[i] == QLatin1String("-drop-tag")) {
+ } else if (args[i] == QLatin1String("-drop-tag") || args[i] == QLatin1String("-drop-tags")) {
if (++i >= args.size())
return usage(args);
cd.m_dropTags.append(args[i]);
diff --git a/src/linguist/linguist/mainwindow.cpp b/src/linguist/linguist/mainwindow.cpp
index def4586e8..509406e27 100644
--- a/src/linguist/linguist/mainwindow.cpp
+++ b/src/linguist/linguist/mainwindow.cpp
@@ -268,7 +268,6 @@ MainWindow::MainWindow()
m_findMatchCase(Qt::CaseInsensitive),
m_findIgnoreAccelerators(true),
m_findWhere(DataModel::NoLocation),
- m_foundWhere(DataModel::NoLocation),
m_translationSettingsDialog(0),
m_settingCurrentMessage(false),
m_fileActiveModel(-1),
@@ -965,9 +964,9 @@ void MainWindow::print()
}
}
-bool MainWindow::searchItem(const QString &searchWhat)
+bool MainWindow::searchItem(DataModel::FindLocation where, const QString &searchWhat)
{
- if ((m_findWhere & m_foundWhere) == 0)
+ if ((m_findWhere & where) == 0)
return false;
QString text = searchWhat;
@@ -994,41 +993,28 @@ void MainWindow::findAgain()
bool hadMessage = false;
for (int i = 0; i < m_dataModel->modelCount(); ++i) {
if (MessageItem *m = m_dataModel->messageItem(dataIndex, i)) {
- // Note: we do not look into plurals on grounds of them not
- // containing anything much different from the singular.
- if (hadMessage) {
- m_foundWhere = DataModel::Translations;
- if (!searchItem(m->translation()))
- m_foundWhere = DataModel::NoLocation;
- } else {
- switch (m_foundWhere) {
- case 0:
- m_foundWhere = DataModel::SourceText;
- // fall-through to search source text
- case DataModel::SourceText:
- if (searchItem(m->text()))
+ bool found = true;
+ do {
+ if (!hadMessage) {
+ if (searchItem(DataModel::SourceText, m->text()))
break;
- if (searchItem(m->pluralText()))
+ if (searchItem(DataModel::SourceText, m->pluralText()))
break;
- m_foundWhere = DataModel::Translations;
- // fall-through to search translation
- case DataModel::Translations:
- if (searchItem(m->translation()))
+ if (searchItem(DataModel::Comments, m->comment()))
break;
- m_foundWhere = DataModel::Comments;
- // fall-through to search comment
- case DataModel::Comments:
- if (searchItem(m->comment()))
+ if (searchItem(DataModel::Comments, m->extraComment()))
break;
- if (searchItem(m->extraComment()))
- break;
- if (searchItem(m->translatorComment()))
- break;
- m_foundWhere = DataModel::NoLocation;
- // did not find the search string in this message
}
- }
- if (m_foundWhere != DataModel::NoLocation) {
+ foreach (const QString &trans, m->translations())
+ if (searchItem(DataModel::Translations, trans))
+ goto didfind;
+ if (searchItem(DataModel::Comments, m->translatorComment()))
+ break;
+ found = false;
+ // did not find the search string in this message
+ } while (0);
+ if (found) {
+ didfind:
setCurrentMessage(realIndex, i);
// determine whether the search wrapped
@@ -1057,7 +1043,6 @@ void MainWindow::findAgain()
qApp->beep();
QMessageBox::warning(m_findDialog, tr("Qt Linguist"),
tr("Cannot find the string '%1'.").arg(m_findText));
- m_foundWhere = DataModel::NoLocation;
}
void MainWindow::showBatchTranslateDialog()
@@ -1492,13 +1477,14 @@ void MainWindow::selectedMessageChanged(const QModelIndex &sortedIndex, const QM
return;
}
+ int model = -1;
+ MessageItem *m = 0;
QModelIndex index = m_sortedMessagesModel->mapToSource(sortedIndex);
if (index.isValid()) {
- int model = (index.column() && (index.column() - 1 < m_dataModel->modelCount())) ?
+ model = (index.column() && (index.column() - 1 < m_dataModel->modelCount())) ?
index.column() - 1 : m_currentIndex.model();
m_currentIndex = m_messageModel->dataIndex(index, model);
m_messageEditor->showMessage(m_currentIndex);
- MessageItem *m = 0;
if (model >= 0 && (m = m_dataModel->messageItem(m_currentIndex))) {
if (m_dataModel->isModelWritable(model) && !m->isObsolete())
m_phraseView->setSourceText(m_currentIndex.model(), m->text());
@@ -1513,30 +1499,14 @@ void MainWindow::selectedMessageChanged(const QModelIndex &sortedIndex, const QM
}
m_phraseView->setSourceText(-1, QString());
}
- if (m && !m->fileName().isEmpty()) {
- if (hasFormPreview(m->fileName())) {
- m_sourceAndFormView->setCurrentWidget(m_formPreviewView);
- m_formPreviewView->setSourceContext(model, m);
- } else {
- m_sourceAndFormView->setCurrentWidget(m_sourceCodeView);
- QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir();
- QString fileName = QDir::cleanPath(dir.absoluteFilePath(m->fileName()));
- m_sourceCodeView->setSourceContext(fileName, m->lineNumber());
- }
- m_errorsView->setEnabled(true);
- } else {
- m_sourceAndFormView->setCurrentWidget(m_sourceCodeView);
- m_sourceCodeView->setSourceContext(QString(), 0);
- m_errorsView->setEnabled(false);
- }
+ m_errorsView->setEnabled(m != 0);
updateDanger(m_currentIndex, true);
} else {
m_currentIndex = MultiDataIndex();
m_messageEditor->showNothing();
m_phraseView->setSourceText(-1, QString());
- m_sourceAndFormView->setCurrentWidget(m_sourceCodeView);
- m_sourceCodeView->setSourceContext(QString(), 0);
}
+ updateSourceView(model, m);
updatePhraseBookActions();
m_ui.actionSelectAll->setEnabled(index.isValid());
@@ -1985,15 +1955,14 @@ void MainWindow::updateLatestModel(int model)
m_currentIndex = MultiDataIndex(model, m_currentIndex.context(), m_currentIndex.message());
bool enable = false;
bool enableRw = false;
+ MessageItem *item = 0;
if (model >= 0) {
enable = true;
if (m_dataModel->isModelWritable(model))
enableRw = true;
if (m_currentIndex.isValid()) {
- if (MessageItem *item = m_dataModel->messageItem(m_currentIndex)) {
- if (!item->fileName().isEmpty() && hasFormPreview(item->fileName()))
- m_formPreviewView->setSourceContext(model, item);
+ if ((item = m_dataModel->messageItem(m_currentIndex))) {
if (enableRw && !item->isObsolete())
m_phraseView->setSourceText(model, item->text());
else
@@ -2003,6 +1972,7 @@ void MainWindow::updateLatestModel(int model)
}
}
}
+ updateSourceView(model, item);
m_ui.actionSave->setEnabled(enableRw);
m_ui.actionSaveAs->setEnabled(enableRw);
m_ui.actionRelease->setEnabled(enableRw);
@@ -2015,6 +1985,24 @@ void MainWindow::updateLatestModel(int model)
updateStatistics();
}
+void MainWindow::updateSourceView(int model, MessageItem *item)
+{
+ if (item && !item->fileName().isEmpty()) {
+ if (hasFormPreview(item->fileName())) {
+ m_sourceAndFormView->setCurrentWidget(m_formPreviewView);
+ m_formPreviewView->setSourceContext(model, item);
+ } else {
+ m_sourceAndFormView->setCurrentWidget(m_sourceCodeView);
+ QDir dir = QFileInfo(m_dataModel->srcFileName(model)).dir();
+ QString fileName = QDir::cleanPath(dir.absoluteFilePath(item->fileName()));
+ m_sourceCodeView->setSourceContext(fileName, item->lineNumber());
+ }
+ } else {
+ m_sourceAndFormView->setCurrentWidget(m_sourceCodeView);
+ m_sourceCodeView->setSourceContext(QString(), 0);
+ }
+}
+
// Note for *AboutToShow: Due to the delayed nature, only actions without shortcuts
// and representations outside the menu may be setEnabled()/setVisible() here.
diff --git a/src/linguist/linguist/mainwindow.h b/src/linguist/linguist/mainwindow.h
index 8b94808ff..cd18b6a5b 100644
--- a/src/linguist/linguist/mainwindow.h
+++ b/src/linguist/linguist/mainwindow.h
@@ -195,6 +195,7 @@ private:
QStringList pickTranslationFiles();
void showTranslationSettings(int model);
void updateLatestModel(int model);
+ void updateSourceView(int model, MessageItem *item);
void updatePhraseBookActions();
void updatePhraseDictInternal(int model);
void releaseInternal(int model);
@@ -205,7 +206,7 @@ private:
// FIXME: move to DataModel
void updateDanger(const MultiDataIndex &index, bool verbose);
- bool searchItem(const QString &searchWhat);
+ bool searchItem(DataModel::FindLocation where, const QString &searchWhat);
QProcess *m_assistantProcess;
QTreeView *m_contextView;
@@ -235,7 +236,6 @@ private:
Qt::CaseSensitivity m_findMatchCase;
bool m_findIgnoreAccelerators;
DataModel::FindLocation m_findWhere;
- DataModel::FindLocation m_foundWhere;
TranslateDialog *m_translateDialog;
QString m_latestFindText;
diff --git a/src/linguist/lrelease/main.cpp b/src/linguist/lrelease/main.cpp
index f5ed9bd12..8116616a9 100644
--- a/src/linguist/lrelease/main.cpp
+++ b/src/linguist/lrelease/main.cpp
@@ -41,6 +41,7 @@
#include "translator.h"
+#include <qmakevfs.h>
#include <qmakeparser.h>
#include <profileevaluator.h>
@@ -205,10 +206,12 @@ static void print(const QString &fileName, int lineNo, int type, const QString &
class EvalHandler : public QMakeHandler {
public:
virtual void message(int type, const QString &msg, const QString &fileName, int lineNo)
- { if (verbose) print(fileName, lineNo, type, msg); }
+ {
+ if (verbose && (type & CategoryMask) == ErrorMessage)
+ print(fileName, lineNo, type, msg);
+ }
- virtual void fileMessage(const QString &msg)
- { printErr(msg + QLatin1Char('\n')); }
+ virtual void fileMessage(const QString &) {}
virtual void aboutToEval(ProFile *, ProFile *, EvalFileType) {}
virtual void doneWithEval(ProFile *) {}
@@ -316,8 +319,9 @@ int main(int argc, char **argv)
option.qmake_abslocation = app.applicationDirPath() + QLatin1String("/qmake");
#endif
option.initProperties();
- QMakeParser parser(0, &evalHandler);
- ProFileEvaluator visitor(&option, &parser, &evalHandler);
+ QMakeVfs vfs;
+ QMakeParser parser(0, &vfs, &evalHandler);
+ ProFileEvaluator visitor(&option, &parser, &vfs, &evalHandler);
visitor.setCumulative(true);
visitor.setOutputDir(QDir::currentPath());
diff --git a/src/linguist/lupdate/cpp.cpp b/src/linguist/lupdate/cpp.cpp
index 24b5a8a3a..6e6bab121 100644
--- a/src/linguist/lupdate/cpp.cpp
+++ b/src/linguist/lupdate/cpp.cpp
@@ -1354,6 +1354,12 @@ void CppParser::processInclude(const QString &file, ConversionData &cd, const QS
{
QString cleanFile = QDir::cleanPath(file);
+ foreach (const QString &ex, cd.m_excludes) {
+ QRegExp rx(ex, Qt::CaseSensitive, QRegExp::Wildcard);
+ if (rx.exactMatch(cleanFile))
+ return;
+ }
+
const int index = includeStack.indexOf(cleanFile);
if (index != -1) {
CppFiles::addIncludeCycle(includeStack.mid(index).toSet());
diff --git a/src/linguist/lupdate/main.cpp b/src/linguist/lupdate/main.cpp
index c0d20fae9..0e4abb63d 100644
--- a/src/linguist/lupdate/main.cpp
+++ b/src/linguist/lupdate/main.cpp
@@ -43,6 +43,7 @@
#include "lupdate.h"
#include <translator.h>
+#include <qmakevfs.h>
#include <qmakeparser.h>
#include <profileevaluator.h>
@@ -396,10 +397,12 @@ static void print(const QString &fileName, int lineNo, int type, const QString &
class EvalHandler : public QMakeHandler {
public:
virtual void message(int type, const QString &msg, const QString &fileName, int lineNo)
- { if (verbose) print(fileName, lineNo, type, msg); }
+ {
+ if (verbose && (type & CategoryMask) == ErrorMessage)
+ print(fileName, lineNo, type, msg);
+ }
- virtual void fileMessage(const QString &msg)
- { printErr(msg + QLatin1Char('\n')); }
+ virtual void fileMessage(const QString &) {}
virtual void aboutToEval(ProFile *, ProFile *, EvalFileType) {}
virtual void doneWithEval(ProFile *) {}
@@ -418,7 +421,8 @@ static QStringList getSources(const char *var, const char *vvar, const QStringLi
return visitor.absoluteFileValues(QLatin1String(var), projectDir, vPaths, 0);
}
-static QStringList getSources(const ProFileEvaluator &visitor, const QString &projectDir)
+static QStringList getSources(const ProFileEvaluator &visitor, const QString &projectDir,
+ const QStringList &excludes)
{
QStringList baseVPaths;
baseVPaths += visitor.absolutePathValues(QLatin1String("VPATH"), projectDir);
@@ -476,12 +480,6 @@ static QStringList getSources(const ProFileEvaluator &visitor, const QString &pr
sourceFiles.removeDuplicates();
sourceFiles.sort();
- QStringList excludes;
- foreach (QString ex, visitor.values(QLatin1String("TR_EXCLUDE"))) {
- if (!QFileInfo(ex).isAbsolute())
- ex = QDir(projectDir).absoluteFilePath(ex);
- excludes << QDir::cleanPath(ex);
- }
foreach (const QString &ex, excludes) {
// TODO: take advantage of the file list being sorted
QRegExp rx(ex, Qt::CaseSensitive, QRegExp::Wildcard);
@@ -496,6 +494,18 @@ static QStringList getSources(const ProFileEvaluator &visitor, const QString &pr
return sourceFiles;
}
+QStringList getExcludes(const ProFileEvaluator &visitor, const QString &projectDir)
+{
+ QStringList excludes;
+ foreach (QString ex, visitor.values(QLatin1String("TR_EXCLUDE"))) {
+ if (!QFileInfo(ex).isAbsolute())
+ ex = QDir(projectDir).absoluteFilePath(ex);
+ excludes << QDir::cleanPath(ex);
+ }
+
+ return excludes;
+}
+
static void excludeProjects(const ProFileEvaluator &visitor, QStringList *subProjects)
{
foreach (const QString &ex, visitor.values(QLatin1String("TR_EXCLUDE"))) {
@@ -549,14 +559,15 @@ static void processSources(Translator &fetchedTor,
}
static void processProjects(bool topLevel, bool nestComplain, const QStringList &proFiles,
- const QHash<QString, QString> &outDirMap, ProFileGlobals *option, QMakeParser *parser,
+ const QHash<QString, QString> &outDirMap,
+ ProFileGlobals *option, QMakeVfs *vfs, QMakeParser *parser,
UpdateOptions options,
const QString &targetLanguage, const QString &sourceLanguage,
Translator *parentTor, bool *fail);
static void processProject(
bool nestComplain, const QString &proFile,
- ProFileGlobals *option, QMakeParser *parser, ProFileEvaluator &visitor,
+ ProFileGlobals *option, QMakeVfs *vfs, QMakeParser *parser, ProFileEvaluator &visitor,
UpdateOptions options,
const QString &targetLanguage, const QString &sourceLanguage,
Translator *fetchedTor, bool *fail)
@@ -596,14 +607,15 @@ static void processProject(
subProFiles << subPro;
}
processProjects(false, nestComplain, subProFiles, QHash<QString, QString>(),
- option, parser, options,
+ option, vfs, parser, options,
targetLanguage, sourceLanguage, fetchedTor, fail);
} else {
ConversionData cd;
cd.m_noUiLines = options & NoUiLines;
cd.m_sourceIsUtf16 = options & SourceIsUtf16;
cd.m_includePath = visitor.values(QLatin1String("INCLUDEPATH"));
- QStringList sourceFiles = getSources(visitor, proPath);
+ cd.m_excludes = getExcludes(visitor, proPath);
+ QStringList sourceFiles = getSources(visitor, proPath, cd.m_excludes);
QSet<QString> sourceDirs;
sourceDirs.insert(proPath + QLatin1Char('/'));
foreach (const QString &sf, sourceFiles)
@@ -621,7 +633,8 @@ static void processProject(
}
static void processProjects(bool topLevel, bool nestComplain, const QStringList &proFiles,
- const QHash<QString, QString> &outDirMap, ProFileGlobals *option, QMakeParser *parser,
+ const QHash<QString, QString> &outDirMap,
+ ProFileGlobals *option, QMakeVfs *vfs, QMakeParser *parser,
UpdateOptions options,
const QString &targetLanguage, const QString &sourceLanguage,
Translator *parentTor, bool *fail)
@@ -631,9 +644,6 @@ static void processProjects(bool topLevel, bool nestComplain, const QStringList
if (!outDirMap.isEmpty())
option->setDirectories(QFileInfo(proFile).path(), outDirMap[proFile]);
- ProFileEvaluator visitor(option, parser, &evalHandler);
- visitor.setCumulative(true);
- visitor.setOutputDir(option->shadowedPath(proFile));
ProFile *pro;
if (!(pro = parser->parsedProFile(proFile, topLevel ? QMakeParser::ParseReportMissing
: QMakeParser::ParseDefault))) {
@@ -641,6 +651,9 @@ static void processProjects(bool topLevel, bool nestComplain, const QStringList
*fail = true;
continue;
}
+ ProFileEvaluator visitor(option, parser, vfs, &evalHandler);
+ visitor.setCumulative(true);
+ visitor.setOutputDir(option->shadowedPath(pro->directoryName()));
if (!visitor.accept(pro)) {
if (topLevel)
*fail = true;
@@ -673,7 +686,7 @@ static void processProjects(bool topLevel, bool nestComplain, const QStringList
continue;
}
Translator tor;
- processProject(false, proFile, option, parser, visitor, options,
+ processProject(false, proFile, option, vfs, parser, visitor, options,
targetLanguage, sourceLanguage, &tor, fail);
updateTsFiles(tor, tsFiles, QStringList(),
sourceLanguage, targetLanguage, options, fail);
@@ -686,10 +699,10 @@ static void processProjects(bool topLevel, bool nestComplain, const QStringList
printErr(LU::tr("lupdate warning: no TS files specified. Only diagnostics "
"will be produced for '%1'.\n").arg(proFile));
Translator tor;
- processProject(nestComplain, proFile, option, parser, visitor, options,
+ processProject(nestComplain, proFile, option, vfs, parser, visitor, options,
targetLanguage, sourceLanguage, &tor, fail);
} else {
- processProject(nestComplain, proFile, option, parser, visitor, options,
+ processProject(nestComplain, proFile, option, vfs, parser, visitor, options,
targetLanguage, sourceLanguage, parentTor, fail);
}
pro->deref();
@@ -1042,16 +1055,17 @@ int main(int argc, char **argv)
option.initProperties();
option.setCommandLineArguments(QDir::currentPath(),
QStringList() << QLatin1String("CONFIG+=lupdate_run"));
- QMakeParser parser(0, &evalHandler);
+ QMakeVfs vfs;
+ QMakeParser parser(0, &vfs, &evalHandler);
if (!tsFileNames.isEmpty()) {
Translator fetchedTor;
- processProjects(true, true, proFiles, outDirMap, &option, &parser, options,
+ processProjects(true, true, proFiles, outDirMap, &option, &vfs, &parser, options,
targetLanguage, sourceLanguage, &fetchedTor, &fail);
updateTsFiles(fetchedTor, tsFileNames, alienFiles,
sourceLanguage, targetLanguage, options, &fail);
} else {
- processProjects(true, false, proFiles, outDirMap, &option, &parser, options,
+ processProjects(true, false, proFiles, outDirMap, &option, &vfs, &parser, options,
targetLanguage, sourceLanguage, 0, &fail);
}
}
diff --git a/src/linguist/shared/ioutils.cpp b/src/linguist/shared/ioutils.cpp
index fd4a18f10..e61ed4b31 100644
--- a/src/linguist/shared/ioutils.cpp
+++ b/src/linguist/shared/ioutils.cpp
@@ -88,6 +88,11 @@ bool IoUtils::isRelativePath(const QString &path)
return true;
}
+QStringRef IoUtils::pathName(const QString &fileName)
+{
+ return fileName.leftRef(fileName.lastIndexOf(QLatin1Char('/')) + 1);
+}
+
QStringRef IoUtils::fileName(const QString &fileName)
{
return fileName.midRef(fileName.lastIndexOf(QLatin1Char('/')) + 1);
diff --git a/src/linguist/shared/ioutils.h b/src/linguist/shared/ioutils.h
index ad2a77571..650b26b9b 100644
--- a/src/linguist/shared/ioutils.h
+++ b/src/linguist/shared/ioutils.h
@@ -64,6 +64,7 @@ public:
static bool exists(const QString &fileName) { return fileType(fileName) != FileNotFound; }
static bool isRelativePath(const QString &fileName);
static bool isAbsolutePath(const QString &fileName) { return !isRelativePath(fileName); }
+ static QStringRef pathName(const QString &fileName); // Requires normalized path
static QStringRef fileName(const QString &fileName); // Requires normalized path
static QString resolvePath(const QString &baseDir, const QString &fileName);
static QString shellQuoteUnix(const QString &arg);
diff --git a/src/linguist/shared/profileevaluator.cpp b/src/linguist/shared/profileevaluator.cpp
index f22b3f48b..58ad162d9 100644
--- a/src/linguist/shared/profileevaluator.cpp
+++ b/src/linguist/shared/profileevaluator.cpp
@@ -55,9 +55,9 @@ void ProFileEvaluator::initialize()
QMakeEvaluator::initStatics();
}
-ProFileEvaluator::ProFileEvaluator(ProFileGlobals *option, QMakeParser *parser,
+ProFileEvaluator::ProFileEvaluator(ProFileGlobals *option, QMakeParser *parser, QMakeVfs *vfs,
QMakeHandler *handler)
- : d(new QMakeEvaluator(option, parser, handler))
+ : d(new QMakeEvaluator(option, parser, vfs, handler))
{
}
diff --git a/src/linguist/shared/profileevaluator.h b/src/linguist/shared/profileevaluator.h
index 6046b1bae..8a7c5c84f 100644
--- a/src/linguist/shared/profileevaluator.h
+++ b/src/linguist/shared/profileevaluator.h
@@ -52,6 +52,7 @@
QT_BEGIN_NAMESPACE
+class QMakeVfs;
class QMakeParser;
class QMakeEvaluator;
class QMakeHandler;
@@ -77,7 +78,8 @@ public:
// Call this from a concurrency-free context
static void initialize();
- ProFileEvaluator(ProFileGlobals *option, QMakeParser *parser, QMakeHandler *handler);
+ ProFileEvaluator(ProFileGlobals *option, QMakeParser *parser, QMakeVfs *vfs,
+ QMakeHandler *handler);
~ProFileEvaluator();
ProFileEvaluator::TemplateType templateType() const;
diff --git a/src/linguist/shared/proparser.pri b/src/linguist/shared/proparser.pri
index 69e09c8d7..124227bc4 100644
--- a/src/linguist/shared/proparser.pri
+++ b/src/linguist/shared/proparser.pri
@@ -6,6 +6,7 @@ DEFINES += PROEVALUATOR_CUMULATIVE PROEVALUATOR_INIT_PROPS
HEADERS += \
$$PWD/qmake_global.h \
$$PWD/ioutils.h \
+ $$PWD/qmakevfs.h \
$$PWD/proitems.h \
$$PWD/qmakeglobals.h \
$$PWD/qmakeparser.h \
@@ -15,6 +16,7 @@ HEADERS += \
SOURCES += \
$$PWD/ioutils.cpp \
+ $$PWD/qmakevfs.cpp \
$$PWD/proitems.cpp \
$$PWD/qmakeglobals.cpp \
$$PWD/qmakeparser.cpp \
diff --git a/src/linguist/shared/qmakebuiltins.cpp b/src/linguist/shared/qmakebuiltins.cpp
index 37ab82ff3..4712df52d 100644
--- a/src/linguist/shared/qmakebuiltins.cpp
+++ b/src/linguist/shared/qmakebuiltins.cpp
@@ -44,6 +44,7 @@
#include "qmakeevaluator_p.h"
#include "qmakeglobals.h"
#include "qmakeparser.h"
+#include "qmakevfs.h"
#include "ioutils.h"
#include <qbytearray.h>
@@ -55,6 +56,9 @@
#include <qset.h>
#include <qstringlist.h>
#include <qtextstream.h>
+#ifdef PROEVALUATOR_THREAD_SAFE
+# include <qthreadpool.h>
+#endif
#ifdef Q_OS_UNIX
#include <time.h>
@@ -281,46 +285,17 @@ quoteValue(const ProString &val)
return ret;
}
-static bool
-doWriteFile(const QString &name, QIODevice::OpenMode mode, const QString &contents, QString *errStr)
-{
- QByteArray bytes = contents.toLocal8Bit();
- QFile cfile(name);
- if (!(mode & QIODevice::Append) && cfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
- if (cfile.readAll() == bytes)
- return true;
- cfile.close();
- }
- if (!cfile.open(mode | QIODevice::WriteOnly | QIODevice::Text)) {
- *errStr = cfile.errorString();
- return false;
- }
- cfile.write(bytes);
- cfile.close();
- if (cfile.error() != QFile::NoError) {
- *errStr = cfile.errorString();
- return false;
- }
- return true;
-}
-
QMakeEvaluator::VisitReturn
QMakeEvaluator::writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode,
const QString &contents)
{
- QFileInfo qfi(fn);
- if (!QDir::current().mkpath(qfi.path())) {
- evalError(fL1S("Cannot create %1directory %2.")
- .arg(ctx, QDir::toNativeSeparators(qfi.path())));
- return ReturnFalse;
- }
QString errStr;
- if (!doWriteFile(qfi.filePath(), mode, contents, &errStr)) {
+ if (!m_vfs->writeFile(fn, mode, contents, &errStr)) {
evalError(fL1S("Cannot write %1file %2: %3.")
- .arg(ctx, QDir::toNativeSeparators(qfi.filePath()), errStr));
+ .arg(ctx, QDir::toNativeSeparators(fn), errStr));
return ReturnFalse;
}
- m_parser->discardFileFromCache(qfi.filePath());
+ m_parser->discardFileFromCache(fn);
return ReturnTrue;
}
@@ -1302,6 +1277,8 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
}
QString parseInto;
LoadFlags flags = 0;
+ if (m_cumulative)
+ flags = LoadSilent;
if (args.count() >= 2) {
parseInto = args.at(1).toQString(m_tmp2);
if (args.count() >= 3 && isTrue(args.at(2), m_tmp3))
@@ -1425,6 +1402,9 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
}
const QString &file = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1)));
+ // Don't use VFS here:
+ // - it supports neither listing nor even directories
+ // - it's unlikely that somebody would test for files they created themselves
if (IoUtils::exists(file))
return ReturnTrue;
int slsh = file.lastIndexOf(QLatin1Char('/'));
@@ -1456,7 +1436,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
evalError(fL1S("write_file(name, [content var, [append]]) requires one to three arguments."));
return ReturnFalse;
}
-#ifdef PROEVALUATOR_FULL
QIODevice::OpenMode mode = QIODevice::Truncate;
QString contents;
if (args.count() >= 2) {
@@ -1468,9 +1447,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
mode = QIODevice::Append;
}
return writeFile(QString(), resolvePath(args.at(0).toQString(m_tmp1)), mode, contents);
-#else
- return ReturnTrue;
-#endif
}
case T_TOUCH: {
if (args.count() != 2) {
@@ -1522,7 +1498,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
evalError(fL1S("cache(var, [set|add|sub] [transient] [super], [srcvar]) requires one to three arguments."));
return ReturnFalse;
}
-#ifdef PROEVALUATOR_FULL
bool persist = true;
bool super = false;
enum { CacheSet, CacheAdd, CacheSub } mode = CacheSet;
@@ -1568,8 +1543,31 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
ProStringList newval;
bool changed = false;
for (bool hostBuild = false; ; hostBuild = true) {
- if (QMakeBaseEnv *baseEnv = m_option->baseEnvs.value(
- QMakeBaseKey(m_buildRoot, hostBuild))) {
+#ifdef PROEVALUATOR_THREAD_SAFE
+ m_option->mutex.lock();
+#endif
+ QMakeBaseEnv *baseEnv =
+ m_option->baseEnvs.value(QMakeBaseKey(m_buildRoot, hostBuild));
+#ifdef PROEVALUATOR_THREAD_SAFE
+ // It's ok to unlock this before locking baseEnv,
+ // as we have no intention to initialize the env.
+ m_option->mutex.unlock();
+#endif
+ do {
+ if (!baseEnv)
+ break;
+#ifdef PROEVALUATOR_THREAD_SAFE
+ QMutexLocker locker(&baseEnv->mutex);
+ if (baseEnv->inProgress && baseEnv->evaluator != this) {
+ // The env is still in the works, but it may be already past the cache
+ // loading. So we need to wait for completion and amend it as usual.
+ QThreadPool::globalInstance()->releaseThread();
+ baseEnv->cond.wait(&baseEnv->mutex);
+ QThreadPool::globalInstance()->reserveThread();
+ }
+ if (!baseEnv->isOk)
+ break;
+#endif
QMakeEvaluator *baseEval = baseEnv->evaluator;
const ProStringList &oldval = baseEval->values(dstvar);
if (mode == CacheSet) {
@@ -1600,7 +1598,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
}
changed = true;
}
- }
+ } while (false);
if (hostBuild)
break;
}
@@ -1648,9 +1646,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
fn = m_cachefile;
}
return writeFile(fL1S("cache "), fn, QIODevice::Append, varstr);
-#else
- return ReturnTrue;
-#endif
}
default:
evalError(fL1S("Function '%1' is not implemented.").arg(function.toQString(m_tmp1)));
diff --git a/src/linguist/shared/qmakeevaluator.cpp b/src/linguist/shared/qmakeevaluator.cpp
index eb9e24c3b..8cbd7b9d5 100644
--- a/src/linguist/shared/qmakeevaluator.cpp
+++ b/src/linguist/shared/qmakeevaluator.cpp
@@ -44,6 +44,7 @@
#include "qmakeglobals.h"
#include "qmakeparser.h"
+#include "qmakevfs.h"
#include "ioutils.h"
#include <qbytearray.h>
@@ -174,13 +175,13 @@ const ProKey &QMakeEvaluator::map(const ProKey &var)
}
-QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option,
- QMakeParser *parser, QMakeHandler *handler)
+QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, QMakeVfs *vfs,
+ QMakeHandler *handler)
:
#ifdef PROEVALUATOR_DEBUG
m_debugLevel(option->debugLevel),
#endif
- m_option(option), m_parser(parser), m_handler(handler)
+ m_option(option), m_parser(parser), m_handler(handler), m_vfs(vfs)
{
// So that single-threaded apps don't have to call initialize() for now.
initStatics();
@@ -276,6 +277,8 @@ ProStringList QMakeEvaluator::split_value_list(const QString &vals, const ProFil
ushort unicode = vals_data[x].unicode();
if (unicode == quote) {
quote = 0;
+ hadWord = true;
+ build += QChar(unicode);
continue;
}
switch (unicode) {
@@ -283,7 +286,7 @@ ProStringList QMakeEvaluator::split_value_list(const QString &vals, const ProFil
case '\'':
quote = unicode;
hadWord = true;
- continue;
+ break;
case ' ':
case '\t':
if (!quote) {
@@ -294,22 +297,23 @@ ProStringList QMakeEvaluator::split_value_list(const QString &vals, const ProFil
}
continue;
}
- build += QChar(unicode);
break;
case '\\':
if (x + 1 != vals_len) {
ushort next = vals_data[++x].unicode();
- if (next == '\'' || next == '"' || next == '\\')
+ if (next == '\'' || next == '"' || next == '\\') {
+ build += QChar(unicode);
unicode = next;
- else
+ } else {
--x;
+ }
}
// fallthrough
default:
hadWord = true;
- build += QChar(unicode);
break;
}
+ build += QChar(unicode);
}
if (hadWord)
ret << ProString(build).setSource(source);
@@ -583,13 +587,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProBlock(
okey = true, or_op = false; // force next evaluation
break;
case TokForLoop:
- if (m_cumulative) { // This is a no-win situation, so just pretend it's no loop
- skipHashStr(tokPtr);
- uint exprLen = getBlockLen(tokPtr);
- tokPtr += exprLen;
- blockLen = getBlockLen(tokPtr);
- ret = visitProBlock(tokPtr);
- } else if (okey != or_op) {
+ if (m_cumulative || okey != or_op) {
const ProKey &variable = getHashStr(tokPtr);
uint exprLen = getBlockLen(tokPtr);
const ushort *exprPtr = tokPtr;
@@ -759,6 +757,11 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop(
ProStringList list = values(it_list.toKey());
if (list.isEmpty()) {
if (it_list == statics.strforever) {
+ if (m_cumulative) {
+ // The termination conditions wouldn't be evaluated, so we must skip it.
+ traceMsg("skipping forever loop in cumulative mode");
+ return ReturnFalse;
+ }
infinite = true;
} else {
const QString &itl = it_list.toQString(m_tmp1);
@@ -769,6 +772,12 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProLoop(
if (ok) {
int end = itl.mid(dotdot+2).toInt(&ok);
if (ok) {
+ if (m_cumulative && qAbs(end - start) > 100) {
+ // Such a loop is unlikely to contribute something useful to the
+ // file collection, and may cause considerable delay.
+ traceMsg("skipping excessive loop in cumulative mode");
+ return ReturnFalse;
+ }
if (start < end) {
for (int i = start; i <= end; i++)
list << ProString(QString::number(i));
@@ -933,7 +942,7 @@ void QMakeEvaluator::visitProVariable(
if (varName == statics.strTEMPLATE)
setTemplate();
else if (varName == statics.strQMAKE_PLATFORM)
- updateFeaturePaths();
+ m_featureRoots = 0;
#ifdef PROEVALUATOR_FULL
else if (varName == statics.strREQUIRES)
checkRequirements(values(varName));
@@ -1037,7 +1046,7 @@ void QMakeEvaluator::loadDefaults()
# endif
#elif defined(Q_OS_UNIX)
struct utsname name;
- if (!uname(&name)) {
+ if (uname(&name) != -1) {
vars[ProKey("QMAKE_HOST.os")] << ProString(name.sysname);
vars[ProKey("QMAKE_HOST.name")] << ProString(QString::fromLocal8Bit(name.nodename));
vars[ProKey("QMAKE_HOST.version")] << ProString(name.release);
@@ -1061,7 +1070,7 @@ bool QMakeEvaluator::prepareProject(const QString &inDir)
superdir = m_outputDir;
forever {
QString superfile = superdir + QLatin1String("/.qmake.super");
- if (IoUtils::exists(superfile)) {
+ if (m_vfs->exists(superfile)) {
m_superfile = QDir::cleanPath(superfile);
break;
}
@@ -1076,10 +1085,10 @@ bool QMakeEvaluator::prepareProject(const QString &inDir)
QString dir = m_outputDir;
forever {
conffile = sdir + QLatin1String("/.qmake.conf");
- if (!IoUtils::exists(conffile))
+ if (!m_vfs->exists(conffile))
conffile.clear();
cachefile = dir + QLatin1String("/.qmake.cache");
- if (!IoUtils::exists(cachefile))
+ if (!m_vfs->exists(cachefile))
cachefile.clear();
if (!conffile.isEmpty() || !cachefile.isEmpty()) {
if (dir != sdir)
@@ -1157,6 +1166,7 @@ bool QMakeEvaluator::loadSpecInternal()
#endif
valuesRef(ProKey("QMAKESPEC")) << ProString(m_qmakespec);
m_qmakespecName = IoUtils::fileName(m_qmakespec).toString();
+ // This also ensures that m_featureRoots is valid.
if (evaluateFeatureFile(QLatin1String("spec_post.prf")) != ReturnTrue)
return false;
// The MinGW and x-build specs may change the separator; $$shell_{path,quote}() need it
@@ -1170,7 +1180,9 @@ bool QMakeEvaluator::loadSpec()
m_hostBuild ? m_option->qmakespec : m_option->xqmakespec);
{
- QMakeEvaluator evaluator(m_option, m_parser, m_handler);
+ QMakeEvaluator evaluator(m_option, m_parser, m_vfs, m_handler);
+ evaluator.m_sourceRoot = m_sourceRoot;
+ evaluator.m_buildRoot = m_buildRoot;
if (!m_superfile.isEmpty()) {
valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
if (evaluator.evaluateFile(
@@ -1222,7 +1234,7 @@ bool QMakeEvaluator::loadSpec()
m_qmakespec = QDir::cleanPath(qmakespec);
if (!m_superfile.isEmpty()
- && evaluateFile(m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) {
+ && evaluateFile(m_superfile, QMakeHandler::EvalConfigFile, LoadProOnly|LoadHidden) != ReturnTrue) {
return false;
}
if (!loadSpecInternal())
@@ -1310,45 +1322,45 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProFile(
QMakeBaseEnv *baseEnv = *baseEnvPtr;
#ifdef PROEVALUATOR_THREAD_SAFE
- {
- QMutexLocker locker(&baseEnv->mutex);
- m_option->mutex.unlock();
- if (baseEnv->inProgress) {
- QThreadPool::globalInstance()->releaseThread();
- baseEnv->cond.wait(&baseEnv->mutex);
- QThreadPool::globalInstance()->reserveThread();
- if (!baseEnv->isOk)
- return ReturnFalse;
- } else
+ QMutexLocker locker(&baseEnv->mutex);
+ m_option->mutex.unlock();
+ if (baseEnv->inProgress) {
+ QThreadPool::globalInstance()->releaseThread();
+ baseEnv->cond.wait(&baseEnv->mutex);
+ QThreadPool::globalInstance()->reserveThread();
+ if (!baseEnv->isOk)
+ return ReturnFalse;
+ } else
#endif
- if (!baseEnv->evaluator) {
+ if (!baseEnv->evaluator) {
#ifdef PROEVALUATOR_THREAD_SAFE
- baseEnv->inProgress = true;
- locker.unlock();
+ baseEnv->inProgress = true;
+ locker.unlock();
#endif
- QMakeEvaluator *baseEval = new QMakeEvaluator(m_option, m_parser, m_handler);
- baseEnv->evaluator = baseEval;
- baseEval->m_superfile = m_superfile;
- baseEval->m_conffile = m_conffile;
- baseEval->m_cachefile = m_cachefile;
- baseEval->m_sourceRoot = m_sourceRoot;
- baseEval->m_buildRoot = m_buildRoot;
- baseEval->m_hostBuild = m_hostBuild;
- bool ok = baseEval->loadSpec();
+ QMakeEvaluator *baseEval = new QMakeEvaluator(m_option, m_parser, m_vfs, m_handler);
+ baseEnv->evaluator = baseEval;
+ baseEval->m_superfile = m_superfile;
+ baseEval->m_conffile = m_conffile;
+ baseEval->m_cachefile = m_cachefile;
+ baseEval->m_sourceRoot = m_sourceRoot;
+ baseEval->m_buildRoot = m_buildRoot;
+ baseEval->m_hostBuild = m_hostBuild;
+ bool ok = baseEval->loadSpec();
#ifdef PROEVALUATOR_THREAD_SAFE
- locker.relock();
- baseEnv->isOk = ok;
- baseEnv->inProgress = false;
- baseEnv->cond.wakeAll();
+ locker.relock();
+ baseEnv->isOk = ok;
+ baseEnv->inProgress = false;
+ baseEnv->cond.wakeAll();
#endif
- if (!ok)
- return ReturnFalse;
- }
-#ifdef PROEVALUATOR_THREAD_SAFE
+ if (!ok)
+ return ReturnFalse;
}
+#ifdef PROEVALUATOR_THREAD_SAFE
+ else if (!baseEnv->isOk)
+ return ReturnFalse;
#endif
initFrom(*baseEnv->evaluator);
@@ -1426,6 +1438,7 @@ void QMakeEvaluator::updateMkspecPaths()
ret << m_sourceRoot + concat;
ret << m_option->propertyValue(ProKey("QT_HOST_DATA/get")) + concat;
+ ret << m_option->propertyValue(ProKey("QT_HOST_DATA/src")) + concat;
ret.removeDuplicates();
m_mkspecPaths = ret;
@@ -1447,10 +1460,14 @@ void QMakeEvaluator::updateFeaturePaths()
m_option->dirlist_sep, QString::SkipEmptyParts);
QStringList feature_bases;
- if (!m_buildRoot.isEmpty())
+ if (!m_buildRoot.isEmpty()) {
+ feature_bases << m_buildRoot + mkspecs_concat;
feature_bases << m_buildRoot;
- if (!m_sourceRoot.isEmpty())
+ }
+ if (!m_sourceRoot.isEmpty()) {
+ feature_bases << m_sourceRoot + mkspecs_concat;
feature_bases << m_sourceRoot;
+ }
foreach (const QString &item, m_option->getPathListEnv(QLatin1String("QMAKEPATH")))
feature_bases << (item + mkspecs_concat);
@@ -1474,8 +1491,8 @@ void QMakeEvaluator::updateFeaturePaths()
}
}
- feature_bases << (m_option->propertyValue(ProKey("QT_HOST_DATA/get")).toQString(m_mtmp)
- + mkspecs_concat);
+ feature_bases << (m_option->propertyValue(ProKey("QT_HOST_DATA/get")) + mkspecs_concat);
+ feature_bases << (m_option->propertyValue(ProKey("QT_HOST_DATA/src")) + mkspecs_concat);
foreach (const QString &fb, feature_bases) {
foreach (const ProString &sfx, values(ProKey("QMAKE_PLATFORM")))
@@ -1493,7 +1510,7 @@ void QMakeEvaluator::updateFeaturePaths()
foreach (const QString &root, feature_roots)
if (IoUtils::exists(root))
ret << root;
- m_featureRoots = ret;
+ m_featureRoots = new QMakeFeatureRoots(ret);
}
ProString QMakeEvaluator::propertyValue(const ProKey &name) const
@@ -1807,13 +1824,16 @@ ProString QMakeEvaluator::first(const ProKey &variableName) const
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFile(
const QString &fileName, QMakeHandler::EvalFileType type, LoadFlags flags)
{
- if (ProFile *pro = m_parser->parsedProFile(fileName, QMakeParser::ParseUseCache)) {
+ QMakeParser::ParseFlags pflags = QMakeParser::ParseUseCache;
+ if (!(flags & LoadSilent))
+ pflags |= QMakeParser::ParseReportMissing;
+ if (ProFile *pro = m_parser->parsedProFile(fileName, pflags)) {
m_locationStack.push(m_current);
VisitReturn ok = visitProFile(pro, type, flags);
m_current = m_locationStack.pop();
pro->deref();
#ifdef PROEVALUATOR_FULL
- if (ok == ReturnTrue) {
+ if (ok == ReturnTrue && !(flags & LoadHidden)) {
ProStringList &iif = m_valuemapStack.first()[ProKey("QMAKE_INTERNAL_INCLUDED_FILES")];
ProString ifn(fileName);
if (!iif.contains(ifn))
@@ -1822,8 +1842,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFile(
#endif
return ok;
} else {
- if (!(flags & LoadSilent) && !IoUtils::exists(fileName))
- evalError(fL1S("WARNING: Include file %1 not found").arg(fileName));
return ReturnFalse;
}
}
@@ -1851,34 +1869,55 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFeatureFile(
if (!fn.endsWith(QLatin1String(".prf")))
fn += QLatin1String(".prf");
- if (m_featureRoots.isEmpty())
+ if (!m_featureRoots)
updateFeaturePaths();
- int start_root = 0;
+#ifdef PROEVALUATOR_THREAD_SAFE
+ m_featureRoots->mutex.lock();
+#endif
QString currFn = currentFileName();
- if (IoUtils::fileName(currFn) == IoUtils::fileName(fn)) {
- for (int root = 0; root < m_featureRoots.size(); ++root)
- if (currFn == m_featureRoots.at(root) + fn) {
- start_root = root + 1;
- break;
+ if (IoUtils::fileName(currFn) != IoUtils::fileName(fn))
+ currFn.clear();
+ // Null values cannot regularly exist in the hash, so they indicate that the value still
+ // needs to be determined. Failed lookups are represented via non-null empty strings.
+ QString *fnp = &m_featureRoots->cache[qMakePair(fn, currFn)];
+ if (fnp->isNull()) {
+ int start_root = 0;
+ const QStringList &paths = m_featureRoots->paths;
+ if (!currFn.isEmpty()) {
+ QStringRef currPath = IoUtils::pathName(currFn);
+ for (int root = 0; root < paths.size(); ++root)
+ if (currPath == paths.at(root)) {
+ start_root = root + 1;
+ break;
+ }
+ }
+ for (int root = start_root; root < paths.size(); ++root) {
+ QString fname = paths.at(root) + fn;
+ if (IoUtils::exists(fname)) {
+ fn = fname;
+ goto cool;
}
- }
- for (int root = start_root; root < m_featureRoots.size(); ++root) {
- QString fname = m_featureRoots.at(root) + fn;
- if (IoUtils::exists(fname)) {
- fn = fname;
- goto cool;
}
- }
#ifdef QMAKE_BUILTIN_PRFS
- fn.prepend(QLatin1String(":/qmake/features/"));
- if (QFileInfo(fn).exists())
- goto cool;
+ fn.prepend(QLatin1String(":/qmake/features/"));
+ if (QFileInfo(fn).exists())
+ goto cool;
#endif
- if (!silent)
- evalError(fL1S("Cannot find feature %1").arg(fileName));
- return ReturnFalse;
+ fn = QLatin1String(""); // Indicate failed lookup. See comment above.
- cool:
+ cool:
+ *fnp = fn;
+ } else {
+ fn = *fnp;
+ }
+#ifdef PROEVALUATOR_THREAD_SAFE
+ m_featureRoots->mutex.unlock();
+#endif
+ if (fn.isEmpty()) {
+ if (!silent)
+ evalError(fL1S("Cannot find feature %1").arg(fileName));
+ return ReturnFalse;
+ }
ProStringList &already = valuesRef(ProKey("QMAKE_INTERNAL_INCLUDED_FEATURES"));
ProString afn(fn);
if (already.contains(afn)) {
@@ -1905,7 +1944,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFeatureFile(
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFileInto(
const QString &fileName, ProValueMap *values, LoadFlags flags)
{
- QMakeEvaluator visitor(m_option, m_parser, m_handler);
+ QMakeEvaluator visitor(m_option, m_parser, m_vfs, m_handler);
visitor.m_caller = this;
visitor.m_outputDir = m_outputDir;
visitor.m_featureRoots = m_featureRoots;
diff --git a/src/linguist/shared/qmakeevaluator.h b/src/linguist/shared/qmakeevaluator.h
index 21f487a3b..13198c389 100644
--- a/src/linguist/shared/qmakeevaluator.h
+++ b/src/linguist/shared/qmakeevaluator.h
@@ -55,9 +55,13 @@
#include <qstack.h>
#include <qstring.h>
#include <qstringlist.h>
+#include <qshareddata.h>
#ifndef QT_BOOTSTRAPPED
# include <qprocess.h>
#endif
+#ifdef PROEVALUATOR_THREAD_SAFE
+# include <qmutex.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -83,6 +87,20 @@ public:
virtual void doneWithEval(ProFile *parent) = 0;
};
+typedef QPair<QString, QString> QMakeFeatureKey; // key, parent
+typedef QHash<QMakeFeatureKey, QString> QMakeFeatureHash;
+
+class QMAKE_EXPORT QMakeFeatureRoots : public QSharedData
+{
+public:
+ QMakeFeatureRoots(const QStringList &_paths) : paths(_paths) {}
+ const QStringList paths;
+ mutable QMakeFeatureHash cache;
+#ifdef PROEVALUATOR_THREAD_SAFE
+ mutable QMutex mutex;
+#endif
+};
+
// We use a QLinkedList based stack instead of a QVector based one (QStack), so that
// the addresses of value maps stay constant. The qmake generators rely on that.
class QMAKE_EXPORT ProValueMapStack : public QLinkedList<ProValueMap>
@@ -102,13 +120,14 @@ public:
LoadPreFiles = 1,
LoadPostFiles = 2,
LoadAll = LoadPreFiles|LoadPostFiles,
- LoadSilent = 0x10
+ LoadSilent = 0x10,
+ LoadHidden = 0x20
};
Q_DECLARE_FLAGS(LoadFlags, LoadFlag)
static void initStatics();
static void initFunctionStatics();
- QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser,
+ QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, QMakeVfs *vfs,
QMakeHandler *handler);
~QMakeEvaluator();
@@ -283,7 +302,7 @@ public:
QStringList m_qmakepath;
QStringList m_qmakefeatures;
QStringList m_mkspecPaths;
- QStringList m_featureRoots;
+ QExplicitlySharedDataPointer<QMakeFeatureRoots> m_featureRoots;
ProString m_dirSep;
ProFunctionDefs m_functionDefs;
ProStringList m_returnValue;
@@ -294,6 +313,7 @@ public:
QMakeGlobals *m_option;
QMakeParser *m_parser;
QMakeHandler *m_handler;
+ QMakeVfs *m_vfs;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeEvaluator::LoadFlags)
diff --git a/src/linguist/shared/qmakeglobals.cpp b/src/linguist/shared/qmakeglobals.cpp
index dbe694443..6f2390fc3 100644
--- a/src/linguist/shared/qmakeglobals.cpp
+++ b/src/linguist/shared/qmakeglobals.cpp
@@ -249,7 +249,8 @@ void QMakeGlobals::setDirectories(const QString &input_dir, const QString &outpu
int srcLen = srcpath.length();
int dstLen = dstpath.length();
int lastSl = -1;
- while (++lastSl, srcpath.at(--srcLen) == dstpath.at(--dstLen))
+ while (++lastSl, --srcLen, --dstLen,
+ srcLen && dstLen && srcpath.at(srcLen) == dstpath.at(dstLen))
if (srcpath.at(srcLen) == QLatin1Char('/'))
lastSl = 0;
source_root = srcpath.left(srcLen + lastSl);
@@ -323,33 +324,46 @@ bool QMakeGlobals::initProperties()
QT_PCLOSE(proc);
}
#endif
- foreach (QByteArray line, data.split('\n'))
- {
- int off = line.indexOf(':');
- if (off < 0) // huh?
- continue;
- if (line.endsWith('\r'))
- line.chop(1);
- QString name = QString::fromLatin1(line.left(off));
- ProString value = ProString(QDir::fromNativeSeparators(
- QString::fromLocal8Bit(line.mid(off + 1))));
- properties.insert(ProKey(name), value);
- if (name.startsWith(QLatin1String("QT_")) && !name.contains(QLatin1Char('/'))) {
- if (name.startsWith(QLatin1String("QT_INSTALL_"))) {
+ foreach (QByteArray line, data.split('\n')) {
+ int off = line.indexOf(':');
+ if (off < 0) // huh?
+ continue;
+ if (line.endsWith('\r'))
+ line.chop(1);
+ QString name = QString::fromLatin1(line.left(off));
+ ProString value = ProString(QDir::fromNativeSeparators(
+ QString::fromLocal8Bit(line.mid(off + 1))));
+ properties.insert(ProKey(name), value);
+ if (name.startsWith(QLatin1String("QT_"))) {
+ bool plain = !name.contains(QLatin1Char('/'));
+ if (!plain) {
+ if (!name.endsWith(QLatin1String("/get")))
+ continue;
+ name.chop(4);
+ }
+ if (name.startsWith(QLatin1String("QT_INSTALL_"))) {
+ if (plain) {
properties.insert(ProKey(name + QLatin1String("/raw")), value);
properties.insert(ProKey(name + QLatin1String("/get")), value);
- if (name == QLatin1String("QT_INSTALL_PREFIX")
- || name == QLatin1String("QT_INSTALL_DATA")
- || name == QLatin1String("QT_INSTALL_BINS")) {
- name.replace(3, 7, QLatin1String("HOST"));
+ }
+ properties.insert(ProKey(name + QLatin1String("/src")), value);
+ if (name == QLatin1String("QT_INSTALL_PREFIX")
+ || name == QLatin1String("QT_INSTALL_DATA")
+ || name == QLatin1String("QT_INSTALL_BINS")) {
+ name.replace(3, 7, QLatin1String("HOST"));
+ if (plain) {
properties.insert(ProKey(name), value);
properties.insert(ProKey(name + QLatin1String("/get")), value);
}
- } else if (name.startsWith(QLatin1String("QT_HOST_"))) {
- properties.insert(ProKey(name + QLatin1String("/get")), value);
+ properties.insert(ProKey(name + QLatin1String("/src")), value);
}
+ } else if (name.startsWith(QLatin1String("QT_HOST_"))) {
+ if (plain)
+ properties.insert(ProKey(name + QLatin1String("/get")), value);
+ properties.insert(ProKey(name + QLatin1String("/src")), value);
}
}
+ }
return true;
}
#else
diff --git a/src/linguist/shared/qmakeparser.cpp b/src/linguist/shared/qmakeparser.cpp
index c61375be3..34e99a82a 100644
--- a/src/linguist/shared/qmakeparser.cpp
+++ b/src/linguist/shared/qmakeparser.cpp
@@ -41,6 +41,7 @@
#include "qmakeparser.h"
+#include "qmakevfs.h"
#include "ioutils.h"
using namespace QMakeInternal;
@@ -142,9 +143,10 @@ void QMakeParser::initialize()
statics.strLITERAL_WHITESPACE = QLatin1String("LITERAL_WHITESPACE");
}
-QMakeParser::QMakeParser(ProFileCache *cache, QMakeParserHandler *handler)
+QMakeParser::QMakeParser(ProFileCache *cache, QMakeVfs *vfs, QMakeParserHandler *handler)
: m_cache(cache)
, m_handler(handler)
+ , m_vfs(vfs)
{
// So that single-threaded apps don't have to call initialize() for now.
initialize();
@@ -230,24 +232,14 @@ void QMakeParser::discardFileFromCache(const QString &fileName)
bool QMakeParser::read(ProFile *pro, ParseFlags flags)
{
- QFile file(pro->fileName());
- if (!file.open(QIODevice::ReadOnly)) {
- if (m_handler && ((flags & ParseReportMissing) || IoUtils::exists(pro->fileName())))
+ QString content;
+ QString errStr;
+ if (!m_vfs->readFile(pro->fileName(), &content, &errStr)) {
+ if (m_handler && ((flags & ParseReportMissing) || m_vfs->exists(pro->fileName())))
m_handler->message(QMakeParserHandler::ParserIoError,
- fL1S("Cannot read %1: %2").arg(pro->fileName(), file.errorString()));
+ fL1S("Cannot read %1: %2").arg(pro->fileName(), errStr));
return false;
}
-
- QByteArray bcont = file.readAll();
- if (bcont.startsWith("\xef\xbb\xbf")) {
- // UTF-8 BOM will cause subtle errors
- m_handler->message(QMakeParserHandler::ParserIoError,
- fL1S("Unexpected UTF-8 BOM in %1").arg(pro->fileName()));
- return false;
- }
- QString content(QString::fromLocal8Bit(bcont));
- bcont.clear();
- file.close();
return read(pro, content, 1, FullGrammar);
}
diff --git a/src/linguist/shared/qmakeparser.h b/src/linguist/shared/qmakeparser.h
index e1dd09094..dd55659d8 100644
--- a/src/linguist/shared/qmakeparser.h
+++ b/src/linguist/shared/qmakeparser.h
@@ -79,6 +79,7 @@ public:
};
class ProFileCache;
+class QMakeVfs;
class QMAKE_EXPORT QMakeParser
{
@@ -93,7 +94,7 @@ public:
};
Q_DECLARE_FLAGS(ParseFlags, ParseFlag)
- QMakeParser(ProFileCache *cache, QMakeParserHandler *handler);
+ QMakeParser(ProFileCache *cache, QMakeVfs *vfs, QMakeParserHandler *handler);
enum SubGrammar { FullGrammar, TestGrammar, ValueGrammar };
// fileName is expected to be absolute and cleanPath()ed.
@@ -182,6 +183,7 @@ private:
ProFileCache *m_cache;
QMakeParserHandler *m_handler;
+ QMakeVfs *m_vfs;
// This doesn't help gcc 3.3 ...
template<typename T> friend class QTypeInfo;
diff --git a/src/linguist/shared/qmakevfs.cpp b/src/linguist/shared/qmakevfs.cpp
new file mode 100644
index 000000000..cfa5f2974
--- /dev/null
+++ b/src/linguist/shared/qmakevfs.cpp
@@ -0,0 +1,192 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Linguist of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qmakevfs.h"
+
+#include "ioutils.h"
+using namespace QMakeInternal;
+
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+
+#define fL1S(s) QString::fromLatin1(s)
+
+QT_BEGIN_NAMESPACE
+
+QMakeVfs::QMakeVfs()
+#ifndef PROEVALUATOR_FULL
+ : m_magicMissing(fL1S("missing"))
+ , m_magicExisting(fL1S("existing"))
+#endif
+{
+}
+
+bool QMakeVfs::writeFile(const QString &fn, QIODevice::OpenMode mode, const QString &contents,
+ QString *errStr)
+{
+#ifndef PROEVALUATOR_FULL
+# ifdef PROEVALUATOR_THREAD_SAFE
+ QMutexLocker locker(&m_mutex);
+# endif
+ QString *cont = &m_files[fn];
+ if (mode & QIODevice::Append)
+ *cont += contents;
+ else
+ *cont = contents;
+ Q_UNUSED(errStr)
+ return true;
+#else
+ QFileInfo qfi(fn);
+ if (!QDir::current().mkpath(qfi.path())) {
+ *errStr = fL1S("Cannot create parent directory");
+ return false;
+ }
+ QByteArray bytes = contents.toLocal8Bit();
+ QFile cfile(fn);
+ if (!(mode & QIODevice::Append) && cfile.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ if (cfile.readAll() == bytes)
+ return true;
+ cfile.close();
+ }
+ if (!cfile.open(mode | QIODevice::WriteOnly | QIODevice::Text)) {
+ *errStr = cfile.errorString();
+ return false;
+ }
+ cfile.write(bytes);
+ cfile.close();
+ if (cfile.error() != QFile::NoError) {
+ *errStr = cfile.errorString();
+ return false;
+ }
+ return true;
+#endif
+}
+
+bool QMakeVfs::readFile(const QString &fn, QString *contents, QString *errStr)
+{
+#ifndef PROEVALUATOR_FULL
+# ifdef PROEVALUATOR_THREAD_SAFE
+ QMutexLocker locker(&m_mutex);
+# endif
+ QHash<QString, QString>::ConstIterator it = m_files.constFind(fn);
+ if (it != m_files.constEnd()) {
+ if (it->constData() == m_magicMissing.constData()) {
+ *errStr = fL1S("No such file or directory");
+ return false;
+ }
+ if (it->constData() != m_magicExisting.constData()) {
+ *contents = *it;
+ return true;
+ }
+ }
+#endif
+
+ QFile file(fn);
+ if (!file.open(QIODevice::ReadOnly)) {
+#ifndef PROEVALUATOR_FULL
+ if (!IoUtils::exists(fn)) {
+ m_files[fn] = m_magicMissing;
+ *errStr = fL1S("No such file or directory");
+ } else
+#endif
+ *errStr = file.errorString();
+ return false;
+ }
+#ifndef PROEVALUATOR_FULL
+ m_files[fn] = m_magicExisting;
+#endif
+
+ QByteArray bcont = file.readAll();
+ if (bcont.startsWith("\xef\xbb\xbf")) {
+ // UTF-8 BOM will cause subtle errors
+ *errStr = fL1S("Unexpected UTF-8 BOM");
+ return false;
+ }
+ *contents = QString::fromLocal8Bit(bcont);
+ return true;
+}
+
+bool QMakeVfs::exists(const QString &fn)
+{
+#ifndef PROEVALUATOR_FULL
+# ifdef PROEVALUATOR_THREAD_SAFE
+ QMutexLocker locker(&m_mutex);
+# endif
+ QHash<QString, QString>::ConstIterator it = m_files.constFind(fn);
+ if (it != m_files.constEnd())
+ return it->constData() != m_magicMissing.constData();
+#endif
+ bool ex = IoUtils::exists(fn);
+#ifndef PROEVALUATOR_FULL
+ m_files[fn] = ex ? m_magicExisting : m_magicMissing;
+#endif
+ return ex;
+}
+
+#ifndef PROEVALUATOR_FULL
+// This should be called when the sources may have changed (e.g., VCS update).
+void QMakeVfs::invalidateCache()
+{
+# ifdef PROEVALUATOR_THREAD_SAFE
+ QMutexLocker locker(&m_mutex);
+# endif
+ QHash<QString, QString>::Iterator it = m_files.begin(), eit = m_files.end();
+ while (it != eit) {
+ if (it->constData() == m_magicMissing.constData()
+ ||it->constData() == m_magicExisting.constData())
+ it = m_files.erase(it);
+ else
+ ++it;
+ }
+}
+
+// This should be called when generated files may have changed (e.g., actual build).
+void QMakeVfs::invalidateContents()
+{
+# ifdef PROEVALUATOR_THREAD_SAFE
+ QMutexLocker locker(&m_mutex);
+# endif
+ m_files.clear();
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/linguist/tests/tst_linguist.h b/src/linguist/shared/qmakevfs.h
index 060cd88a2..c5e77ed94 100644
--- a/src/linguist/tests/tst_linguist.h
+++ b/src/linguist/shared/qmakevfs.h
@@ -3,7 +3,7 @@
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
-** This file is part of the test suite of the Qt Toolkit.
+** This file is part of the Qt Linguist of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
@@ -39,21 +39,47 @@
**
****************************************************************************/
-#ifndef TST_LINGUIST
-#define TST_LINGUIST
+#ifndef QMAKEVFS_H
+#define QMAKEVFS_H
-#include <QtTest/QtTest>
-#include <QtCore/QtCore>
+#include "qmake_global.h"
-class tst_linguist : public QObject
+# include <qiodevice.h>
+#ifndef PROEVALUATOR_FULL
+# include <qhash.h>
+# include <qstring.h>
+# ifdef PROEVALUATOR_THREAD_SAFE
+# include <qmutex.h>
+# endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QMAKE_EXPORT QMakeVfs
{
- Q_OBJECT
-private slots:
- void fetchtr();
- void fetchtr_data();
+public:
+ QMakeVfs();
- void simtexth();
- void simtexth_data();
-};
+ bool writeFile(const QString &fn, QIODevice::OpenMode mode, const QString &contents, QString *errStr);
+ bool readFile(const QString &fn, QString *contents, QString *errStr);
+ bool exists(const QString &fn);
+#ifndef PROEVALUATOR_FULL
+ void invalidateCache();
+ void invalidateContents();
#endif
+
+private:
+#ifndef PROEVALUATOR_FULL
+# ifdef PROEVALUATOR_THREAD_SAFE
+ QMutex m_mutex;
+# endif
+ QHash<QString, QString> m_files;
+ QString m_magicMissing;
+ QString m_magicExisting;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif // QMAKEVFS_H
diff --git a/src/linguist/shared/translator.cpp b/src/linguist/shared/translator.cpp
index d4de8192c..5beb4a30f 100644
--- a/src/linguist/shared/translator.cpp
+++ b/src/linguist/shared/translator.cpp
@@ -62,6 +62,7 @@
#include <QtCore/QFileInfo>
#include <QtCore/QTextStream>
+#include <private/qlocale_p.h>
#include <private/qtranslator_p.h>
QT_BEGIN_NAMESPACE
@@ -367,30 +368,19 @@ bool Translator::save(const QString &filename, ConversionData &cd, const QString
QString Translator::makeLanguageCode(QLocale::Language language, QLocale::Country country)
{
- QLocale locale(language, country);
- if (country == QLocale::AnyCountry) {
- QString languageCode = locale.name().section(QLatin1Char('_'), 0, 0);
- if (languageCode.length() <= 3)
- return languageCode;
- return QString();
- } else {
- return locale.name();
+ QString result = QLocalePrivate::languageToCode(language);
+ if (language != QLocale::C && country != QLocale::AnyCountry) {
+ result.append(QLatin1Char('_'));
+ result.append(QLocalePrivate::countryToCode(country));
}
+ return result;
}
void Translator::languageAndCountry(const QString &languageCode,
QLocale::Language *lang, QLocale::Country *country)
{
- QLocale locale(languageCode);
- if (lang)
- *lang = locale.language();
-
- if (country) {
- if (languageCode.indexOf(QLatin1Char('_')) != -1)
- *country = locale.country();
- else
- *country = QLocale::AnyCountry;
- }
+ QLocale::Script script;
+ QLocalePrivate::getLangAndCountry(languageCode, *lang, script, *country);
}
int Translator::find(const TranslatorMessage &msg) const
diff --git a/src/linguist/shared/translator.h b/src/linguist/shared/translator.h
index 3d521ac83..6ee62b446 100644
--- a/src/linguist/shared/translator.h
+++ b/src/linguist/shared/translator.h
@@ -93,6 +93,7 @@ public:
QString m_unTrPrefix; // QM specific
QString m_sourceFileName;
QString m_targetFileName;
+ QStringList m_excludes;
QDir m_sourceDir;
QDir m_targetDir; // FIXME: TS specific
QSet<QString> m_projectRoots;
diff --git a/src/linguist/tests/data/main.cpp b/src/linguist/tests/data/main.cpp
deleted file mode 100644
index 331a0c68d..000000000
--- a/src/linguist/tests/data/main.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtGui>
-#include <QtCore>
-
-int main(int argc, char **argv)
-{
- QApplication app(argc, argv);
- QStringList args = app.arguments();
-
- if (argc <= 1) {
- qDebug() << "Usage: " << qPrintable(args[0]) << " <ts-file>";
- return 1;
- }
-
- QTranslator trans;
- trans.load(args[1], ".");
- app.installTranslator(&trans);
-
- QWidget w;
- QVBoxLayout *layout = new QVBoxLayout(&w);
-
- QLabel label1(QObject::tr("XXXXXXXXX \33 XXXXXXXXXXX • and → "), 0);
- QLabel label2(QObject::tr("\32"), 0);
- QLabel label3(QObject::tr("\176"), 0);
- QLabel label4(QObject::tr("\301"), 0);
-
- layout->addWidget(&label1);
- layout->addWidget(&label2);
- layout->addWidget(&label3);
- layout->addWidget(&label4);
-
- w.show();
-
- return app.exec();
-}
diff --git a/src/linguist/tests/data/test.pro b/src/linguist/tests/data/test.pro
deleted file mode 100644
index 412390fbb..000000000
--- a/src/linguist/tests/data/test.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-SOURCES += main.cpp
-
-TRANSLATIONS += t1_en.ts
-TRANSLATIONS += t1_de.ts
diff --git a/src/linguist/tests/tests.pro b/src/linguist/tests/tests.pro
deleted file mode 100644
index dc878bb79..000000000
--- a/src/linguist/tests/tests.pro
+++ /dev/null
@@ -1,17 +0,0 @@
-TARGET = tst_tests
-CONFIG += testcase
-
-QT += xml testlib
-
-HEADERS += \
- tst_linguist.h \
- ../shared/translator.h
-
-SOURCES += \
- tst_linguist.cpp \
- tst_lupdate.cpp \
- tst_simtexth.cpp \
- ../shared/simtexth.cpp \
- ../shared/translator.cpp \
- ../shared/translatormessage.cpp \
- ../shared/xliff.cpp
diff --git a/src/linguist/tests/tst_linguist.cpp b/src/linguist/tests/tst_linguist.cpp
deleted file mode 100644
index 0c9f210a3..000000000
--- a/src/linguist/tests/tst_linguist.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "tst_linguist.h"
-#include "moc_tst_linguist.cpp"
-
-QTEST_MAIN(tst_linguist)
diff --git a/src/linguist/tests/tst_lupdate.cpp b/src/linguist/tests/tst_lupdate.cpp
deleted file mode 100644
index de7cf7055..000000000
--- a/src/linguist/tests/tst_lupdate.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
-#include <QtCore/QtCore>
-
-#include "tst_linguist.h"
-
-void tst_linguist::fetchtr()
-{
- // FIXME: This probably should use some yet-to-be-invented
- // binary interface to 'lupdate' instead of playing around
- // with the filesystem,
-
- QRegExp reg("\\s*");
- QString lupdate("lupdate");
-
- QFETCH(QString, input);
-
- QFETCH(QString, name);
- QFETCH(QString, file);
- QFETCH(QString, line);
- QFETCH(QString, src);
-
- QString result;
-
- QTemporaryFile profile("tst_lu_XXXXXX.pro");
- QTemporaryFile cppfile("tst_lu_XXXXXX.cpp");
- QTemporaryFile tsfile("tst_lu_XXXXXX.ts");
-
- profile.open();
- cppfile.open();
- tsfile.open();
-
-#if 0
- profile.setAutoRemove(false);
- cppfile.setAutoRemove(false);
- tsfile.setAutoRemove(false);
-
- qDebug() << ".pro: " << profile.fileName();
- qDebug() << ".cpp: " << cppfile.fileName();
- qDebug() << ".ts: " << tsfile.fileName();
-#endif
-
- QTextStream prots(&profile);
- prots << "SOURCES += " << cppfile.fileName() << "\n";
- prots << "TRANSLATIONS += " << tsfile.fileName() << "\n";
- prots.flush();
-
- QTextStream cppts(&cppfile);
- cppts.setCodec("ISO 8859-1");
- cppts << input << '\n';
- cppts.flush();
-
- QProcess proc;
- proc.start(lupdate, QStringList() << profile.fileName());
- proc.waitForFinished();
-
- result = tsfile.readAll();
-
- static QRegExp re(
- "<name>(.+)</name>\\s*"
- "<message.*>\\s*" // there might be a numerus="yes" attribiute
- "<location filename=\"(.+)\" line=\"(\\d+)\"/>\\s*"
- "<source>(.+)</source>\\s*"
- "<translation type=\"unfinished\">.*</translation>\\s*"
- );
-
- re.indexIn(result);
- QString resname = re.cap(1);
- //QString resfile = re.cap(2);
- QString resline = re.cap(3);
- QString ressrc = re.cap(4);
-
- //qDebug() << "pattern:" << re.pattern();
- //qDebug() << "result:" << result;
- //qDebug() << "resname:" << resname;
- ////qDebug() << "resfile:" << resfile;
- //qDebug() << "resline:" << resline;
- //qDebug() << "ressource:" << ressrc;
-
- QCOMPARE(src + ": " + resname, src + ": " + name);
- QCOMPARE(src + ": " + resline, src + ": " + line);
- QCOMPARE(src + ": " + ressrc, src + ": " + src);
-}
-
-void tst_linguist::fetchtr_data()
-{
- using namespace QTest;
-
- addColumn<QString>("input");
- addColumn<QString>("name");
- addColumn<QString>("file");
- addColumn<QString>("line");
- addColumn<QString>("src");
-
- // plain stuff
- newRow("00") << "int main() { tr(\"foo\"); }"
- << "@default" << "XXXXXX" << "1" << "foo";
-
- // space at beginning of text
- newRow("01") << "int main() { tr(\" foo\"); }"
- << "@default" << "XXXXXX" << "1" << " foo";
- // space at end of text
- newRow("02") << "int main() { tr(\"foo \"); }"
- << "@default" << "XXXXXX" << "1" << "foo ";
- // space in the middle of the text
- newRow("03") << "int main() { tr(\"foo bar\"); }"
- << "@default" << "XXXXXX" << "1" << "foo bar";
-
- // tab at beginning of text
- newRow("04") << "int main() { tr(\"\tfoo\"); }"
- << "@default" << "XXXXXX" << "1" << "<byte value=\"x9\"/>foo";
- // tab at end of text
- newRow("05") << "int main() { tr(\"foo\t\"); }"
- << "@default" << "XXXXXX" << "1" << "foo<byte value=\"x9\"/>";
- // tab in the middle of the text
- newRow("06") << "int main() { tr(\"foo\tbar\"); }"
- << "@default" << "XXXXXX" << "1" << "foo<byte value=\"x9\"/>bar";
-
- // check for unicode
- newRow("07") << "int main() { tr(\"\32\"); }" // 26 dec
- << "@default" << "XXXXXX" << "1" << "<byte value=\"x1a\"/>";
- // check for unicode
- newRow("08") << "int main() { tr(\"\33\"); }" // 27 dec
- << "@default" << "XXXXXX" << "1" << "<byte value=\"x1b\"/>";
- // check for unicode
- newRow("09") << "int main() { tr(\"\176\"); }" // 124 dec
- << "@default" << "XXXXXX" << "1" << "~";
- // check for unicode
- newRow("10") << "int main() { tr(\"\301\"); }" // 193 dec
- << "@default" << "XXXXXX" << "1" << "&#xc1;";
-
- // Bug 162562: lupdate does not find QCoreApplication::translate() strings
- newRow("11") << "int main() { QString s = QCoreApplication::translate"
- "(\"mycontext\", \"msg\", \"\", QCoreApplication::CodecForTr, 2);"
- << "mycontext" << "XXXXXX" << "1" << "msg";
-
- // Bug 161504: lupdate produces wrong ts file with context "N::QObject"
- newRow("12") << "namespace N { QString foo() "
- "{ return QObject::tr(\"msg\"); }"
- << "QObject" << "XXXXXX" << "1" << "msg";
-
- // Correct example from 161504:
- newRow("13") << "namespace N { QString foo(); }"
- "QString N::anyfunc() { return QObject::tr(\"msg\"); }"
- << "QObject" << "XXXXXX" << "1" << "msg";
-
- // Bug 161106: When specifying ::QObject::tr() then lupdate will
- // take the previous word as being the namespace
- newRow("14") << " std::cout << ::QObject::tr(\"msg\");"
- << "QObject" << "XXXXXX" << "1" << "msg";
-
-}
diff --git a/src/linguist/tests/tst_simtexth.cpp b/src/linguist/tests/tst_simtexth.cpp
deleted file mode 100644
index 3ab3199af..000000000
--- a/src/linguist/tests/tst_simtexth.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
-#include <QtCore/QtCore>
-
-//int getSimilarityScore(const QString &str1, const char* str2);
-#include "../shared/simtexth.h"
-#include "tst_linguist.h"
-
-void tst_linguist::simtexth()
-{
- QFETCH(QString, one);
- QFETCH(QString, two);
- QFETCH(int, expected);
-
- int measured = getSimilarityScore(one, two.toLatin1());
- QCOMPARE(measured, expected);
-}
-
-
-void tst_linguist::simtexth_data()
-{
- using namespace QTest;
-
- addColumn<QString>("one");
- addColumn<QString>("two");
- addColumn<int>("expected");
-
- newRow("00") << "" << "" << 1024;
- newRow("01") << "a" << "a" << 1024;
- newRow("02") << "ab" << "ab" << 1024;
- newRow("03") << "abc" << "abc" << 1024;
- newRow("04") << "abcd" << "abcd" << 1024;
-}
diff --git a/tests/auto/linguist/lupdate/testdata/good/parsecpp/main.cpp b/tests/auto/linguist/lupdate/testdata/good/parsecpp/main.cpp
index f07b85596..62ececc58 100644
--- a/tests/auto/linguist/lupdate/testdata/good/parsecpp/main.cpp
+++ b/tests/auto/linguist/lupdate/testdata/good/parsecpp/main.cpp
@@ -399,3 +399,6 @@ Class42::hello(int something /*= 17 */, QString str = Class42::tr("eyo"))
// QTBUG-27974: strings from included sources are not collected
#include "included.cpp"
+
+// test TR_EXCLUDE
+#include "notincluded.cpp"
diff --git a/tests/auto/linguist/lupdate/testdata/good/parsecpp/project.pro b/tests/auto/linguist/lupdate/testdata/good/parsecpp/project.pro
index ac5c2846e..0fd8a9627 100644
--- a/tests/auto/linguist/lupdate/testdata/good/parsecpp/project.pro
+++ b/tests/auto/linguist/lupdate/testdata/good/parsecpp/project.pro
@@ -2,6 +2,6 @@ SOURCES += main.cpp
SOURCES += finddialog.cpp
SOURCES += excluded.cpp
-TR_EXCLUDE = $$PWD/excluded.*
+TR_EXCLUDE = $$PWD/excluded.* $$PWD/notincluded.cpp
TRANSLATIONS = project.ts
diff --git a/tests/auto/linguist/lupdate/testdata/good/proparsingpri/project.pro b/tests/auto/linguist/lupdate/testdata/good/proparsingpri/project.pro
index 5e23538b9..73c6d19a7 100644
--- a/tests/auto/linguist/lupdate/testdata/good/proparsingpri/project.pro
+++ b/tests/auto/linguist/lupdate/testdata/good/proparsingpri/project.pro
@@ -1,6 +1,7 @@
include(win/win.pri)
-include(mac/mac.pri)
-include(unix/unix.pri)
+more = mac unix
+for(dir, more): \
+ include($$dir/$${dir}.pri)
include (common/common.pri) # Important: keep the space before the '('
include(relativity/relativity.pri)