diff options
author | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-08-27 22:46:13 +0200 |
---|---|---|
committer | Frederik Gladhorn <frederik.gladhorn@digia.com> | 2013-08-27 22:46:13 +0200 |
commit | 3e7f3a198c26fce9d19fab38d33444d05ea29c74 (patch) | |
tree | d16d5de3df640ca794e383ca332ee8a211111fcf | |
parent | 140fa19ad926f15246c8a501f8a3ccdeda4c5420 (diff) | |
parent | 262744ac1b6ba95d72bedc7679e2b49c11c74803 (diff) | |
download | qttools-3e7f3a198c26fce9d19fab38d33444d05ea29c74.tar.gz |
Merge remote-tracking branch 'origin/stable' into dev
Change-Id: I5db3f62185e7e99ce2c701debd00a44f40a9f5e5
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" << "Á"; - - // 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) |