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