FileInProjectFinder: Search DeploymentData for paths

The deployment data is the most reliable information we can get
regarding where a file from the host ended up on the target. Therefore
it is searched first. Since the "directories" found this way may not
actually exist on the host, we return the list of entries rather than
the actual path for those. Files from different host directories can be
deployed to the same target directory, after all. Using the list of
entries, a client can reconstruct children of the directory by appending
an entry to the base path and searching again.

Change-Id: Ia0f72872a4ff6313f1ae8b0f441b67f55be5a456
Reviewed-by: Vikas Pachdha <vikas.pachdha@qt.io>
This commit is contained in:
Ulf Hermann
2018-08-20 18:04:35 +02:00
parent 6fba0be280
commit 7b0b4c92cd
3 changed files with 127 additions and 60 deletions

View File

@@ -39,11 +39,18 @@ enum { debug = false };
namespace Utils {
static bool checkPath(const QString &candidate, FileInProjectFinder::FindMode findMode)
static bool checkPath(const QString &candidate, FileInProjectFinder::FileHandler fileHandler,
FileInProjectFinder::DirectoryHandler directoryHandler)
{
const QFileInfo candidateInfo(candidate);
return (candidateInfo.isFile() && (findMode & FileInProjectFinder::FindFile))
|| (candidateInfo.isDir() && (findMode & FileInProjectFinder::FindDirectory));
if (fileHandler && candidateInfo.isFile()) {
fileHandler(candidate);
return true;
} else if (directoryHandler && candidateInfo.isDir()) {
directoryHandler(QDir(candidate).entryList());
return true;
}
return false;
}
/*!
@@ -115,6 +122,20 @@ void FileInProjectFinder::setSysroot(const QString &sysroot)
m_cache.clear();
}
void FileInProjectFinder::addMappedPath(const QString &localFilePath, const QString &remoteFilePath)
{
const QStringList segments = remoteFilePath.split('/', QString::SkipEmptyParts);
PathMappingNode *node = &m_pathMapRoot;
for (const QString &segment : segments) {
auto it = node->children.find(segment);
if (it == node->children.end())
it = node->children.insert(segment, new PathMappingNode);
node = *it;
}
node->localPath = localFilePath;
}
/*!
Returns the best match for the given file URL in the project directory.
@@ -133,30 +154,57 @@ QString FileInProjectFinder::findFile(const QUrl &fileUrl, bool *success) const
if (originalPath.isEmpty()) // e.g. qrc://
originalPath = fileUrl.path();
return findFileOrDirectory(originalPath, FindFile, success);
QString result;
bool found = findFileOrDirectory(originalPath, [&](const QString &fileName) {
result = fileName;
});
if (success)
*success = found;
return result;
}
QString FileInProjectFinder::handleSuccess(const QString &originalPath, const QString &found,
bool *success, const char *where, bool doCache) const
bool FileInProjectFinder::handleSuccess(const QString &originalPath, const QString &found,
const char *where) const
{
if (debug)
qDebug() << "FileInProjectFinder: found" << found << where;
if (doCache)
m_cache.insert(originalPath, found);
if (success)
*success = true;
return found;
m_cache.insert(originalPath, found);
return true;
}
QString FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FindMode findMode,
bool *success) const
bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileHandler fileHandler,
DirectoryHandler directoryHandler) const
{
if (originalPath.isEmpty()) {
if (debug)
qDebug() << "FileInProjectFinder: malformed original path, returning";
if (success)
*success = false;
return originalPath;
return false;
}
const auto segments = originalPath.splitRef('/', QString::SkipEmptyParts);
const PathMappingNode *node = &m_pathMapRoot;
for (const auto &segment : segments) {
auto it = node->children.find(segment.toString());
if (it == node->children.end()) {
node = nullptr;
break;
}
node = *it;
}
if (node) {
if (!node->localPath.isEmpty()) {
if (checkPath(node->localPath, fileHandler, directoryHandler))
return handleSuccess(originalPath, node->localPath, "in deployment data");
} else if (directoryHandler) {
directoryHandler(node->children.keys());
if (debug)
qDebug() << "FileInProjectFinder: found virtual directory" << originalPath
<< "in deployment data";
return true;
}
}
auto it = m_cache.find(originalPath);
@@ -165,10 +213,13 @@ QString FileInProjectFinder::findFileOrDirectory(const QString &originalPath, Fi
qDebug() << "FileInProjectFinder: checking cache ...";
// check if cached path is still there
const QString &candidate = it.value();
if (checkPath(candidate, findMode))
return handleSuccess(originalPath, candidate, success, "in the cache", false);
else
if (checkPath(candidate, fileHandler, directoryHandler)) {
if (debug)
qDebug() << "FileInProjectFinder: found" << candidate << "in the cache";
return true;
} else {
m_cache.erase(it);
}
}
if (!m_projectDir.isEmpty()) {
@@ -188,8 +239,8 @@ QString FileInProjectFinder::findFileOrDirectory(const QString &originalPath, Fi
prefixToIgnore = originalPath.indexOf(appResourcePath) + appResourcePath.length();
}
}
if (prefixToIgnore == -1 && checkPath(originalPath, findMode))
return handleSuccess(originalPath, originalPath, success, "in project directory");
if (prefixToIgnore == -1 && checkPath(originalPath, fileHandler, directoryHandler))
return handleSuccess(originalPath, originalPath, "in project directory");
}
if (debug)
@@ -209,8 +260,8 @@ QString FileInProjectFinder::findFileOrDirectory(const QString &originalPath, Fi
QString candidate = originalPath;
candidate.remove(0, prefixToIgnore);
candidate.prepend(m_projectDir);
if (checkPath(candidate, findMode))
return handleSuccess(originalPath, candidate, success, "in project directory");
if (checkPath(candidate, fileHandler, directoryHandler))
return handleSuccess(originalPath, candidate, "in project directory");
prefixToIgnore = originalPath.indexOf(separator, prefixToIgnore + 1);
}
}
@@ -221,18 +272,17 @@ QString FileInProjectFinder::findFileOrDirectory(const QString &originalPath, Fi
QStringList matches;
const QString lastSegment = FileName::fromString(originalPath).fileName();
if (findMode & FindFile)
if (fileHandler)
matches.append(filesWithSameFileName(lastSegment));
if (findMode & FindDirectory)
if (directoryHandler)
matches.append(pathSegmentsWithSameName(lastSegment));
const QString matchedFilePath = bestMatch(matches, originalPath);
if (!matchedFilePath.isEmpty())
return handleSuccess(originalPath, matchedFilePath, success, "when matching project files");
if (!matchedFilePath.isEmpty() && checkPath(matchedFilePath, fileHandler, directoryHandler))
return handleSuccess(originalPath, matchedFilePath, "when matching project files");
bool foundinSearchPaths = false;
const QString foundPath = findInSearchPaths(originalPath, findMode, &foundinSearchPaths);
if (foundinSearchPaths)
return handleSuccess(originalPath, foundPath, success, "in search path");
QString foundPath = findInSearchPaths(originalPath, fileHandler, directoryHandler);
if (!foundPath.isEmpty())
return handleSuccess(originalPath, foundPath, "in search path");
if (debug)
qDebug() << "FileInProjectFinder: checking absolute path in sysroot ...";
@@ -240,25 +290,22 @@ QString FileInProjectFinder::findFileOrDirectory(const QString &originalPath, Fi
// check if absolute path is found in sysroot
if (!m_sysroot.isEmpty()) {
const QString sysrootPath = m_sysroot + originalPath;
if (checkPath(sysrootPath, findMode))
return handleSuccess(originalPath, sysrootPath, success, "in sysroot");
if (checkPath(sysrootPath, fileHandler, directoryHandler))
return handleSuccess(originalPath, sysrootPath, "in sysroot");
}
if (success)
*success = false;
if (debug)
qDebug() << "FileInProjectFinder: couldn't find file!";
return originalPath;
return false;
}
QString FileInProjectFinder::findInSearchPaths(const QString &filePath, FindMode findMode,
bool *success) const
QString FileInProjectFinder::findInSearchPaths(const QString &filePath, FileHandler fileHandler,
DirectoryHandler directoryHandler) const
{
*success = false;
for (const QString &dirPath : m_searchDirectories) {
const QString found = findInSearchPath(dirPath, filePath, findMode, success);
if (*success)
const QString found = findInSearchPath(dirPath, filePath, fileHandler, directoryHandler);
if (!found.isEmpty())
return found;
}
return QString();
@@ -274,7 +321,8 @@ static QString chopFirstDir(const QString &dirPath)
}
QString FileInProjectFinder::findInSearchPath(const QString &searchPath, const QString &filePath,
FindMode findMode, bool *success)
FileHandler fileHandler,
DirectoryHandler directoryHandler)
{
if (debug)
qDebug() << "FileInProjectFinder: checking search path" << searchPath;
@@ -285,14 +333,14 @@ QString FileInProjectFinder::findInSearchPath(const QString &searchPath, const Q
const QString candidate = searchPath + QLatin1Char('/') + s;
if (debug)
qDebug() << "FileInProjectFinder: trying" << candidate;
if (checkPath(candidate, findMode)) {
*success = true;
if (checkPath(candidate, fileHandler, directoryHandler))
return candidate;
}
QString next = chopFirstDir(s);
if (next.isEmpty()) {
if ((findMode & FindDirectory) && QFileInfo(searchPath).fileName() == s) {
*success = true;
if (directoryHandler && QFileInfo(searchPath).fileName() == s) {
directoryHandler(QDir(searchPath).entryList());
return searchPath;
}
break;
@@ -300,7 +348,6 @@ QString FileInProjectFinder::findInSearchPath(const QString &searchPath, const Q
s = next;
}
*success = false;
return QString();
}
@@ -371,5 +418,9 @@ void FileInProjectFinder::setAdditionalSearchDirectories(const QStringList &sear
m_searchDirectories = searchDirectories;
}
FileInProjectFinder::PathMappingNode::~PathMappingNode()
{
qDeleteAll(children);
}
} // namespace Utils

View File

@@ -38,11 +38,9 @@ namespace Utils {
class QTCREATOR_UTILS_EXPORT FileInProjectFinder
{
public:
enum FindMode {
FindFile = 0x1,
FindDirectory = 0x2,
FindEither = FindFile | FindDirectory
};
using FileHandler = std::function<void(const QString &)>;
using DirectoryHandler = std::function<void(const QStringList &)>;
FileInProjectFinder();
@@ -52,22 +50,31 @@ public:
void setProjectFiles(const FileNameList &projectFiles);
void setSysroot(const QString &sysroot);
void addMappedPath(const QString &localFilePath, const QString &remoteFilePath);
QString findFile(const QUrl &fileUrl, bool *success = nullptr) const;
QString findFileOrDirectory(const QString &originalPath, FindMode findMode,
bool *success) const;
bool findFileOrDirectory(const QString &originalPath, FileHandler fileHandler = nullptr,
DirectoryHandler directoryHandler = nullptr) const;
QStringList searchDirectories() const;
void setAdditionalSearchDirectories(const QStringList &searchDirectories);
private:
QString findInSearchPaths(const QString &filePath, FindMode findMode, bool *success) const;
struct PathMappingNode
{
~PathMappingNode();
QString localPath;
QHash<QString, PathMappingNode *> children;
};
QString findInSearchPaths(const QString &filePath, FileHandler fileHandler,
DirectoryHandler directoryHandler) const;
static QString findInSearchPath(const QString &searchPath, const QString &filePath,
FindMode findMode, bool *success);
FileHandler fileHandler, DirectoryHandler directoryHandler);
QStringList filesWithSameFileName(const QString &fileName) const;
QStringList pathSegmentsWithSameName(const QString &path) const;
QString handleSuccess(const QString &originalPath, const QString &found, bool *success,
const char *where, bool doCache = true) const;
bool handleSuccess(const QString &originalPath, const QString &found, const char *where) const;
static int rankFilePath(const QString &candidatePath, const QString &filePathToFind);
static QString bestMatch(const QStringList &filePaths, const QString &filePathToFind);
@@ -76,6 +83,8 @@ private:
QString m_sysroot;
FileNameList m_projectFiles;
QStringList m_searchDirectories;
PathMappingNode m_pathMapRoot;
mutable QHash<QString,QString> m_cache;
};

View File

@@ -33,6 +33,8 @@
#include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <proparser/qmakevfs.h>
#include <projectexplorer/deployablefile.h>
#include <projectexplorer/deploymentdata.h>
#include <projectexplorer/toolchainmanager.h>
#include <projectexplorer/toolchain.h>
#include <projectexplorer/projectexplorer.h>
@@ -1358,6 +1360,11 @@ void BaseQtVersion::populateQmlFileFinder(FileInProjectFinder *finder, const Tar
QStringList additionalSearchDirectories = qtVersion
? QStringList(qtVersion->qmlPath().toString()) : QStringList();
if (target) {
for (const ProjectExplorer::DeployableFile &file : target->deploymentData().allFiles())
finder->addMappedPath(file.localFilePath().toString(), file.remoteFilePath());
}
// Finally, do populate m_projectFinder
finder->setProjectDirectory(projectDirectory);
finder->setProjectFiles(sourceFiles);