From 1e86df719135c93e70f73c3a30db356acdaabb6c Mon Sep 17 00:00:00 2001 From: Kai Koehne <kai.koehne@nokia.com> Date: Wed, 1 Dec 2010 16:09:08 +0100 Subject: [PATCH] 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 --- src/libs/utils/fileinprojectfinder.cpp | 122 ++++++++++++++++++ src/libs/utils/fileinprojectfinder.h | 57 ++++++++ src/libs/utils/utils-lib.pri | 6 +- src/plugins/qmljsinspector/qmljsinspector.cpp | 39 +----- src/plugins/qmljsinspector/qmljsinspector.h | 2 + .../qt4projectmanager/qtoutputformatter.cpp | 50 +------ .../qt4projectmanager/qtoutputformatter.h | 3 +- 7 files changed, 194 insertions(+), 85 deletions(-) create mode 100644 src/libs/utils/fileinprojectfinder.cpp create mode 100644 src/libs/utils/fileinprojectfinder.h diff --git a/src/libs/utils/fileinprojectfinder.cpp b/src/libs/utils/fileinprojectfinder.cpp new file mode 100644 index 00000000000..141c78e7329 --- /dev/null +++ b/src/libs/utils/fileinprojectfinder.cpp @@ -0,0 +1,122 @@ +/************************************************************************** +** +** 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 diff --git a/src/libs/utils/fileinprojectfinder.h b/src/libs/utils/fileinprojectfinder.h new file mode 100644 index 00000000000..878693f5f75 --- /dev/null +++ b/src/libs/utils/fileinprojectfinder.h @@ -0,0 +1,57 @@ +/************************************************************************** +** +** 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 diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri index 245bb7ecdd2..857844aa2b2 100644 --- a/src/libs/utils/utils-lib.pri +++ b/src/libs/utils/utils-lib.pri @@ -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 \ diff --git a/src/plugins/qmljsinspector/qmljsinspector.cpp b/src/plugins/qmljsinspector/qmljsinspector.cpp index a9bd4ea35a0..542826809f9 100644 --- a/src/plugins/qmljsinspector/qmljsinspector.cpp +++ b/src/plugins/qmljsinspector/qmljsinspector.cpp @@ -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()) { diff --git a/src/plugins/qmljsinspector/qmljsinspector.h b/src/plugins/qmljsinspector/qmljsinspector.h index 6d379ea0461..15acda68a97 100644 --- a/src/plugins/qmljsinspector/qmljsinspector.h +++ b/src/plugins/qmljsinspector/qmljsinspector.h @@ -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; }; diff --git a/src/plugins/qt4projectmanager/qtoutputformatter.cpp b/src/plugins/qt4projectmanager/qtoutputformatter.cpp index 98dc10b044d..15a92262871 100644 --- a/src/plugins/qt4projectmanager/qtoutputformatter.cpp +++ b/src/plugins/qt4projectmanager/qtoutputformatter.cpp @@ -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; diff --git a/src/plugins/qt4projectmanager/qtoutputformatter.h b/src/plugins/qt4projectmanager/qtoutputformatter.h index a5e4aff8f97..8941ab4c1b0 100644 --- a/src/plugins/qt4projectmanager/qtoutputformatter.h +++ b/src/plugins/qt4projectmanager/qtoutputformatter.h @@ -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; }; -- GitLab