From 66802ef8bf7989dc025e34bf91d93576189c483c Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Wed, 29 May 2013 20:18:51 +0200 Subject: [PATCH] implement simple VFS to support caching during project parsing this tremendously speeds up loading of qt 5.1 based projects (including qt itself) under mac os, as these look up the sdk dynamically, and use caching to avoid doing that in every subproject. Change-Id: I833253f81c3159056fab2ff888f293b36cc2ef56 Reviewed-by: Daniel Teske --- src/plugins/qt4projectmanager/qt4nodes.cpp | 4 +- src/plugins/qt4projectmanager/qt4project.cpp | 19 +- src/plugins/qt4projectmanager/qt4project.h | 4 + src/plugins/qtsupport/baseqtversion.cpp | 6 +- src/plugins/qtsupport/profilereader.cpp | 6 +- src/plugins/qtsupport/profilereader.h | 2 +- src/plugins/qtsupport/qtversionfactory.cpp | 7 +- src/shared/proparser/profileevaluator.cpp | 4 +- src/shared/proparser/profileevaluator.h | 4 +- src/shared/proparser/proparser.pri | 2 + src/shared/proparser/qmakebuiltins.cpp | 43 +---- src/shared/proparser/qmakeevaluator.cpp | 21 ++- src/shared/proparser/qmakeevaluator.h | 3 +- src/shared/proparser/qmakeparser.cpp | 24 +-- src/shared/proparser/qmakeparser.h | 4 +- src/shared/proparser/qmakevfs.cpp | 178 ++++++++++++++++++ src/shared/proparser/qmakevfs.h | 73 +++++++ .../auto/profilewriter/tst_profilewriter.cpp | 16 +- tests/manual/proparser/main.cpp | 13 +- tests/manual/proparser/testreader.pro | 23 ++- 20 files changed, 365 insertions(+), 91 deletions(-) create mode 100644 src/shared/proparser/qmakevfs.cpp create mode 100644 src/shared/proparser/qmakevfs.h diff --git a/src/plugins/qt4projectmanager/qt4nodes.cpp b/src/plugins/qt4projectmanager/qt4nodes.cpp index 2553c1f25d7..26568c4dd61 100644 --- a/src/plugins/qt4projectmanager/qt4nodes.cpp +++ b/src/plugins/qt4projectmanager/qt4nodes.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include @@ -1135,8 +1136,9 @@ void Qt4PriFileNode::changeFiles(const FileType fileType, lines = contents.split(QLatin1Char('\n')); } + QMakeVfs vfs; QtSupport::ProMessageHandler handler; - QMakeParser parser(0, &handler); + QMakeParser parser(0, &vfs, &handler); includeFile = parser.parsedProBlock(contents, m_projectFilePath, 1); } diff --git a/src/plugins/qt4projectmanager/qt4project.cpp b/src/plugins/qt4projectmanager/qt4project.cpp index b2efd28323c..aa811840313 100644 --- a/src/plugins/qt4projectmanager/qt4project.cpp +++ b/src/plugins/qt4projectmanager/qt4project.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,7 @@ #include #include #include +#include #include #include @@ -344,6 +346,7 @@ Qt4Project::Qt4Project(Qt4Manager *manager, const QString& fileName) : m_nodesWatcher(new Internal::Qt4NodesWatcher(this)), m_fileInfo(new Qt4ProjectFile(fileName, this)), m_projectFiles(new Qt4ProjectFiles), + m_qmakeVfs(new QMakeVfs), m_qmakeGlobals(0), m_asyncUpdateFutureInterface(0), m_pendingEvaluateFuturesCount(0), @@ -358,6 +361,10 @@ Qt4Project::Qt4Project(Qt4Manager *manager, const QString& fileName) : m_asyncUpdateTimer.setSingleShot(true); m_asyncUpdateTimer.setInterval(3000); connect(&m_asyncUpdateTimer, SIGNAL(timeout()), this, SLOT(asyncUpdate())); + + connect(ProjectExplorerPlugin::instance()->buildManager(), + SIGNAL(buildQueueFinished(bool)), + SLOT(buildFinished(bool))); } Qt4Project::~Qt4Project() @@ -365,6 +372,7 @@ Qt4Project::~Qt4Project() m_codeModelFuture.cancel(); m_asyncUpdateState = ShuttingDown; m_manager->unregisterProject(this); + delete m_qmakeVfs; delete m_projectFiles; m_cancelEvaluate = true; // Deleting the root node triggers a few things, make sure rootProjectNode @@ -847,6 +855,9 @@ void Qt4Project::asyncUpdate() { if (debug) qDebug()<<"async update, timer expired, doing now"; + + m_qmakeVfs->invalidateCache(); + Q_ASSERT(!m_asyncUpdateFutureInterface); m_asyncUpdateFutureInterface = new QFutureInterface(); @@ -877,6 +888,12 @@ void Qt4Project::asyncUpdate() m_asyncUpdateState = AsyncUpdateInProgress; } +void Qt4Project::buildFinished(bool success) +{ + if (success) + m_qmakeVfs->invalidateContents(); +} + ProjectExplorer::IProjectManager *Qt4Project::projectManager() const { return m_manager; @@ -1002,7 +1019,7 @@ QtSupport::ProFileReader *Qt4Project::createProFileReader(const Qt4ProFileNode * } ++m_qmakeGlobalsRefCnt; - QtSupport::ProFileReader *reader = new QtSupport::ProFileReader(m_qmakeGlobals); + QtSupport::ProFileReader *reader = new QtSupport::ProFileReader(m_qmakeGlobals, m_qmakeVfs); reader->setOutputDir(qt4ProFileNode->buildDir()); diff --git a/src/plugins/qt4projectmanager/qt4project.h b/src/plugins/qt4projectmanager/qt4project.h index 2f402081536..a4bb93b1b3c 100644 --- a/src/plugins/qt4projectmanager/qt4project.h +++ b/src/plugins/qt4projectmanager/qt4project.h @@ -42,6 +42,7 @@ QT_BEGIN_NAMESPACE class ProFileGlobals; +class QMakeVfs; QT_END_NAMESPACE namespace ProjectExplorer { class DeploymentData; } @@ -158,6 +159,7 @@ protected: private slots: void asyncUpdate(); + void buildFinished(bool success); void activeTargetWasChanged(); @@ -198,6 +200,8 @@ private: // cached lists of all of files Internal::Qt4ProjectFiles *m_projectFiles; + QMakeVfs *m_qmakeVfs; + // cached data during project rescan ProFileGlobals *m_qmakeGlobals; int m_qmakeGlobalsRefCnt; diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index 093123caa8b..fcb447bf827 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -36,6 +36,7 @@ #include "qtversionmanager.h" #include "profilereader.h" +#include #include #include #include @@ -780,12 +781,13 @@ void BaseQtVersion::ensureMkSpecParsed() const if (mkspecPath().isEmpty()) return; + QMakeVfs vfs; ProFileGlobals option; option.setProperties(versionInfo()); ProMessageHandler msgHandler(true); ProFileCacheManager::instance()->incRefCount(); - QMakeParser parser(ProFileCacheManager::instance()->cache(), &msgHandler); - ProFileEvaluator evaluator(&option, &parser, &msgHandler); + QMakeParser parser(ProFileCacheManager::instance()->cache(), &vfs, &msgHandler); + ProFileEvaluator evaluator(&option, &parser, &vfs, &msgHandler); evaluator.loadNamedSpec(mkspecPath().toString(), false); parseMkSpec(&evaluator); diff --git a/src/plugins/qtsupport/profilereader.cpp b/src/plugins/qtsupport/profilereader.cpp index b538c0e6cc3..747c4f14ff5 100644 --- a/src/plugins/qtsupport/profilereader.cpp +++ b/src/plugins/qtsupport/profilereader.cpp @@ -65,9 +65,9 @@ void ProMessageHandler::fileMessage(const QString &) } -ProFileReader::ProFileReader(ProFileGlobals *option) - : QMakeParser(ProFileCacheManager::instance()->cache(), this) - , ProFileEvaluator(option, this, this) +ProFileReader::ProFileReader(ProFileGlobals *option, QMakeVfs *vfs) + : QMakeParser(ProFileCacheManager::instance()->cache(), vfs, this) + , ProFileEvaluator(option, this, vfs, this) , m_ignoreLevel(0) { } diff --git a/src/plugins/qtsupport/profilereader.h b/src/plugins/qtsupport/profilereader.h index 2ba4c99aa67..3bd8a914b8c 100644 --- a/src/plugins/qtsupport/profilereader.h +++ b/src/plugins/qtsupport/profilereader.h @@ -69,7 +69,7 @@ class QTSUPPORT_EXPORT ProFileReader : public ProMessageHandler, public QMakePar Q_OBJECT public: - ProFileReader(ProFileGlobals *option); + ProFileReader(ProFileGlobals *option, QMakeVfs *vfs); ~ProFileReader(); QList includeFiles() const; diff --git a/src/plugins/qtsupport/qtversionfactory.cpp b/src/plugins/qtsupport/qtversionfactory.cpp index 7ff99b6ff4f..cac661f629e 100644 --- a/src/plugins/qtsupport/qtversionfactory.cpp +++ b/src/plugins/qtsupport/qtversionfactory.cpp @@ -31,6 +31,8 @@ #include "profilereader.h" #include "baseqtversion.h" +#include + #include #include @@ -61,12 +63,13 @@ BaseQtVersion *QtVersionFactory::createQtVersionFromQMakePath(const Utils::FileN return 0; Utils::FileName mkspec = BaseQtVersion::mkspecFromVersionInfo(versionInfo); + QMakeVfs vfs; ProFileGlobals globals; globals.setProperties(versionInfo); ProMessageHandler msgHandler(true); ProFileCacheManager::instance()->incRefCount(); - QMakeParser parser(ProFileCacheManager::instance()->cache(), &msgHandler); - ProFileEvaluator evaluator(&globals, &parser, &msgHandler); + QMakeParser parser(ProFileCacheManager::instance()->cache(), &vfs, &msgHandler); + ProFileEvaluator evaluator(&globals, &parser, &vfs, &msgHandler); evaluator.loadNamedSpec(mkspec.toString(), false); QList factories = ExtensionSystem::PluginManager::getObjects(); diff --git a/src/shared/proparser/profileevaluator.cpp b/src/shared/proparser/profileevaluator.cpp index 47012d99fdd..c9558519281 100644 --- a/src/shared/proparser/profileevaluator.cpp +++ b/src/shared/proparser/profileevaluator.cpp @@ -43,9 +43,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/shared/proparser/profileevaluator.h b/src/shared/proparser/profileevaluator.h index a172536411d..8d6fec86c3d 100644 --- a/src/shared/proparser/profileevaluator.h +++ b/src/shared/proparser/profileevaluator.h @@ -40,6 +40,7 @@ QT_BEGIN_NAMESPACE +class QMakeVfs; class QMakeParser; class QMakeEvaluator; class QMakeHandler; @@ -65,7 +66,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/shared/proparser/proparser.pri b/src/shared/proparser/proparser.pri index 900151d75f9..a3667e52919 100644 --- a/src/shared/proparser/proparser.pri +++ b/src/shared/proparser/proparser.pri @@ -14,6 +14,7 @@ HEADERS += \ profileevaluator.h \ proitems.h \ prowriter.h \ + qmakevfs.h \ ioutils.h SOURCES += \ @@ -24,6 +25,7 @@ SOURCES += \ qmakebuiltins.cpp \ proitems.cpp \ prowriter.cpp \ + qmakevfs.cpp \ ioutils.cpp RESOURCES += proparser.qrc diff --git a/src/shared/proparser/qmakebuiltins.cpp b/src/shared/proparser/qmakebuiltins.cpp index 00f476cb51f..eb58632c84b 100644 --- a/src/shared/proparser/qmakebuiltins.cpp +++ b/src/shared/proparser/qmakebuiltins.cpp @@ -32,6 +32,7 @@ #include "qmakeevaluator_p.h" #include "qmakeglobals.h" #include "qmakeparser.h" +#include "qmakevfs.h" #include "ioutils.h" #include @@ -269,41 +270,12 @@ 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(fn, mode, contents, &errStr)) { + if (!m_vfs->writeFile(fn, mode, contents, &errStr)) { evalError(fL1S("Cannot write %1file %2: %3.") .arg(ctx, QDir::toNativeSeparators(fn), errStr)); return ReturnFalse; @@ -1413,6 +1385,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('/')); @@ -1444,7 +1419,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) { @@ -1456,9 +1430,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) { @@ -1510,7 +1481,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; @@ -1636,9 +1606,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/shared/proparser/qmakeevaluator.cpp b/src/shared/proparser/qmakeevaluator.cpp index 9ba0d70b16a..5a750adbd0d 100644 --- a/src/shared/proparser/qmakeevaluator.cpp +++ b/src/shared/proparser/qmakeevaluator.cpp @@ -32,6 +32,7 @@ #include "qmakeglobals.h" #include "qmakeparser.h" +#include "qmakevfs.h" #include "ioutils.h" #include @@ -162,13 +163,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(); @@ -1049,7 +1050,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; } @@ -1064,10 +1065,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) @@ -1158,7 +1159,7 @@ 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); if (!m_superfile.isEmpty()) { valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile); if (evaluator.evaluateFile( @@ -1315,7 +1316,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProFile( locker.unlock(); #endif - QMakeEvaluator *baseEval = new QMakeEvaluator(m_option, m_parser, m_handler); + 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; @@ -1810,7 +1811,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFile( #endif return ok; } else { - if (!(flags & LoadSilent) && !IoUtils::exists(fileName)) + if (!(flags & LoadSilent) && !m_vfs->exists(fileName)) evalError(fL1S("WARNING: Include file %1 not found").arg(fileName)); return ReturnFalse; } @@ -1893,7 +1894,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/shared/proparser/qmakeevaluator.h b/src/shared/proparser/qmakeevaluator.h index 63d34e8f764..4d2e7c7693a 100644 --- a/src/shared/proparser/qmakeevaluator.h +++ b/src/shared/proparser/qmakeevaluator.h @@ -96,7 +96,7 @@ public: static void initStatics(); static void initFunctionStatics(); - QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, + QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, QMakeVfs *vfs, QMakeHandler *handler); ~QMakeEvaluator(); @@ -282,6 +282,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/shared/proparser/qmakeparser.cpp b/src/shared/proparser/qmakeparser.cpp index ae0fdd0fac5..9dd10ac448e 100644 --- a/src/shared/proparser/qmakeparser.cpp +++ b/src/shared/proparser/qmakeparser.cpp @@ -29,6 +29,7 @@ #include "qmakeparser.h" +#include "qmakevfs.h" #include "ioutils.h" using namespace QMakeInternal; @@ -130,9 +131,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(); @@ -218,24 +220,14 @@ void QMakeParser::discardFileFromCache(const QString &fileName) bool QMakeParser::read(ProFile *pro) { - QFile file(pro->fileName()); - if (!file.open(QIODevice::ReadOnly)) { - if (m_handler && IoUtils::exists(pro->fileName())) + QString content; + QString errStr; + if (!m_vfs->readFile(pro->fileName(), &content, &errStr)) { + if (m_handler && 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/shared/proparser/qmakeparser.h b/src/shared/proparser/qmakeparser.h index 6fc599f617d..87cd8c2244a 100644 --- a/src/shared/proparser/qmakeparser.h +++ b/src/shared/proparser/qmakeparser.h @@ -67,6 +67,7 @@ public: }; class ProFileCache; +class QMakeVfs; class QMAKE_EXPORT QMakeParser { @@ -74,7 +75,7 @@ public: // Call this from a concurrency-free context static void initialize(); - 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. @@ -163,6 +164,7 @@ private: ProFileCache *m_cache; QMakeParserHandler *m_handler; + QMakeVfs *m_vfs; // This doesn't help gcc 3.3 ... template friend class QTypeInfo; diff --git a/src/shared/proparser/qmakevfs.cpp b/src/shared/proparser/qmakevfs.cpp new file mode 100644 index 00000000000..69373c4a9e4 --- /dev/null +++ b/src/shared/proparser/qmakevfs.cpp @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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. +** +****************************************************************************/ + +#include "qmakevfs.h" + +#include "ioutils.h" +using namespace QMakeInternal; + +#include +#include +#include + +#define fL1S(s) QString::fromLatin1(s) + +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::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::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::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/shared/proparser/qmakevfs.h b/src/shared/proparser/qmakevfs.h new file mode 100644 index 00000000000..045765a3629 --- /dev/null +++ b/src/shared/proparser/qmakevfs.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** 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. +** +****************************************************************************/ + +#ifndef QMAKEVFS_H +#define QMAKEVFS_H + +#include "qmake_global.h" + +# include +#ifndef PROEVALUATOR_FULL +# include +# include +# ifdef PROEVALUATOR_THREAD_SAFE +# include +# endif +#endif + +QT_BEGIN_NAMESPACE + +class QMAKE_EXPORT QMakeVfs +{ +public: + QMakeVfs(); + + 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 m_files; + QString m_magicMissing; + QString m_magicExisting; +#endif +}; + +QT_END_NAMESPACE + +#endif // QMAKEVFS_H diff --git a/tests/auto/profilewriter/tst_profilewriter.cpp b/tests/auto/profilewriter/tst_profilewriter.cpp index cf31b48efe5..4dd71629b8e 100644 --- a/tests/auto/profilewriter/tst_profilewriter.cpp +++ b/tests/auto/profilewriter/tst_profilewriter.cpp @@ -27,6 +27,7 @@ ** ****************************************************************************/ +#include #include #include @@ -449,7 +450,8 @@ void tst_ProFileWriter::adds() QStringList lines = input.isEmpty() ? QStringList() : input.split(QLatin1String("\n")); QString var = QLatin1String("SOURCES"); - QMakeParser parser(0, &parseHandler); + QMakeVfs vfs; + QMakeParser parser(0, &vfs, &parseHandler); ProFile *proFile = parser.parsedProBlock(input, QLatin1String(BASE_DIR "/test.pro"), 1); QVERIFY(proFile); PW::putVarValues(proFile, &lines, values, var, PW::PutFlags(flags), scope); @@ -619,7 +621,8 @@ void tst_ProFileWriter::removes() QStringList lines = input.split(QLatin1String("\n")); QStringList vars; vars << QLatin1String("SOURCES"); - QMakeParser parser(0, &parseHandler); + QMakeVfs vfs; + QMakeParser parser(0, &vfs, &parseHandler); ProFile *proFile = parser.parsedProBlock(input, QLatin1String(BASE_DIR "/test.pro"), 1); QVERIFY(proFile); Qt4ProjectManager::Internal::ProWriter::removeVarValues(proFile, &lines, values, vars); @@ -647,7 +650,8 @@ void tst_ProFileWriter::multiVar() << QString::fromLatin1(BASE_DIR "/bak"); QStringList vars; vars << QLatin1String("SOURCES") << QLatin1String("HEADERS"); - QMakeParser parser(0, &parseHandler); + QMakeVfs vfs; + QMakeParser parser(0, &vfs, &parseHandler); ProFile *proFile = parser.parsedProBlock(input, QLatin1String(BASE_DIR "/test.pro"), 1); QVERIFY(proFile); Qt4ProjectManager::Internal::ProWriter::removeFiles(proFile, &lines, baseDir, files, vars); @@ -667,7 +671,8 @@ void tst_ProFileWriter::addFiles() " sub/bar.cpp" ); - QMakeParser parser(0, &parseHandler); + QMakeVfs vfs; + QMakeParser parser(0, &vfs, &parseHandler); ProFile *proFile = parser.parsedProBlock(input, QLatin1String(BASE_DIR "/test.pro"), 1); QVERIFY(proFile); Qt4ProjectManager::Internal::ProWriter::addFiles(proFile, &lines, QDir(BASE_DIR), @@ -688,7 +693,8 @@ void tst_ProFileWriter::removeFiles() "SOURCES = foo.cpp" ); - QMakeParser parser(0, &parseHandler); + QMakeVfs vfs; + QMakeParser parser(0, &vfs, &parseHandler); ProFile *proFile = parser.parsedProBlock(input, QLatin1String(BASE_DIR "/test.pro"), 1); QVERIFY(proFile); Qt4ProjectManager::Internal::ProWriter::removeFiles(proFile, &lines, QDir(BASE_DIR), diff --git a/tests/manual/proparser/main.cpp b/tests/manual/proparser/main.cpp index 36ee676106c..c7b5d972327 100644 --- a/tests/manual/proparser/main.cpp +++ b/tests/manual/proparser/main.cpp @@ -28,6 +28,7 @@ ****************************************************************************/ #include "qmakeglobals.h" +#include "qmakevfs.h" #include "qmakeparser.h" #include "qmakeevaluator.h" #include "profileevaluator.h" @@ -69,14 +70,15 @@ public: static EvalHandler evalHandler; static int evaluate(const QString &fileName, const QString &in_pwd, const QString &out_pwd, - bool cumulative, ProFileGlobals *option, QMakeParser *parser, int level) + bool cumulative, ProFileGlobals *option, QMakeParser *parser, QMakeVfs *vfs, + int level) { static QSet visited; if (visited.contains(fileName)) return 0; visited.insert(fileName); - ProFileEvaluator visitor(option, parser, &evalHandler); + ProFileEvaluator visitor(option, parser, vfs, &evalHandler); #ifdef PROEVALUATOR_CUMULATIVE visitor.setCumulative(cumulative); #endif @@ -130,7 +132,7 @@ static int evaluate(const QString &fileName, const QString &in_pwd, const QStrin fflush(stdout); nlevel++; } - evaluate(inFile, inPwd, outPwd, cumulative, option, parser, nlevel); + evaluate(inFile, inPwd, outPwd, cumulative, option, parser, vfs, nlevel); } } @@ -195,6 +197,7 @@ int main(int argc, char **argv) out_pwd = in_pwd; option.setDirectories(in_pwd, out_pwd); - QMakeParser parser(0, &evalHandler); - return evaluate(file, in_pwd, out_pwd, cumulative, &option, &parser, level); + QMakeVfs vfs; + QMakeParser parser(0, &vfs, &evalHandler); + return evaluate(file, in_pwd, out_pwd, cumulative, &option, &parser, &vfs, level); } diff --git a/tests/manual/proparser/testreader.pro b/tests/manual/proparser/testreader.pro index 6d58c9e257d..022ac67667c 100644 --- a/tests/manual/proparser/testreader.pro +++ b/tests/manual/proparser/testreader.pro @@ -15,8 +15,27 @@ build_all:!build_pass { CONFIG += release } -SOURCES = main.cpp qmakeglobals.cpp qmakeparser.cpp qmakeevaluator.cpp profileevaluator.cpp qmakebuiltins.cpp proitems.cpp ioutils.cpp -HEADERS = qmakeglobals.h qmakeparser.h profileevaluator.h qmakeevaluator.h qmakeevaluator_p.h proitems.h ioutils.h +SOURCES += \ + main.cpp \ + qmakeglobals.cpp \ + qmakeparser.cpp \ + qmakeevaluator.cpp \ + profileevaluator.cpp \ + qmakebuiltins.cpp \ + proitems.cpp \ + qmakevfs.cpp \ + ioutils.cpp + +HEADERS += \ + qmake_global.h \ + qmakeglobals.h \ + qmakeparser.h \ + qmakeevaluator.h \ + qmakeevaluator_p.h \ + profileevaluator.h \ + proitems.h \ + qmakevfs.h \ + ioutils.h RESOURCES += proparser.qrc DEFINES += QMAKE_BUILTIN_PRFS