Commit 1e86df71 authored by Kai Koehne's avatar Kai Koehne
Browse files

Utils: New class to map installed file path to source path

FileInProjectFinder implements an heuristic to find the likely source
file for an aribrary file path, e.g. in the shadow build folder or
on the device. This is useful especially for .qml files, which are
executed at runtime.

Reviewed-by: con
parent 801c8bb6
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#include "fileinprojectfinder.h"
#include <utils/qtcassert.h>
#include <QFileInfo>
namespace Utils {
/**
\class FileInProjectFinder
Helper class to find the 'original' file in the project directory for a given file path.
Often files are copied in the build + deploy process. findFile() searches for an existing file
in the project directory for a given file path:
E.g. following file paths:
C:/app-build-desktop/qml/app/main.qml (shadow build directory)
C:/Private/e3026d63/qml/app/main.qml (Application data folder on Symbian device)
/Users/x/app-build-desktop/App.app/Contents/Resources/qml/App/main.qml (folder on Mac OS X)
should all be mapped to
$PROJECTDIR/qml/app/main.qml
*/
FileInProjectFinder::FileInProjectFinder()
{
}
void FileInProjectFinder::setProjectDirectory(const QString &absoluteProjectPath)
{
QTC_ASSERT(QFileInfo(absoluteProjectPath).exists()
&& QFileInfo(absoluteProjectPath).isAbsolute(), return);
if (absoluteProjectPath == m_projectDir)
return;
m_projectDir = absoluteProjectPath;
while (m_projectDir.endsWith(QLatin1Char('/')))
m_projectDir.remove(m_projectDir.length() - 1, 1);
m_cache.clear();
}
QString FileInProjectFinder::projectDirectory() const
{
return m_projectDir;
}
/**
Returns the best match for the given originalPath in the project directory.
The method first checks whether the originalPath inside the project directory exists.
If not, the leading directory in the path is stripped, and the - now shorter - path is
checked for existence. This continues until either the file is found, or the relative path
does not contain any directories any more: In this case the originalPath is returned.
*/
QString FileInProjectFinder::findFile(const QString &originalPath, bool *success) const
{
if (m_projectDir.isEmpty())
return originalPath;
const QChar separator = QLatin1Char('/');
if (originalPath.startsWith(m_projectDir + separator)) {
return originalPath;
}
if (m_cache.contains(originalPath))
return m_cache.value(originalPath);
// Strip directories one by one from the beginning of the path,
// and see if the new relative path exists in the build directory.
if (originalPath.contains(separator)) {
for (int pos = originalPath.indexOf(separator); pos != -1;
pos = originalPath.indexOf(separator, pos + 1)) {
QString candidate = originalPath;
candidate.remove(0, pos);
candidate.prepend(m_projectDir);
QFileInfo candidateInfo(candidate);
if (candidateInfo.exists() && candidateInfo.isFile()) {
if (success)
*success = true;
m_cache.insert(originalPath, candidate);
return candidate;
}
}
}
if (success)
*success = false;
return originalPath;
}
} // namespace Utils
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef FILEINPROJECTFINDER_H
#define FILEINPROJECTFINDER_H
#include <utils/utils_global.h>
#include <QtCore/QHash>
#include <QtCore/QString>
namespace Utils {
class QTCREATOR_UTILS_EXPORT FileInProjectFinder
{
public:
FileInProjectFinder();
void setProjectDirectory(const QString &absoluteProjectPath);
QString projectDirectory() const;
QString findFile(const QString &originalPath, bool *success = 0) const;
private:
QString m_projectDir;
QHash<QString,QString> m_cache;
};
} // namespace Utils
#endif // FILEINPROJECTFINDER_H
......@@ -52,7 +52,8 @@ SOURCES += $$PWD/environment.cpp \
$$PWD/debuggerlanguagechooser.cpp \
$$PWD/historycompleter.cpp \
$$PWD/buildablehelperlibrary.cpp \
$$PWD/annotateditemdelegate.cpp
$$PWD/annotateditemdelegate.cpp \
$$PWD/fileinprojectfinder.cpp
win32 {
SOURCES += $$PWD/abstractprocess_win.cpp \
......@@ -115,7 +116,8 @@ HEADERS += $$PWD/environment.h \
$$PWD/debuggerlanguagechooser.h \
$$PWD/historycompleter.h \
$$PWD/buildablehelperlibrary.h \
$$PWD/annotateditemdelegate.h
$$PWD/annotateditemdelegate.h \
$$PWD/fileinprojectfinder.h
FORMS += $$PWD/filewizardpage.ui \
$$PWD/projectintropage.ui \
......
......@@ -302,6 +302,7 @@ void InspectorUi::connected(ClientProxy *clientProxy)
}
connect(m_debugProject, SIGNAL(destroyed()), SLOT(currentDebugProjectRemoved()));
m_projectFinder.setProjectDirectory(m_debugProject->projectDirectory());
setupToolbar(true);
resetViews();
......@@ -562,12 +563,7 @@ void InspectorUi::gotoObjectReferenceDefinition(const QDeclarativeDebugObjectRef
if (source.lineNumber() < 0 || !QFile::exists(fileName))
return;
// do some extra checking for filenames with shadow builds - we don't want to
// open the shadow build file, but the real one by default.
if (isShadowBuildProject()) {
fileName = filenameForShadowBuildFile(fileName);
}
fileName = m_projectFinder.findFile(fileName);
Core::EditorManager *editorManager = Core::EditorManager::instance();
Core::IEditor *editor = editorManager->openEditor(fileName);
......@@ -582,37 +578,6 @@ void InspectorUi::gotoObjectReferenceDefinition(const QDeclarativeDebugObjectRef
}
}
QString InspectorUi::filenameForShadowBuildFile(const QString &filename) const
{
if (!debugProject() || !isShadowBuildProject())
return filename;
QDir projectDir(debugProject()->projectDirectory());
QDir buildDir(debugProjectBuildDirectory());
QFileInfo fileInfo(filename);
if (projectDir.exists() && buildDir.exists() && fileInfo.exists()) {
if (fileInfo.absoluteFilePath().startsWith(buildDir.canonicalPath())) {
QString fileRelativePath
= fileInfo.canonicalFilePath().mid(debugProjectBuildDirectory().length());
#ifdef Q_OS_MACX
// Qt Quick Applications by default copy the qml directory to
// buildDir()/X.app/Contents/Resources
static QRegExp resourceBundlePattern(QLatin1String("^.*\\.app/Contents/Resources/"));
fileRelativePath.remove(resourceBundlePattern);
#endif
QFileInfo projectFile(projectDir.canonicalPath() + QLatin1Char('/') + fileRelativePath);
if (projectFile.exists())
return projectFile.canonicalFilePath();
}
}
return filename;
}
bool InspectorUi::addQuotesForData(const QVariant &value) const
{
switch (value.type()) {
......
......@@ -35,6 +35,7 @@
#include <coreplugin/basemode.h>
#include <debugger/debuggerconstants.h>
#include <qmlprojectmanager/qmlprojectrunconfiguration.h>
#include <utils/fileinprojectfinder.h>
#include <qmljs/qmljsdocument.h>
#include <qmljs/parser/qmljsastfwd_p.h>
......@@ -173,6 +174,7 @@ private:
QString m_debugProjectBuildDir;
QStringList m_pendingPreviewDocumentNames;
Utils::FileInProjectFinder m_projectFinder;
static InspectorUi *m_instance;
};
......
......@@ -52,6 +52,8 @@ QtOutputFormatter::QtOutputFormatter(ProjectExplorer::Project *project)
, m_qtTestFail(QLatin1String("^ Loc: \\[(.*)\\]$"))
, m_project(project)
{
if(project)
m_projectFinder.setProjectDirectory(project->projectDirectory());
}
LinkResult QtOutputFormatter::matchLine(const QString &line) const
......@@ -177,48 +179,6 @@ void QtOutputFormatter::appendLine(QTextCursor &cursor, LinkResult lr, const QSt
cursor.insertText(line.mid(lr.end), normalFormat);
}
// Map absolute path in shadow build / in the deployment folder to the path in the project directory
//
// Input is e.g.
// C:/app-build-desktop/qml/app/main.qml (shadow build directory)
// C:/Private/e3026d63/qml/app/main.qml (Application data folder on Symbian device)
// /Users/x/app-build-desktop/App.app/Contents/Resources/qml/App/main.qml (folder on Mac OS X)
// which should be mapped to
// $PROJECTDIR/qml/app/main.qml
QString QtOutputFormatter::pathInSourceDirectory(const QString &originalFilePath)
{
QTC_ASSERT(QFileInfo(originalFilePath).isAbsolute(), return originalFilePath);
if (!m_project)
return originalFilePath;
const QString projectDirectory = m_project.data()->projectDirectory();
QTC_ASSERT(!projectDirectory.isEmpty(), return originalFilePath);
QTC_ASSERT(!projectDirectory.endsWith(QLatin1Char('/')), return originalFilePath);
const QChar separator = QLatin1Char('/');
if (originalFilePath.startsWith(projectDirectory + separator)) {
return originalFilePath;
}
// Strip directories one by one from the beginning of the path,
// and see if the new relative path exists in the build directory.
if (originalFilePath.contains(separator)) {
for (int pos = originalFilePath.indexOf(separator); pos != -1; pos = originalFilePath.indexOf(separator, pos + 1)) {
QString candidate = originalFilePath;
candidate.remove(0, pos);
candidate.prepend(projectDirectory);
QFileInfo candidateInfo(candidate);
if (candidateInfo.exists() && candidateInfo.isFile())
return candidate;
}
}
return originalFilePath;
}
void QtOutputFormatter::handleLink(const QString &href)
{
if (!href.isEmpty()) {
......@@ -230,7 +190,7 @@ void QtOutputFormatter::handleLink(const QString &href)
const QString fileName = QUrl(qmlLineColumnLink.cap(1)).toLocalFile();
const int line = qmlLineColumnLink.cap(2).toInt();
const int column = qmlLineColumnLink.cap(3).toInt();
TextEditor::BaseTextEditor::openEditorAt(pathInSourceDirectory(fileName), line, column - 1);
TextEditor::BaseTextEditor::openEditorAt(m_projectFinder.findFile(fileName), line, column - 1);
return;
}
......@@ -241,7 +201,7 @@ void QtOutputFormatter::handleLink(const QString &href)
if (qmlLineLink.indexIn(href) != -1) {
const QString fileName = QUrl(qmlLineLink.cap(1)).toLocalFile();
const int line = qmlLineLink.cap(2).toInt();
TextEditor::BaseTextEditor::openEditorAt(pathInSourceDirectory(fileName), line);
TextEditor::BaseTextEditor::openEditorAt(m_projectFinder.findFile(fileName), line);
return;
}
......@@ -283,7 +243,7 @@ void QtOutputFormatter::handleLink(const QString &href)
}
} else if (!fi.exists()) {
// map possible on-device path to source path
fileName = pathInSourceDirectory(fileName);
fileName = m_projectFinder.findFile(fileName);
}
TextEditor::BaseTextEditor::openEditorAt(fileName, line, 0);
return;
......
......@@ -33,6 +33,7 @@
#include "qt4projectmanager_global.h"
#include <projectexplorer/outputformatter.h>
#include <utils/fileinprojectfinder.h>
#include <QtCore/QRegExp>
#include <QtCore/QWeakPointer>
......@@ -65,7 +66,6 @@ public:
private:
LinkResult matchLine(const QString &line) const;
void appendLine(QTextCursor & cursor, LinkResult lr, const QString &line, bool onStdError);
QString pathInSourceDirectory(const QString &originalFilePath);
QRegExp m_qmlError;
QRegExp m_qtError;
......@@ -74,6 +74,7 @@ private:
QWeakPointer<ProjectExplorer::Project> m_project;
QString m_lastLine;
QString m_deferedText;
Utils::FileInProjectFinder m_projectFinder;
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment