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 <daniel.teske@digia.com>
This commit is contained in:
Oswald Buddenhagen
2013-05-29 20:18:51 +02:00
parent 571234786a
commit 66802ef8bf
20 changed files with 365 additions and 91 deletions

View File

@@ -56,6 +56,7 @@
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/stringutils.h> #include <utils/stringutils.h>
#include <proparser/prowriter.h> #include <proparser/prowriter.h>
#include <proparser/qmakevfs.h>
#include <algorithm> #include <algorithm>
#include <QDebug> #include <QDebug>
@@ -1135,8 +1136,9 @@ void Qt4PriFileNode::changeFiles(const FileType fileType,
lines = contents.split(QLatin1Char('\n')); lines = contents.split(QLatin1Char('\n'));
} }
QMakeVfs vfs;
QtSupport::ProMessageHandler handler; QtSupport::ProMessageHandler handler;
QMakeParser parser(0, &handler); QMakeParser parser(0, &vfs, &handler);
includeFile = parser.parsedProBlock(contents, m_projectFilePath, 1); includeFile = parser.parsedProBlock(contents, m_projectFilePath, 1);
} }

View File

@@ -46,6 +46,7 @@
#include <coreplugin/documentmanager.h> #include <coreplugin/documentmanager.h>
#include <cpptools/cppmodelmanagerinterface.h> #include <cpptools/cppmodelmanagerinterface.h>
#include <qmljstools/qmljsmodelmanager.h> #include <qmljstools/qmljsmodelmanager.h>
#include <projectexplorer/buildmanager.h>
#include <projectexplorer/buildtargetinfo.h> #include <projectexplorer/buildtargetinfo.h>
#include <projectexplorer/deploymentdata.h> #include <projectexplorer/deploymentdata.h>
#include <projectexplorer/toolchain.h> #include <projectexplorer/toolchain.h>
@@ -53,6 +54,7 @@
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectmacroexpander.h> #include <projectexplorer/projectmacroexpander.h>
#include <proparser/qmakevfs.h>
#include <qtsupport/profilereader.h> #include <qtsupport/profilereader.h>
#include <qtsupport/qtkitinformation.h> #include <qtsupport/qtkitinformation.h>
@@ -344,6 +346,7 @@ Qt4Project::Qt4Project(Qt4Manager *manager, const QString& fileName) :
m_nodesWatcher(new Internal::Qt4NodesWatcher(this)), m_nodesWatcher(new Internal::Qt4NodesWatcher(this)),
m_fileInfo(new Qt4ProjectFile(fileName, this)), m_fileInfo(new Qt4ProjectFile(fileName, this)),
m_projectFiles(new Qt4ProjectFiles), m_projectFiles(new Qt4ProjectFiles),
m_qmakeVfs(new QMakeVfs),
m_qmakeGlobals(0), m_qmakeGlobals(0),
m_asyncUpdateFutureInterface(0), m_asyncUpdateFutureInterface(0),
m_pendingEvaluateFuturesCount(0), m_pendingEvaluateFuturesCount(0),
@@ -358,6 +361,10 @@ Qt4Project::Qt4Project(Qt4Manager *manager, const QString& fileName) :
m_asyncUpdateTimer.setSingleShot(true); m_asyncUpdateTimer.setSingleShot(true);
m_asyncUpdateTimer.setInterval(3000); m_asyncUpdateTimer.setInterval(3000);
connect(&m_asyncUpdateTimer, SIGNAL(timeout()), this, SLOT(asyncUpdate())); connect(&m_asyncUpdateTimer, SIGNAL(timeout()), this, SLOT(asyncUpdate()));
connect(ProjectExplorerPlugin::instance()->buildManager(),
SIGNAL(buildQueueFinished(bool)),
SLOT(buildFinished(bool)));
} }
Qt4Project::~Qt4Project() Qt4Project::~Qt4Project()
@@ -365,6 +372,7 @@ Qt4Project::~Qt4Project()
m_codeModelFuture.cancel(); m_codeModelFuture.cancel();
m_asyncUpdateState = ShuttingDown; m_asyncUpdateState = ShuttingDown;
m_manager->unregisterProject(this); m_manager->unregisterProject(this);
delete m_qmakeVfs;
delete m_projectFiles; delete m_projectFiles;
m_cancelEvaluate = true; m_cancelEvaluate = true;
// Deleting the root node triggers a few things, make sure rootProjectNode // Deleting the root node triggers a few things, make sure rootProjectNode
@@ -847,6 +855,9 @@ void Qt4Project::asyncUpdate()
{ {
if (debug) if (debug)
qDebug()<<"async update, timer expired, doing now"; qDebug()<<"async update, timer expired, doing now";
m_qmakeVfs->invalidateCache();
Q_ASSERT(!m_asyncUpdateFutureInterface); Q_ASSERT(!m_asyncUpdateFutureInterface);
m_asyncUpdateFutureInterface = new QFutureInterface<void>(); m_asyncUpdateFutureInterface = new QFutureInterface<void>();
@@ -877,6 +888,12 @@ void Qt4Project::asyncUpdate()
m_asyncUpdateState = AsyncUpdateInProgress; m_asyncUpdateState = AsyncUpdateInProgress;
} }
void Qt4Project::buildFinished(bool success)
{
if (success)
m_qmakeVfs->invalidateContents();
}
ProjectExplorer::IProjectManager *Qt4Project::projectManager() const ProjectExplorer::IProjectManager *Qt4Project::projectManager() const
{ {
return m_manager; return m_manager;
@@ -1002,7 +1019,7 @@ QtSupport::ProFileReader *Qt4Project::createProFileReader(const Qt4ProFileNode *
} }
++m_qmakeGlobalsRefCnt; ++m_qmakeGlobalsRefCnt;
QtSupport::ProFileReader *reader = new QtSupport::ProFileReader(m_qmakeGlobals); QtSupport::ProFileReader *reader = new QtSupport::ProFileReader(m_qmakeGlobals, m_qmakeVfs);
reader->setOutputDir(qt4ProFileNode->buildDir()); reader->setOutputDir(qt4ProFileNode->buildDir());

View File

@@ -42,6 +42,7 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class ProFileGlobals; class ProFileGlobals;
class QMakeVfs;
QT_END_NAMESPACE QT_END_NAMESPACE
namespace ProjectExplorer { class DeploymentData; } namespace ProjectExplorer { class DeploymentData; }
@@ -158,6 +159,7 @@ protected:
private slots: private slots:
void asyncUpdate(); void asyncUpdate();
void buildFinished(bool success);
void activeTargetWasChanged(); void activeTargetWasChanged();
@@ -198,6 +200,8 @@ private:
// cached lists of all of files // cached lists of all of files
Internal::Qt4ProjectFiles *m_projectFiles; Internal::Qt4ProjectFiles *m_projectFiles;
QMakeVfs *m_qmakeVfs;
// cached data during project rescan // cached data during project rescan
ProFileGlobals *m_qmakeGlobals; ProFileGlobals *m_qmakeGlobals;
int m_qmakeGlobalsRefCnt; int m_qmakeGlobalsRefCnt;

View File

@@ -36,6 +36,7 @@
#include "qtversionmanager.h" #include "qtversionmanager.h"
#include "profilereader.h" #include "profilereader.h"
#include <proparser/qmakevfs.h>
#include <projectexplorer/toolchainmanager.h> #include <projectexplorer/toolchainmanager.h>
#include <projectexplorer/toolchain.h> #include <projectexplorer/toolchain.h>
#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorer.h>
@@ -780,12 +781,13 @@ void BaseQtVersion::ensureMkSpecParsed() const
if (mkspecPath().isEmpty()) if (mkspecPath().isEmpty())
return; return;
QMakeVfs vfs;
ProFileGlobals option; ProFileGlobals option;
option.setProperties(versionInfo()); option.setProperties(versionInfo());
ProMessageHandler msgHandler(true); ProMessageHandler msgHandler(true);
ProFileCacheManager::instance()->incRefCount(); ProFileCacheManager::instance()->incRefCount();
QMakeParser parser(ProFileCacheManager::instance()->cache(), &msgHandler); QMakeParser parser(ProFileCacheManager::instance()->cache(), &vfs, &msgHandler);
ProFileEvaluator evaluator(&option, &parser, &msgHandler); ProFileEvaluator evaluator(&option, &parser, &vfs, &msgHandler);
evaluator.loadNamedSpec(mkspecPath().toString(), false); evaluator.loadNamedSpec(mkspecPath().toString(), false);
parseMkSpec(&evaluator); parseMkSpec(&evaluator);

View File

@@ -65,9 +65,9 @@ void ProMessageHandler::fileMessage(const QString &)
} }
ProFileReader::ProFileReader(ProFileGlobals *option) ProFileReader::ProFileReader(ProFileGlobals *option, QMakeVfs *vfs)
: QMakeParser(ProFileCacheManager::instance()->cache(), this) : QMakeParser(ProFileCacheManager::instance()->cache(), vfs, this)
, ProFileEvaluator(option, this, this) , ProFileEvaluator(option, this, vfs, this)
, m_ignoreLevel(0) , m_ignoreLevel(0)
{ {
} }

View File

@@ -69,7 +69,7 @@ class QTSUPPORT_EXPORT ProFileReader : public ProMessageHandler, public QMakePar
Q_OBJECT Q_OBJECT
public: public:
ProFileReader(ProFileGlobals *option); ProFileReader(ProFileGlobals *option, QMakeVfs *vfs);
~ProFileReader(); ~ProFileReader();
QList<ProFile*> includeFiles() const; QList<ProFile*> includeFiles() const;

View File

@@ -31,6 +31,8 @@
#include "profilereader.h" #include "profilereader.h"
#include "baseqtversion.h" #include "baseqtversion.h"
#include <proparser/qmakevfs.h>
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
#include <utils/environment.h> #include <utils/environment.h>
@@ -61,12 +63,13 @@ BaseQtVersion *QtVersionFactory::createQtVersionFromQMakePath(const Utils::FileN
return 0; return 0;
Utils::FileName mkspec = BaseQtVersion::mkspecFromVersionInfo(versionInfo); Utils::FileName mkspec = BaseQtVersion::mkspecFromVersionInfo(versionInfo);
QMakeVfs vfs;
ProFileGlobals globals; ProFileGlobals globals;
globals.setProperties(versionInfo); globals.setProperties(versionInfo);
ProMessageHandler msgHandler(true); ProMessageHandler msgHandler(true);
ProFileCacheManager::instance()->incRefCount(); ProFileCacheManager::instance()->incRefCount();
QMakeParser parser(ProFileCacheManager::instance()->cache(), &msgHandler); QMakeParser parser(ProFileCacheManager::instance()->cache(), &vfs, &msgHandler);
ProFileEvaluator evaluator(&globals, &parser, &msgHandler); ProFileEvaluator evaluator(&globals, &parser, &vfs, &msgHandler);
evaluator.loadNamedSpec(mkspec.toString(), false); evaluator.loadNamedSpec(mkspec.toString(), false);
QList<QtVersionFactory *> factories = ExtensionSystem::PluginManager::getObjects<QtVersionFactory>(); QList<QtVersionFactory *> factories = ExtensionSystem::PluginManager::getObjects<QtVersionFactory>();

View File

@@ -43,9 +43,9 @@ void ProFileEvaluator::initialize()
QMakeEvaluator::initStatics(); QMakeEvaluator::initStatics();
} }
ProFileEvaluator::ProFileEvaluator(ProFileGlobals *option, QMakeParser *parser, ProFileEvaluator::ProFileEvaluator(ProFileGlobals *option, QMakeParser *parser, QMakeVfs *vfs,
QMakeHandler *handler) QMakeHandler *handler)
: d(new QMakeEvaluator(option, parser, handler)) : d(new QMakeEvaluator(option, parser, vfs, handler))
{ {
} }

View File

@@ -40,6 +40,7 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QMakeVfs;
class QMakeParser; class QMakeParser;
class QMakeEvaluator; class QMakeEvaluator;
class QMakeHandler; class QMakeHandler;
@@ -65,7 +66,8 @@ public:
// Call this from a concurrency-free context // Call this from a concurrency-free context
static void initialize(); static void initialize();
ProFileEvaluator(ProFileGlobals *option, QMakeParser *parser, QMakeHandler *handler); ProFileEvaluator(ProFileGlobals *option, QMakeParser *parser, QMakeVfs *vfs,
QMakeHandler *handler);
~ProFileEvaluator(); ~ProFileEvaluator();
ProFileEvaluator::TemplateType templateType() const; ProFileEvaluator::TemplateType templateType() const;

View File

@@ -14,6 +14,7 @@ HEADERS += \
profileevaluator.h \ profileevaluator.h \
proitems.h \ proitems.h \
prowriter.h \ prowriter.h \
qmakevfs.h \
ioutils.h ioutils.h
SOURCES += \ SOURCES += \
@@ -24,6 +25,7 @@ SOURCES += \
qmakebuiltins.cpp \ qmakebuiltins.cpp \
proitems.cpp \ proitems.cpp \
prowriter.cpp \ prowriter.cpp \
qmakevfs.cpp \
ioutils.cpp ioutils.cpp
RESOURCES += proparser.qrc RESOURCES += proparser.qrc

View File

@@ -32,6 +32,7 @@
#include "qmakeevaluator_p.h" #include "qmakeevaluator_p.h"
#include "qmakeglobals.h" #include "qmakeglobals.h"
#include "qmakeparser.h" #include "qmakeparser.h"
#include "qmakevfs.h"
#include "ioutils.h" #include "ioutils.h"
#include <qbytearray.h> #include <qbytearray.h>
@@ -269,41 +270,12 @@ quoteValue(const ProString &val)
return ret; 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::VisitReturn
QMakeEvaluator::writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode, QMakeEvaluator::writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode,
const QString &contents) 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; QString errStr;
if (!doWriteFile(fn, mode, contents, &errStr)) { if (!m_vfs->writeFile(fn, mode, contents, &errStr)) {
evalError(fL1S("Cannot write %1file %2: %3.") evalError(fL1S("Cannot write %1file %2: %3.")
.arg(ctx, QDir::toNativeSeparators(fn), errStr)); .arg(ctx, QDir::toNativeSeparators(fn), errStr));
return ReturnFalse; return ReturnFalse;
@@ -1413,6 +1385,9 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
} }
const QString &file = resolvePath(m_option->expandEnvVars(args.at(0).toQString(m_tmp1))); 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)) if (IoUtils::exists(file))
return ReturnTrue; return ReturnTrue;
int slsh = file.lastIndexOf(QLatin1Char('/')); 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.")); evalError(fL1S("write_file(name, [content var, [append]]) requires one to three arguments."));
return ReturnFalse; return ReturnFalse;
} }
#ifdef PROEVALUATOR_FULL
QIODevice::OpenMode mode = QIODevice::Truncate; QIODevice::OpenMode mode = QIODevice::Truncate;
QString contents; QString contents;
if (args.count() >= 2) { if (args.count() >= 2) {
@@ -1456,9 +1430,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
mode = QIODevice::Append; mode = QIODevice::Append;
} }
return writeFile(QString(), resolvePath(args.at(0).toQString(m_tmp1)), mode, contents); return writeFile(QString(), resolvePath(args.at(0).toQString(m_tmp1)), mode, contents);
#else
return ReturnTrue;
#endif
} }
case T_TOUCH: { case T_TOUCH: {
if (args.count() != 2) { 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.")); evalError(fL1S("cache(var, [set|add|sub] [transient] [super], [srcvar]) requires one to three arguments."));
return ReturnFalse; return ReturnFalse;
} }
#ifdef PROEVALUATOR_FULL
bool persist = true; bool persist = true;
bool super = false; bool super = false;
enum { CacheSet, CacheAdd, CacheSub } mode = CacheSet; enum { CacheSet, CacheAdd, CacheSub } mode = CacheSet;
@@ -1636,9 +1606,6 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional(
fn = m_cachefile; fn = m_cachefile;
} }
return writeFile(fL1S("cache "), fn, QIODevice::Append, varstr); return writeFile(fL1S("cache "), fn, QIODevice::Append, varstr);
#else
return ReturnTrue;
#endif
} }
default: default:
evalError(fL1S("Function '%1' is not implemented.").arg(function.toQString(m_tmp1))); evalError(fL1S("Function '%1' is not implemented.").arg(function.toQString(m_tmp1)));

View File

@@ -32,6 +32,7 @@
#include "qmakeglobals.h" #include "qmakeglobals.h"
#include "qmakeparser.h" #include "qmakeparser.h"
#include "qmakevfs.h"
#include "ioutils.h" #include "ioutils.h"
#include <qbytearray.h> #include <qbytearray.h>
@@ -162,13 +163,13 @@ const ProKey &QMakeEvaluator::map(const ProKey &var)
} }
QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option, QMakeEvaluator::QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, QMakeVfs *vfs,
QMakeParser *parser, QMakeHandler *handler) QMakeHandler *handler)
: :
#ifdef PROEVALUATOR_DEBUG #ifdef PROEVALUATOR_DEBUG
m_debugLevel(option->debugLevel), m_debugLevel(option->debugLevel),
#endif #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. // So that single-threaded apps don't have to call initialize() for now.
initStatics(); initStatics();
@@ -1049,7 +1050,7 @@ bool QMakeEvaluator::prepareProject(const QString &inDir)
superdir = m_outputDir; superdir = m_outputDir;
forever { forever {
QString superfile = superdir + QLatin1String("/.qmake.super"); QString superfile = superdir + QLatin1String("/.qmake.super");
if (IoUtils::exists(superfile)) { if (m_vfs->exists(superfile)) {
m_superfile = QDir::cleanPath(superfile); m_superfile = QDir::cleanPath(superfile);
break; break;
} }
@@ -1064,10 +1065,10 @@ bool QMakeEvaluator::prepareProject(const QString &inDir)
QString dir = m_outputDir; QString dir = m_outputDir;
forever { forever {
conffile = sdir + QLatin1String("/.qmake.conf"); conffile = sdir + QLatin1String("/.qmake.conf");
if (!IoUtils::exists(conffile)) if (!m_vfs->exists(conffile))
conffile.clear(); conffile.clear();
cachefile = dir + QLatin1String("/.qmake.cache"); cachefile = dir + QLatin1String("/.qmake.cache");
if (!IoUtils::exists(cachefile)) if (!m_vfs->exists(cachefile))
cachefile.clear(); cachefile.clear();
if (!conffile.isEmpty() || !cachefile.isEmpty()) { if (!conffile.isEmpty() || !cachefile.isEmpty()) {
if (dir != sdir) if (dir != sdir)
@@ -1158,7 +1159,7 @@ bool QMakeEvaluator::loadSpec()
m_hostBuild ? m_option->qmakespec : m_option->xqmakespec); 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()) { if (!m_superfile.isEmpty()) {
valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile); valuesRef(ProKey("_QMAKE_SUPER_CACHE_")) << ProString(m_superfile);
if (evaluator.evaluateFile( if (evaluator.evaluateFile(
@@ -1315,7 +1316,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::visitProFile(
locker.unlock(); locker.unlock();
#endif #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; baseEnv->evaluator = baseEval;
baseEval->m_superfile = m_superfile; baseEval->m_superfile = m_superfile;
baseEval->m_conffile = m_conffile; baseEval->m_conffile = m_conffile;
@@ -1810,7 +1811,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFile(
#endif #endif
return ok; return ok;
} else { } else {
if (!(flags & LoadSilent) && !IoUtils::exists(fileName)) if (!(flags & LoadSilent) && !m_vfs->exists(fileName))
evalError(fL1S("WARNING: Include file %1 not found").arg(fileName)); evalError(fL1S("WARNING: Include file %1 not found").arg(fileName));
return ReturnFalse; return ReturnFalse;
} }
@@ -1893,7 +1894,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFeatureFile(
QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFileInto( QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFileInto(
const QString &fileName, ProValueMap *values, LoadFlags flags) 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_caller = this;
visitor.m_outputDir = m_outputDir; visitor.m_outputDir = m_outputDir;
visitor.m_featureRoots = m_featureRoots; visitor.m_featureRoots = m_featureRoots;

View File

@@ -96,7 +96,7 @@ public:
static void initStatics(); static void initStatics();
static void initFunctionStatics(); static void initFunctionStatics();
QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, QMakeEvaluator(QMakeGlobals *option, QMakeParser *parser, QMakeVfs *vfs,
QMakeHandler *handler); QMakeHandler *handler);
~QMakeEvaluator(); ~QMakeEvaluator();
@@ -282,6 +282,7 @@ public:
QMakeGlobals *m_option; QMakeGlobals *m_option;
QMakeParser *m_parser; QMakeParser *m_parser;
QMakeHandler *m_handler; QMakeHandler *m_handler;
QMakeVfs *m_vfs;
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeEvaluator::LoadFlags) Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeEvaluator::LoadFlags)

View File

@@ -29,6 +29,7 @@
#include "qmakeparser.h" #include "qmakeparser.h"
#include "qmakevfs.h"
#include "ioutils.h" #include "ioutils.h"
using namespace QMakeInternal; using namespace QMakeInternal;
@@ -130,9 +131,10 @@ void QMakeParser::initialize()
statics.strLITERAL_WHITESPACE = QLatin1String("LITERAL_WHITESPACE"); statics.strLITERAL_WHITESPACE = QLatin1String("LITERAL_WHITESPACE");
} }
QMakeParser::QMakeParser(ProFileCache *cache, QMakeParserHandler *handler) QMakeParser::QMakeParser(ProFileCache *cache, QMakeVfs *vfs, QMakeParserHandler *handler)
: m_cache(cache) : m_cache(cache)
, m_handler(handler) , m_handler(handler)
, m_vfs(vfs)
{ {
// So that single-threaded apps don't have to call initialize() for now. // So that single-threaded apps don't have to call initialize() for now.
initialize(); initialize();
@@ -218,24 +220,14 @@ void QMakeParser::discardFileFromCache(const QString &fileName)
bool QMakeParser::read(ProFile *pro) bool QMakeParser::read(ProFile *pro)
{ {
QFile file(pro->fileName()); QString content;
if (!file.open(QIODevice::ReadOnly)) { QString errStr;
if (m_handler && IoUtils::exists(pro->fileName())) if (!m_vfs->readFile(pro->fileName(), &content, &errStr)) {
if (m_handler && m_vfs->exists(pro->fileName()))
m_handler->message(QMakeParserHandler::ParserIoError, 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; 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); return read(pro, content, 1, FullGrammar);
} }

View File

@@ -67,6 +67,7 @@ public:
}; };
class ProFileCache; class ProFileCache;
class QMakeVfs;
class QMAKE_EXPORT QMakeParser class QMAKE_EXPORT QMakeParser
{ {
@@ -74,7 +75,7 @@ public:
// Call this from a concurrency-free context // Call this from a concurrency-free context
static void initialize(); static void initialize();
QMakeParser(ProFileCache *cache, QMakeParserHandler *handler); QMakeParser(ProFileCache *cache, QMakeVfs *vfs, QMakeParserHandler *handler);
enum SubGrammar { FullGrammar, TestGrammar, ValueGrammar }; enum SubGrammar { FullGrammar, TestGrammar, ValueGrammar };
// fileName is expected to be absolute and cleanPath()ed. // fileName is expected to be absolute and cleanPath()ed.
@@ -163,6 +164,7 @@ private:
ProFileCache *m_cache; ProFileCache *m_cache;
QMakeParserHandler *m_handler; QMakeParserHandler *m_handler;
QMakeVfs *m_vfs;
// This doesn't help gcc 3.3 ... // This doesn't help gcc 3.3 ...
template<typename T> friend class QTypeInfo; template<typename T> friend class QTypeInfo;

View File

@@ -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 <qdir.h>
#include <qfile.h>
#include <qfileinfo.h>
#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<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

View File

@@ -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 <QIODevice>
#ifndef PROEVALUATOR_FULL
# include <QHash>
# include <QString>
# ifdef PROEVALUATOR_THREAD_SAFE
# include <qmutex.h>
# 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<QString, QString> m_files;
QString m_magicMissing;
QString m_magicExisting;
#endif
};
QT_END_NAMESPACE
#endif // QMAKEVFS_H

View File

@@ -27,6 +27,7 @@
** **
****************************************************************************/ ****************************************************************************/
#include <qmakevfs.h>
#include <qmakeparser.h> #include <qmakeparser.h>
#include <prowriter.h> #include <prowriter.h>
@@ -449,7 +450,8 @@ void tst_ProFileWriter::adds()
QStringList lines = input.isEmpty() ? QStringList() : input.split(QLatin1String("\n")); QStringList lines = input.isEmpty() ? QStringList() : input.split(QLatin1String("\n"));
QString var = QLatin1String("SOURCES"); 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); ProFile *proFile = parser.parsedProBlock(input, QLatin1String(BASE_DIR "/test.pro"), 1);
QVERIFY(proFile); QVERIFY(proFile);
PW::putVarValues(proFile, &lines, values, var, PW::PutFlags(flags), scope); 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 lines = input.split(QLatin1String("\n"));
QStringList vars; vars << QLatin1String("SOURCES"); 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); ProFile *proFile = parser.parsedProBlock(input, QLatin1String(BASE_DIR "/test.pro"), 1);
QVERIFY(proFile); QVERIFY(proFile);
Qt4ProjectManager::Internal::ProWriter::removeVarValues(proFile, &lines, values, vars); Qt4ProjectManager::Internal::ProWriter::removeVarValues(proFile, &lines, values, vars);
@@ -647,7 +650,8 @@ void tst_ProFileWriter::multiVar()
<< QString::fromLatin1(BASE_DIR "/bak"); << QString::fromLatin1(BASE_DIR "/bak");
QStringList vars; vars << QLatin1String("SOURCES") << QLatin1String("HEADERS"); 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); ProFile *proFile = parser.parsedProBlock(input, QLatin1String(BASE_DIR "/test.pro"), 1);
QVERIFY(proFile); QVERIFY(proFile);
Qt4ProjectManager::Internal::ProWriter::removeFiles(proFile, &lines, baseDir, files, vars); Qt4ProjectManager::Internal::ProWriter::removeFiles(proFile, &lines, baseDir, files, vars);
@@ -667,7 +671,8 @@ void tst_ProFileWriter::addFiles()
" sub/bar.cpp" " 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); ProFile *proFile = parser.parsedProBlock(input, QLatin1String(BASE_DIR "/test.pro"), 1);
QVERIFY(proFile); QVERIFY(proFile);
Qt4ProjectManager::Internal::ProWriter::addFiles(proFile, &lines, QDir(BASE_DIR), Qt4ProjectManager::Internal::ProWriter::addFiles(proFile, &lines, QDir(BASE_DIR),
@@ -688,7 +693,8 @@ void tst_ProFileWriter::removeFiles()
"SOURCES = foo.cpp" "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); ProFile *proFile = parser.parsedProBlock(input, QLatin1String(BASE_DIR "/test.pro"), 1);
QVERIFY(proFile); QVERIFY(proFile);
Qt4ProjectManager::Internal::ProWriter::removeFiles(proFile, &lines, QDir(BASE_DIR), Qt4ProjectManager::Internal::ProWriter::removeFiles(proFile, &lines, QDir(BASE_DIR),

View File

@@ -28,6 +28,7 @@
****************************************************************************/ ****************************************************************************/
#include "qmakeglobals.h" #include "qmakeglobals.h"
#include "qmakevfs.h"
#include "qmakeparser.h" #include "qmakeparser.h"
#include "qmakeevaluator.h" #include "qmakeevaluator.h"
#include "profileevaluator.h" #include "profileevaluator.h"
@@ -69,14 +70,15 @@ public:
static EvalHandler evalHandler; static EvalHandler evalHandler;
static int evaluate(const QString &fileName, const QString &in_pwd, const QString &out_pwd, 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<QString> visited; static QSet<QString> visited;
if (visited.contains(fileName)) if (visited.contains(fileName))
return 0; return 0;
visited.insert(fileName); visited.insert(fileName);
ProFileEvaluator visitor(option, parser, &evalHandler); ProFileEvaluator visitor(option, parser, vfs, &evalHandler);
#ifdef PROEVALUATOR_CUMULATIVE #ifdef PROEVALUATOR_CUMULATIVE
visitor.setCumulative(cumulative); visitor.setCumulative(cumulative);
#endif #endif
@@ -130,7 +132,7 @@ static int evaluate(const QString &fileName, const QString &in_pwd, const QStrin
fflush(stdout); fflush(stdout);
nlevel++; 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; out_pwd = in_pwd;
option.setDirectories(in_pwd, out_pwd); option.setDirectories(in_pwd, out_pwd);
QMakeParser parser(0, &evalHandler); QMakeVfs vfs;
return evaluate(file, in_pwd, out_pwd, cumulative, &option, &parser, level); QMakeParser parser(0, &vfs, &evalHandler);
return evaluate(file, in_pwd, out_pwd, cumulative, &option, &parser, &vfs, level);
} }

View File

@@ -15,8 +15,27 @@ build_all:!build_pass {
CONFIG += release CONFIG += release
} }
SOURCES = main.cpp qmakeglobals.cpp qmakeparser.cpp qmakeevaluator.cpp profileevaluator.cpp qmakebuiltins.cpp proitems.cpp ioutils.cpp SOURCES += \
HEADERS = qmakeglobals.h qmakeparser.h profileevaluator.h qmakeevaluator.h qmakeevaluator_p.h proitems.h ioutils.h 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 RESOURCES += proparser.qrc
DEFINES += QMAKE_BUILTIN_PRFS DEFINES += QMAKE_BUILTIN_PRFS