From e5d909d6d68055c057bbaeadb8f7a4078e6d54e8 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Mon, 24 Oct 2016 19:30:24 +0200 Subject: qmake: make VFS aware of exact vs. cumulative evaluation sync-up with qt-creator; no effect on qmake. comment on cherry-pick: this is actually a lot more than a cherry-pick, because the dual VFS needs to deal with the file ids which were concurrently introduced on the qmake side. Change-Id: I2c1eb16c97526fa275a1c6a2eae9266d385859ac (cherry picked from qtcreator/424639ecac9d2e404d2bfaff7f46b45ed98664b8) (cherry picked from qtcreator/a8010b0fff47d903d4a1f80e3adb1a2ef41beb33) Reviewed-by: Joerg Bornemann --- qmake/library/qmakebuiltins.cpp | 25 +++++--- qmake/library/qmakeevaluator.cpp | 16 ++--- qmake/library/qmakeevaluator.h | 3 +- qmake/library/qmakeparser.cpp | 79 +++++++++++------------ qmake/library/qmakeparser.h | 29 +++++---- qmake/library/qmakevfs.cpp | 105 ++++++++++++++++++++++++++----- qmake/library/qmakevfs.h | 68 +++++++++++++++++--- qmake/project.cpp | 2 +- tests/auto/tools/qmakelib/evaltest.cpp | 4 +- tests/auto/tools/qmakelib/parsertest.cpp | 2 +- 10 files changed, 231 insertions(+), 102 deletions(-) diff --git a/qmake/library/qmakebuiltins.cpp b/qmake/library/qmakebuiltins.cpp index 0a83b9b930..c1d446263e 100644 --- a/qmake/library/qmakebuiltins.cpp +++ b/qmake/library/qmakebuiltins.cpp @@ -443,15 +443,18 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::parseJsonInto(const QByteArray &json QMakeEvaluator::VisitReturn QMakeEvaluator::writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode, - bool exe, const QString &contents) + QMakeVfs::VfsFlags flags, const QString &contents) { + int oldId = m_vfs->idForFileName(fn, flags | QMakeVfs::VfsAccessedOnly); + int id = m_vfs->idForFileName(fn, flags | QMakeVfs::VfsCreate); QString errStr; - if (!m_vfs->writeFile(fn, mode, exe, contents, &errStr)) { + if (!m_vfs->writeFile(id, mode, flags, contents, &errStr)) { evalError(fL1S("Cannot write %1file %2: %3") .arg(ctx, QDir::toNativeSeparators(fn), errStr)); return ReturnFalse; } - m_parser->discardFileFromCache(fn); + if (oldId) + m_parser->discardFileFromCache(oldId); return ReturnTrue; } @@ -1343,7 +1346,8 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ReturnFalse; } QString fn = resolvePath(args.at(0).toQString(m_tmp1)); - int pro = m_parser->idForFileName(fn); + QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact); + int pro = m_vfs->idForFileName(fn, flags | QMakeVfs::VfsAccessedOnly); if (!pro) return ReturnFalse; ProValueMap &vmap = m_valuemapStack.first(); @@ -1416,7 +1420,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( VisitReturn ret = ReturnFalse; QString contents = args.join(statics.field_sep); ProFile *pro = m_parser->parsedProBlock(QStringRef(&contents), - m_current.pro->fileName(), m_current.line); + 0, m_current.pro->fileName(), m_current.line); if (m_cumulative || pro->isOk()) { m_locationStack.push(m_current); visitProBlock(pro, pro->tokPtr()); @@ -1784,7 +1788,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( return ReturnFalse; } QIODevice::OpenMode mode = QIODevice::Truncate; - bool exe = false; + QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact); QString contents; if (args.count() >= 2) { const ProStringList &vals = values(args.at(1).toKey()); @@ -1796,7 +1800,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( if (opt == QLatin1String("append")) { mode = QIODevice::Append; } else if (opt == QLatin1String("exe")) { - exe = true; + flags |= QMakeVfs::VfsExecutable; } else { evalError(fL1S("write_file(): invalid flag %1.").arg(opt.toQString(m_tmp3))); return ReturnFalse; @@ -1806,7 +1810,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( } QString path = resolvePath(args.at(0).toQString(m_tmp1)); path.detach(); // make sure to not leak m_tmp1 into the map of written files. - return writeFile(QString(), path, mode, exe, contents); + return writeFile(QString(), path, mode, flags, contents); } case T_TOUCH: { if (args.count() != 2) { @@ -1960,6 +1964,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( varstr += QLatin1Char('\n'); } QString fn; + QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact); if (target == TargetSuper) { if (m_superfile.isEmpty()) { m_superfile = QDir::cleanPath(m_outputDir + QLatin1String("/.qmake.super")); @@ -1983,12 +1988,12 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinConditional( fn = m_stashfile; if (fn.isEmpty()) fn = QDir::cleanPath(m_outputDir + QLatin1String("/.qmake.stash")); - if (!m_vfs->exists(fn)) { + if (!m_vfs->exists(fn, flags)) { printf("Info: creating stash file %s\n", qPrintable(QDir::toNativeSeparators(fn))); valuesRef(ProKey("_QMAKE_STASH_")) << ProString(fn); } } - return writeFile(fL1S("cache "), fn, QIODevice::Append, false, varstr); + return writeFile(fL1S("cache "), fn, QIODevice::Append, flags, varstr); } case T_RELOAD_PROPERTIES: #ifdef QT_BUILD_QMAKE diff --git a/qmake/library/qmakeevaluator.cpp b/qmake/library/qmakeevaluator.cpp index 3e613038a3..7112b57c11 100644 --- a/qmake/library/qmakeevaluator.cpp +++ b/qmake/library/qmakeevaluator.cpp @@ -1104,6 +1104,7 @@ void QMakeEvaluator::loadDefaults() bool QMakeEvaluator::prepareProject(const QString &inDir) { + QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact); QString superdir; if (m_option->do_cache) { QString conffile; @@ -1114,7 +1115,7 @@ bool QMakeEvaluator::prepareProject(const QString &inDir) superdir = m_outputDir; forever { QString superfile = superdir + QLatin1String("/.qmake.super"); - if (m_vfs->exists(superfile)) { + if (m_vfs->exists(superfile, flags)) { m_superfile = QDir::cleanPath(superfile); break; } @@ -1129,10 +1130,10 @@ bool QMakeEvaluator::prepareProject(const QString &inDir) QString dir = m_outputDir; forever { conffile = sdir + QLatin1String("/.qmake.conf"); - if (!m_vfs->exists(conffile)) + if (!m_vfs->exists(conffile, flags)) conffile.clear(); cachefile = dir + QLatin1String("/.qmake.cache"); - if (!m_vfs->exists(cachefile)) + if (!m_vfs->exists(cachefile, flags)) cachefile.clear(); if (!conffile.isEmpty() || !cachefile.isEmpty()) { if (dir != sdir) @@ -1160,7 +1161,7 @@ bool QMakeEvaluator::prepareProject(const QString &inDir) QString dir = m_outputDir; forever { QString stashfile = dir + QLatin1String("/.qmake.stash"); - if (dir == (!superdir.isEmpty() ? superdir : m_buildRoot) || m_vfs->exists(stashfile)) { + if (dir == (!superdir.isEmpty() ? superdir : m_buildRoot) || m_vfs->exists(stashfile, flags)) { m_stashfile = QDir::cleanPath(stashfile); break; } @@ -1285,7 +1286,8 @@ bool QMakeEvaluator::loadSpec() m_cachefile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) return false; } - if (!m_stashfile.isEmpty() && m_vfs->exists(m_stashfile)) { + QMakeVfs::VfsFlags flags = (m_cumulative ? QMakeVfs::VfsCumulative : QMakeVfs::VfsExact); + if (!m_stashfile.isEmpty() && m_vfs->exists(m_stashfile, flags)) { valuesRef(ProKey("_QMAKE_STASH_")) << ProString(m_stashfile); if (evaluateFile( m_stashfile, QMakeHandler::EvalConfigFile, LoadProOnly) != ReturnTrue) @@ -1308,7 +1310,7 @@ void QMakeEvaluator::setupProject() void QMakeEvaluator::evaluateCommand(const QString &cmds, const QString &where) { if (!cmds.isEmpty()) { - ProFile *pro = m_parser->parsedProBlock(QStringRef(&cmds), where, -1); + ProFile *pro = m_parser->parsedProBlock(QStringRef(&cmds), 0, where, -1); if (pro->isOk()) { m_locationStack.push(m_current); visitProBlock(pro, pro->tokPtr()); @@ -1812,7 +1814,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateConditional( const QStringRef &cond, const QString &where, int line) { VisitReturn ret = ReturnFalse; - ProFile *pro = m_parser->parsedProBlock(cond, where, line, QMakeParser::TestGrammar); + ProFile *pro = m_parser->parsedProBlock(cond, 0, where, line, QMakeParser::TestGrammar); if (pro->isOk()) { m_locationStack.push(m_current); ret = visitProBlock(pro, pro->tokPtr()); diff --git a/qmake/library/qmakeevaluator.h b/qmake/library/qmakeevaluator.h index 7318664d46..1f0255e55a 100644 --- a/qmake/library/qmakeevaluator.h +++ b/qmake/library/qmakeevaluator.h @@ -34,6 +34,7 @@ #endif #include "qmakeparser.h" +#include "qmakevfs.h" #include "ioutils.h" #include @@ -237,7 +238,7 @@ public: VisitReturn parseJsonInto(const QByteArray &json, const QString &into, ProValueMap *value); VisitReturn writeFile(const QString &ctx, const QString &fn, QIODevice::OpenMode mode, - bool exe, const QString &contents); + QMakeVfs::VfsFlags flags, const QString &contents); #if QT_CONFIG(process) void runProcess(QProcess *proc, const QString &command) const; #endif diff --git a/qmake/library/qmakeparser.cpp b/qmake/library/qmakeparser.cpp index 62e8875c5e..37608c7a15 100644 --- a/qmake/library/qmakeparser.cpp +++ b/qmake/library/qmakeparser.cpp @@ -52,12 +52,22 @@ ProFileCache::~ProFileCache() ent.pro->deref(); } -void ProFileCache::discardFile(const QString &fileName) +void ProFileCache::discardFile(const QString &fileName, QMakeVfs *vfs) +{ + int eid = vfs->idForFileName(fileName, QMakeVfs::VfsExact | QMakeVfs::VfsAccessedOnly); + if (eid) + discardFile(eid); + int cid = vfs->idForFileName(fileName, QMakeVfs::VfsCumulative | QMakeVfs::VfsAccessedOnly); + if (cid && cid != eid) + discardFile(cid); +} + +void ProFileCache::discardFile(int id) { #ifdef PROPARSER_THREAD_SAFE QMutexLocker lck(&mutex); #endif - QHash::Iterator it = parsed_files.find(fileName); + auto it = parsed_files.find(id); if (it != parsed_files.end()) { #ifdef PROPARSER_THREAD_SAFE if (it->locker) { @@ -77,16 +87,16 @@ void ProFileCache::discardFile(const QString &fileName) } } -void ProFileCache::discardFiles(const QString &prefix) +void ProFileCache::discardFiles(const QString &prefix, QMakeVfs *vfs) { #ifdef PROPARSER_THREAD_SAFE QMutexLocker lck(&mutex); #endif - QHash::Iterator - it = parsed_files.begin(), - end = parsed_files.end(); - while (it != end) - if (it.key().startsWith(prefix)) { + auto it = parsed_files.begin(), end = parsed_files.end(); + while (it != end) { + // Note: this is empty for virtual files from other VFSes. + QString fn = vfs->fileNameForId(it.key()); + if (fn.startsWith(prefix)) { #ifdef PROPARSER_THREAD_SAFE if (it->locker) { if (!it->locker->done) { @@ -105,6 +115,7 @@ void ProFileCache::discardFiles(const QString &prefix) } else { ++it; } + } } ////////// Parser /////////// @@ -167,12 +178,15 @@ QMakeParser::QMakeParser(ProFileCache *cache, QMakeVfs *vfs, QMakeParserHandler ProFile *QMakeParser::parsedProFile(const QString &fileName, ParseFlags flags) { ProFile *pro; + QMakeVfs::VfsFlags vfsFlags = ((flags & ParseCumulative) ? QMakeVfs::VfsCumulative + : QMakeVfs::VfsExact); + int id = m_vfs->idForFileName(fileName, vfsFlags); if ((flags & ParseUseCache) && m_cache) { ProFileCache::Entry *ent; #ifdef PROPARSER_THREAD_SAFE QMutexLocker locker(&m_cache->mutex); #endif - QHash::Iterator it = m_cache->parsed_files.find(fileName); + auto it = m_cache->parsed_files.find(id); if (it != m_cache->parsed_files.end()) { ent = &*it; #ifdef PROPARSER_THREAD_SAFE @@ -190,18 +204,18 @@ ProFile *QMakeParser::parsedProFile(const QString &fileName, ParseFlags flags) if ((pro = ent->pro)) pro->ref(); } else { - ent = &m_cache->parsed_files[fileName]; + ent = &m_cache->parsed_files[id]; #ifdef PROPARSER_THREAD_SAFE ent->locker = new ProFileCache::Entry::Locker; locker.unlock(); #endif - pro = new ProFile(idForFileName(fileName), fileName); - if (!read(pro, flags)) { - delete pro; - pro = 0; - } else { + QString contents; + if (readFile(id, flags, &contents)) { + pro = parsedProBlock(QStringRef(&contents), id, fileName, 1, FullGrammar); pro->itemsRef()->squeeze(); pro->ref(); + } else { + pro = 0; } ent->pro = pro; #ifdef PROPARSER_THREAD_SAFE @@ -216,52 +230,39 @@ ProFile *QMakeParser::parsedProFile(const QString &fileName, ParseFlags flags) #endif } } else { - pro = new ProFile(idForFileName(fileName), fileName); - if (!read(pro, flags)) { - delete pro; + QString contents; + if (readFile(id, flags, &contents)) + pro = parsedProBlock(QStringRef(&contents), id, fileName, 1, FullGrammar); + else pro = 0; - } } return pro; } ProFile *QMakeParser::parsedProBlock( - const QStringRef &contents, const QString &name, int line, SubGrammar grammar) + const QStringRef &contents, int id, const QString &name, int line, SubGrammar grammar) { - ProFile *pro = new ProFile(0, name); + ProFile *pro = new ProFile(id, name); read(pro, contents, line, grammar); return pro; } -int QMakeParser::idForFileName(const QString &fileName) -{ -#ifdef PROPARSER_THREAD_SAFE - QMutexLocker lck(&fileIdMutex); -#endif - int &place = fileIdMap[fileName]; - if (!place) - place = ++fileIdCounter; - return place; -} - -void QMakeParser::discardFileFromCache(const QString &fileName) +void QMakeParser::discardFileFromCache(int id) { if (m_cache) - m_cache->discardFile(fileName); + m_cache->discardFile(id); } -bool QMakeParser::read(ProFile *pro, ParseFlags flags) +bool QMakeParser::readFile(int id, ParseFlags flags, QString *contents) { - QString content; QString errStr; - QMakeVfs::ReadResult result = m_vfs->readFile(pro->fileName(), &content, &errStr); + QMakeVfs::ReadResult result = m_vfs->readFile(id, contents, &errStr); if (result != QMakeVfs::ReadOk) { if (m_handler && ((flags & ParseReportMissing) || result != QMakeVfs::ReadNotFound)) m_handler->message(QMakeParserHandler::ParserIoError, - fL1S("Cannot read %1: %2").arg(pro->fileName(), errStr)); + fL1S("Cannot read %1: %2").arg(m_vfs->fileNameForId(id), errStr)); return false; } - read(pro, QStringRef(&content), 1, FullGrammar); return true; } diff --git a/qmake/library/qmakeparser.h b/qmake/library/qmakeparser.h index a29e9c227d..3ae30dcf74 100644 --- a/qmake/library/qmakeparser.h +++ b/qmake/library/qmakeparser.h @@ -30,6 +30,7 @@ #define QMAKEPARSER_H #include "qmake_global.h" +#include "qmakevfs.h" #include "proitems.h" #include @@ -78,7 +79,12 @@ public: enum ParseFlag { ParseDefault = 0, ParseUseCache = 1, - ParseReportMissing = 4 + ParseReportMissing = 4, +#ifdef PROEVALUATOR_DUAL_VFS + ParseCumulative = 8 +#else + ParseCumulative = 0 +#endif }; Q_DECLARE_FLAGS(ParseFlags, ParseFlag) @@ -87,12 +93,10 @@ public: enum SubGrammar { FullGrammar, TestGrammar, ValueGrammar }; // fileName is expected to be absolute and cleanPath()ed. ProFile *parsedProFile(const QString &fileName, ParseFlags flags = ParseDefault); - ProFile *parsedProBlock(const QStringRef &contents, const QString &name, int line = 0, + ProFile *parsedProBlock(const QStringRef &contents, int id, const QString &name, int line = 0, SubGrammar grammar = FullGrammar); - int idForFileName(const QString &fileName); - - void discardFileFromCache(const QString &fileName); + void discardFileFromCache(int id); #ifdef PROPARSER_DEBUG static QString formatProBlock(const QString &block); @@ -131,7 +135,7 @@ private: ushort terminator; // '}' if replace function call is braced, ':' if test function }; - bool read(ProFile *pro, ParseFlags flags); + bool readFile(int id, QMakeParser::ParseFlags flags, QString *contents); void read(ProFile *pro, const QStringRef &content, int line, SubGrammar grammar); ALWAYS_INLINE void putTok(ushort *&tokPtr, ushort tok); @@ -182,12 +186,6 @@ private: QString m_tmp; // Temporary for efficient toQString - QHash fileIdMap; -#ifdef PROEVALUATOR_THREAD_SAFE - QMutex fileIdMutex; -#endif - int fileIdCounter = 0; - ProFileCache *m_cache; QMakeParserHandler *m_handler; QMakeVfs *m_vfs; @@ -206,8 +204,9 @@ public: ProFileCache() {} ~ProFileCache(); - void discardFile(const QString &fileName); - void discardFiles(const QString &prefix); + void discardFile(int id); + void discardFile(const QString &fileName, QMakeVfs *vfs); + void discardFiles(const QString &prefix, QMakeVfs *vfs); private: struct Entry { @@ -223,7 +222,7 @@ private: #endif }; - QHash parsed_files; + QHash parsed_files; #ifdef PROPARSER_THREAD_SAFE QMutex mutex; #endif diff --git a/qmake/library/qmakevfs.cpp b/qmake/library/qmakevfs.cpp index c40f49c4ad..16e21395ba 100644 --- a/qmake/library/qmakevfs.cpp +++ b/qmake/library/qmakevfs.cpp @@ -47,32 +47,102 @@ QMakeVfs::QMakeVfs() { } -bool QMakeVfs::writeFile(const QString &fn, QIODevice::OpenMode mode, bool exe, +#ifdef PROPARSER_THREAD_SAFE +QMutex QMakeVfs::s_mutex; +#endif +QAtomicInt QMakeVfs::s_fileIdCounter; +QHash QMakeVfs::s_fileIdMap; +QHash QMakeVfs::s_idFileMap; + +int QMakeVfs::idForFileName(const QString &fn, VfsFlags flags) +{ +#ifdef PROEVALUATOR_DUAL_VFS + { +# ifdef PROPARSER_THREAD_SAFE + QMutexLocker locker(&m_vmutex); +# endif + int idx = (flags & VfsCumulative) ? 1 : 0; + if (flags & VfsCreate) { + int &id = m_virtualFileIdMap[idx][fn]; + if (!id) { + id = ++s_fileIdCounter; + m_virtualIdFileMap[id] = fn; + } + return id; + } + int id = m_virtualFileIdMap[idx].value(fn); + if (id || (flags & VfsCreatedOnly)) + return id; + } +#endif + if (!(flags & VfsAccessedOnly)) { +#ifdef PROPARSER_THREAD_SAFE + QMutexLocker locker(&s_mutex); +#endif + int &id = s_fileIdMap[fn]; + if (!id) { + id = ++s_fileIdCounter; + s_idFileMap[id] = fn; + } + return id; + } + return s_fileIdMap.value(fn); +} + +QString QMakeVfs::fileNameForId(int id) +{ +#ifdef PROEVALUATOR_DUAL_VFS + { +# ifdef PROPARSER_THREAD_SAFE + QMutexLocker locker(&m_vmutex); +# endif + const QString &fn = m_virtualIdFileMap.value(id); + if (!fn.isEmpty()) + return fn; + } +#endif +#ifdef PROPARSER_THREAD_SAFE + QMutexLocker locker(&s_mutex); +#endif + return s_idFileMap.value(id); +} + +void QMakeVfs::clearIds() +{ +#ifdef PROEVALUATOR_THREAD_SAFE + QMutexLocker locker(&s_mutex); +#endif + s_fileIdCounter = 0; + s_fileIdMap.clear(); + s_idFileMap.clear(); +} + +bool QMakeVfs::writeFile(int id, QIODevice::OpenMode mode, VfsFlags flags, const QString &contents, QString *errStr) { #ifndef PROEVALUATOR_FULL # ifdef PROEVALUATOR_THREAD_SAFE QMutexLocker locker(&m_mutex); # endif - QString *cont = &m_files[fn]; + QString *cont = &m_files[id]; + Q_UNUSED(flags) if (mode & QIODevice::Append) *cont += contents; else *cont = contents; Q_UNUSED(errStr) - Q_UNUSED(exe) return true; #else - QFileInfo qfi(fn); + QFileInfo qfi(fileNameForId(id)); if (!QDir::current().mkpath(qfi.path())) { *errStr = fL1S("Cannot create parent directory"); return false; } QByteArray bytes = contents.toLocal8Bit(); - QFile cfile(fn); + QFile cfile(qfi.filePath()); if (!(mode & QIODevice::Append) && cfile.open(QIODevice::ReadOnly | QIODevice::Text)) { if (cfile.readAll() == bytes) { - if (exe) { + if (flags & VfsExecutable) { cfile.setPermissions(cfile.permissions() | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther); } else { @@ -93,20 +163,20 @@ bool QMakeVfs::writeFile(const QString &fn, QIODevice::OpenMode mode, bool exe, *errStr = cfile.errorString(); return false; } - if (exe) + if (flags & VfsExecutable) cfile.setPermissions(cfile.permissions() | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther); return true; #endif } -QMakeVfs::ReadResult QMakeVfs::readFile(const QString &fn, QString *contents, QString *errStr) +QMakeVfs::ReadResult QMakeVfs::readFile(int id, QString *contents, QString *errStr) { #ifndef PROEVALUATOR_FULL # ifdef PROEVALUATOR_THREAD_SAFE QMutexLocker locker(&m_mutex); # endif - QHash::ConstIterator it = m_files.constFind(fn); + auto it = m_files.constFind(id); if (it != m_files.constEnd()) { if (it->constData() == m_magicMissing.constData()) { *errStr = fL1S("No such file or directory"); @@ -119,11 +189,11 @@ QMakeVfs::ReadResult QMakeVfs::readFile(const QString &fn, QString *contents, QS } #endif - QFile file(fn); + QFile file(fileNameForId(id)); if (!file.open(QIODevice::ReadOnly)) { if (!file.exists()) { #ifndef PROEVALUATOR_FULL - m_files[fn] = m_magicMissing; + m_files[id] = m_magicMissing; #endif *errStr = fL1S("No such file or directory"); return ReadNotFound; @@ -132,7 +202,7 @@ QMakeVfs::ReadResult QMakeVfs::readFile(const QString &fn, QString *contents, QS return ReadOtherError; } #ifndef PROEVALUATOR_FULL - m_files[fn] = m_magicExisting; + m_files[id] = m_magicExisting; #endif QByteArray bcont = file.readAll(); @@ -145,19 +215,22 @@ QMakeVfs::ReadResult QMakeVfs::readFile(const QString &fn, QString *contents, QS return ReadOk; } -bool QMakeVfs::exists(const QString &fn) +bool QMakeVfs::exists(const QString &fn, VfsFlags flags) { #ifndef PROEVALUATOR_FULL # ifdef PROEVALUATOR_THREAD_SAFE QMutexLocker locker(&m_mutex); # endif - QHash::ConstIterator it = m_files.constFind(fn); + int id = idForFileName(fn, flags); + auto it = m_files.constFind(id); if (it != m_files.constEnd()) return it->constData() != m_magicMissing.constData(); +#else + Q_UNUSED(flags) #endif bool ex = IoUtils::fileType(fn) == IoUtils::FileIsRegular; #ifndef PROEVALUATOR_FULL - m_files[fn] = ex ? m_magicExisting : m_magicMissing; + m_files[id] = ex ? m_magicExisting : m_magicMissing; #endif return ex; } @@ -169,7 +242,7 @@ void QMakeVfs::invalidateCache() # ifdef PROEVALUATOR_THREAD_SAFE QMutexLocker locker(&m_mutex); # endif - QHash::Iterator it = m_files.begin(), eit = m_files.end(); + auto it = m_files.begin(), eit = m_files.end(); while (it != eit) { if (it->constData() == m_magicMissing.constData() ||it->constData() == m_magicExisting.constData()) diff --git a/qmake/library/qmakevfs.h b/qmake/library/qmakevfs.h index 5e6a2f88e2..3b69b60bee 100644 --- a/qmake/library/qmakevfs.h +++ b/qmake/library/qmakevfs.h @@ -31,12 +31,16 @@ #include "qmake_global.h" -# include -#ifndef PROEVALUATOR_FULL -# include -# include -# ifdef PROEVALUATOR_THREAD_SAFE -# include +#include +#include +#include +#ifdef PROEVALUATOR_THREAD_SAFE +# include +#endif + +#ifdef PROEVALUATOR_DUAL_VFS +# ifndef PROEVALUATOR_CUMULATIVE +# error PROEVALUATOR_DUAL_VFS requires PROEVALUATOR_CUMULATIVE # endif #endif @@ -51,11 +55,30 @@ public: ReadOtherError }; + enum VfsFlag { + VfsExecutable = 1, + VfsExact = 0, +#ifdef PROEVALUATOR_DUAL_VFS + VfsCumulative = 2, + VfsCreate = 4, + VfsCreatedOnly = 8, +#else + VfsCumulative = 0, + VfsCreate = 0, + VfsCreatedOnly = 0, +#endif + VfsAccessedOnly = 16 + }; + Q_DECLARE_FLAGS(VfsFlags, VfsFlag) + QMakeVfs(); - bool writeFile(const QString &fn, QIODevice::OpenMode mode, bool exe, const QString &contents, QString *errStr); - ReadResult readFile(const QString &fn, QString *contents, QString *errStr); - bool exists(const QString &fn); + int idForFileName(const QString &fn, VfsFlags flags); + QString fileNameForId(int id); + static void clearIds(); + bool writeFile(int id, QIODevice::OpenMode mode, VfsFlags flags, const QString &contents, QString *errStr); + ReadResult readFile(int id, QString *contents, QString *errStr); + bool exists(const QString &fn, QMakeVfs::VfsFlags flags); #ifndef PROEVALUATOR_FULL void invalidateCache(); @@ -63,16 +86,41 @@ public: #endif private: +#ifdef PROEVALUATOR_THREAD_SAFE + static QMutex s_mutex; +#endif + static QAtomicInt s_fileIdCounter; + // Qt Creator's ProFile cache is a singleton to maximize its cross-project + // effectiveness (shared prf files from QtVersions). + // For this to actually work, real files need a global mapping. + // This is fine, because the namespace of real files is indeed global. + static QHash s_fileIdMap; + static QHash s_idFileMap; +#ifdef PROEVALUATOR_DUAL_VFS +# ifdef PROEVALUATOR_THREAD_SAFE + // The simple way to avoid recursing m_mutex. + QMutex m_vmutex; +# endif + // Virtual files are bound to the project context they were created in, + // so their ids need to be local as well. + // We violate that rule in lupdate (which has a non-dual VFS), but that + // does not matter, because it has only one project context anyway. + QHash m_virtualFileIdMap[2]; // Exact and cumulative + QHash m_virtualIdFileMap; // Only one map, as ids are unique across realms. +#endif + #ifndef PROEVALUATOR_FULL # ifdef PROEVALUATOR_THREAD_SAFE QMutex m_mutex; # endif - QHash m_files; + QHash m_files; QString m_magicMissing; QString m_magicExisting; #endif }; +Q_DECLARE_OPERATORS_FOR_FLAGS(QMakeVfs::VfsFlags) + QT_END_NAMESPACE #endif // QMAKEVFS_H diff --git a/qmake/project.cpp b/qmake/project.cpp index e8509ad096..e6bdb04bfb 100644 --- a/qmake/project.cpp +++ b/qmake/project.cpp @@ -123,7 +123,7 @@ QStringList QMakeProject::expand(const ProKey &func, const QList ProString QMakeProject::expand(const QString &expr, const QString &where, int line) { ProString ret; - ProFile *pro = m_parser->parsedProBlock(QStringRef(&expr), where, line, + ProFile *pro = m_parser->parsedProBlock(QStringRef(&expr), 0, where, line, QMakeParser::ValueGrammar); if (pro->isOk()) { m_current.pro = pro; diff --git a/tests/auto/tools/qmakelib/evaltest.cpp b/tests/auto/tools/qmakelib/evaltest.cpp index 3fdd30b685..aeedf4ae32 100644 --- a/tests/auto/tools/qmakelib/evaltest.cpp +++ b/tests/auto/tools/qmakelib/evaltest.cpp @@ -2869,12 +2869,12 @@ void tst_qmakelib::proEval() globals.environment = m_env; globals.setProperties(m_prop); globals.setDirectories(m_indir, m_outdir); - ProFile *outPro = parser.parsedProBlock(QStringRef(&out), "out", 1, QMakeParser::FullGrammar); + ProFile *outPro = parser.parsedProBlock(QStringRef(&out), 0, "out", 1, QMakeParser::FullGrammar); if (!outPro->isOk()) { qWarning("Expected output is malformed"); verified = false; } - ProFile *pro = parser.parsedProBlock(QStringRef(&in), infile, 1, QMakeParser::FullGrammar); + ProFile *pro = parser.parsedProBlock(QStringRef(&in), 0, infile, 1, QMakeParser::FullGrammar); QMakeEvaluator visitor(&globals, &parser, &vfs, &handler); visitor.setOutputDir(m_outdir); #ifdef Q_OS_WIN diff --git a/tests/auto/tools/qmakelib/parsertest.cpp b/tests/auto/tools/qmakelib/parsertest.cpp index 70f1be5fc3..f736bf38bf 100644 --- a/tests/auto/tools/qmakelib/parsertest.cpp +++ b/tests/auto/tools/qmakelib/parsertest.cpp @@ -2031,7 +2031,7 @@ void tst_qmakelib::proParser() handler.setExpectedMessages(msgs.split('\n', QString::SkipEmptyParts)); QMakeVfs vfs; QMakeParser parser(0, &vfs, &handler); - ProFile *pro = parser.parsedProBlock(QStringRef(&in), "in", 1, QMakeParser::FullGrammar); + ProFile *pro = parser.parsedProBlock(QStringRef(&in), 0, "in", 1, QMakeParser::FullGrammar); if (handler.printedMessages()) { qWarning("Got unexpected message(s)"); verified = false; -- cgit v1.2.3