diff options
author | Eike Ziller <eike.ziller@qt.io> | 2017-10-19 13:01:12 +0200 |
---|---|---|
committer | Eike Ziller <eike.ziller@qt.io> | 2017-10-19 13:01:12 +0200 |
commit | bb9663529beec0530f227cb196907d8a87593a3d (patch) | |
tree | bd772de7f00d73086e1d0c0a051b9fefafdeefb2 | |
parent | 7208ef1ff51e766ae5af64c95436b74a6535d811 (diff) | |
parent | 38d307ffb84e4c945682263e2e006ddf23aed857 (diff) |
Merge remote-tracking branch 'origin/4.5'
Change-Id: Ie83666bd18e899dabf5190c360027bf02abecdaf
64 files changed, 611 insertions, 719 deletions
diff --git a/doc/src/qtquick/qtquick-modules-with-plugins.qdoc b/doc/src/qtquick/qtquick-modules-with-plugins.qdoc index d03626efca7..816c6c934de 100644 --- a/doc/src/qtquick/qtquick-modules-with-plugins.qdoc +++ b/doc/src/qtquick/qtquick-modules-with-plugins.qdoc @@ -42,7 +42,47 @@ the contained components, and therefore, the modules must provide extra type information for code completion and the semantic checks to work correctly. - When you write a QML module or use QML from a C++ application you typically + To create a QML module and make it appear in the \uicontrol Library in + \QMLD: + + \list 1 + + \li Create custom QML controls and place all the \c .qml files in a + directory dedicated to your module. + + \li Create a \c qmldir file for your module and place it in the module + directory. For more information, see + \l {Module Definition qmldir Files}. + + \li Create a \c qmltypes file, preferably using \c qmlplugindump. + For more information see, \l {Generating qmltypes Files}. + + \li Create a directory named \c designer in your module directory. + + \li Create a \c .metainfo file for your module and place it in the + \c designer directory. Meta information is needed to display the + components in the \uicontrol {QML Types} tab in the \uicontrol + Library. Use a metainfo file delivered with Qt, such as + \c qtquickcontrols2.metainfo, as an example. + + \li Import your module into a project using \c QML_IMPORT_PATH in the + .pro file: \c {QML_IMPORT_PATH += path/to/module}. + For more information, see \l {Importing QML Modules}. + + \li Make sure that the QML emulation layer of \QMLD is built with + the same Qt version as your QML modules. For more information, see + \l {Running QML Modules in Qt Quick Designer}. You can also try + skipping this step and take it later, if necessary. + + \endlist + + Your module should now appear in the \uicontrol Imports tab in the + \uicontrol Library in \QMLD. Your components should appear in the + \uicontrol {QML Types} tab if a valid \c .metainfo file is in place. + + \section1 Registering QML Types + + When you write a QML module or use QML from a C++ application, you typically register new types with the qmlRegisterType() function or expose some class instances with \l{QQmlContext::setContextProperty()}. The \QC C++ code model now scans for these calls and @@ -55,19 +95,7 @@ Classes registered with \c qmlRegisterType() can be used as backend objects in the \QMLD. For more information, see \l {Adding Connections}. - By default, \QC will look in the QML import path of Qt for QML modules. - If your applications adds additional import paths that \QC should use, - then you can specify those using \c{QML_IMPORT_PATH} in the \c{.pro} file of your - application. - - If you use CMake, add the following command to the CMakeLists.txt file to - set the QML import path: - - \code - {set(QML_IMPORT_PATH ${CMAKE_SOURCE_DIR}/qml ${CMAKE_BINARY_DIR}/imports CACHE string "" FORCE)} - \endcode - - The import path affects all the targets built by the CMake project. + \section1 Generating qmltypes Files Ideally, QML modules have a \c{plugins.qmltypes} file in the same directory as the \c qmldir file. The \c qmltypes file contains a description of the @@ -79,20 +107,18 @@ addition to \c{plugins.qmltypes}. For more information, see \l{Writing a qmltypes File}. - \section1 Generating qmltypes Files - You can create and edit \c qmltypes files manually, but you are recommended to use the \c qmlplugindump tool shipped with Qt 4.8 and later to generate them automatically. - Once you have obtained qmlplugindump for the Qt version the QML module's + Once you have obtained \c qmlplugindump for the Qt version the QML module's plugins were compiled with, run the following command to load My.Module version 1.0 from \c{/import/path/my/module} including all its plugins and output a description of the plugins' types to \c{/import/path/my/module/plugins.qmltypes}: \code - qmlplugindump My.Module 1.0 /import/path > /import/path/my/module/plugins.qmltypes + qmlplugindump -nonrelocatable My.Module 1.0 /import/path > /import/path/my/module/plugins.qmltypes \endcode You can safely ignore the debug output. @@ -101,13 +127,29 @@ the sources in \c{<QtCreator>/share/qtcreator/qml/qmldump} if the Qt version contains private headers. - \section1 Dumping Plugins Automatically + \section2 Dumping Plugins Automatically If a module with plugins lacks the \c qmltypes file, \QC tries to generate a temporary file itself by running the \c qmldump program in the background. However, this automatic dumping is a fallback mechanism with many points of failure and you cannot rely upon it. + \section1 Importing QML Modules + + By default, \QC will look in the QML import path of Qt for QML modules. + If your applications adds additional import paths that \QC should use, + then you can specify those using \c{QML_IMPORT_PATH} in the \c{.pro} file of your + application. + + If you use CMake, add the following command to the CMakeLists.txt file to + set the QML import path: + + \code + {set(QML_IMPORT_PATH ${CMAKE_SOURCE_DIR}/qml ${CMAKE_BINARY_DIR}/imports CACHE string "" FORCE)} + \endcode + + The import path affects all the targets built by the CMake project. + \section1 Running QML Modules in Qt Quick Designer \QMLD uses a QML emulation layer (also called QML Puppet) to render and diff --git a/qbs/imports/QtcAutotest.qbs b/qbs/imports/QtcAutotest.qbs index bbef04d1751..cb914f7ef1c 100644 --- a/qbs/imports/QtcAutotest.qbs +++ b/qbs/imports/QtcAutotest.qbs @@ -15,7 +15,11 @@ QtcProduct { project.buildDirectory + '/' + qtc.ide_library_path, project.buildDirectory + '/' + qtc.ide_plugin_path ] - cpp.defines: base.filter(function(d) { return d != "QT_RESTRICTED_CAST_FROM_ASCII"; }) + cpp.defines: base.filter(function(d) { + return d !== "QT_RESTRICTED_CAST_FROM_ASCII" + && d !== "QT_USE_FAST_OPERATOR_PLUS" + && d !== "QT_USE_FAST_CONCATENATION"; + }) Group { fileTagsFilter: product.type diff --git a/qbs/modules/qtc/qtc.qbs b/qbs/modules/qtc/qtc.qbs index 4b8058d3a6a..60816c0eb07 100644 --- a/qbs/modules/qtc/qtc.qbs +++ b/qbs/modules/qtc/qtc.qbs @@ -76,6 +76,8 @@ Module { "QT_NO_CAST_TO_ASCII", "QT_RESTRICTED_CAST_FROM_ASCII", "QT_DISABLE_DEPRECATED_BEFORE=0x050600", + "QT_USE_FAST_OPERATOR_PLUS", + "QT_USE_FAST_CONCATENATION", ].concat(testsEnabled ? ["WITH_TESTS"] : []) Rule { diff --git a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp index 102e3851d04..1dc4716649c 100644 --- a/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp +++ b/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/objectnodeinstance.cpp @@ -693,7 +693,7 @@ static inline QString fixComponentPathForIncompatibleQt(const QString &component //plugin directories might contain the version number fixedPath.chop(4); fixedPath += QLatin1Char('/') + QFileInfo(componentPath).fileName(); - if (QFileInfo(fixedPath).exists()) + if (QFileInfo::exists(fixedPath)) return fixedPath; } } diff --git a/src/libs/extensionsystem/pluginerroroverview.ui b/src/libs/extensionsystem/pluginerroroverview.ui index e87cb34c639..080a73b233f 100644 --- a/src/libs/extensionsystem/pluginerroroverview.ui +++ b/src/libs/extensionsystem/pluginerroroverview.ui @@ -11,7 +11,7 @@ </rect> </property> <property name="windowTitle"> - <string>Plugin loader messages</string> + <string>Plugin Loader Messages</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> diff --git a/src/libs/utils/bracematcher.cpp b/src/libs/utils/bracematcher.cpp deleted file mode 100644 index eb56df561fc..00000000000 --- a/src/libs/utils/bracematcher.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Konstantin Tokarev <annulen@yandex.ru> -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "bracematcher.h" - -#include <QTextDocument> -#include <QTextCursor> - -/*! - \class Utils::BraceMatcher - \brief The BraceMatcher class implements a generic autocompleter of braces - and quotes. - - This is a helper class for autocompleter implementations. To use it, - define \e brace, \e quote, and \e delimiter characters for a given language. -*/ - -namespace Utils { - -/*! - * Adds a pair of characters, corresponding to \a opening and \a closing braces. - */ -void BraceMatcher::addBraceCharPair(const QChar opening, const QChar closing) -{ - m_braceChars[opening] = closing; -} - -/*! - * Adds a \a quote character. - */ -void BraceMatcher::addQuoteChar(const QChar quote) -{ - m_quoteChars << quote; -} - -/*! - * Adds a separator character \a sep that should be skipped when overtyping it. - * For example, it could be ';' or ',' in C-like languages. - */ -void BraceMatcher::addDelimiterChar(const QChar sep) -{ - m_delimiterChars << sep; -} - -bool BraceMatcher::shouldInsertMatchingText(const QTextCursor &tc) const -{ - QTextDocument *doc = tc.document(); - return shouldInsertMatchingText(doc->characterAt(tc.selectionEnd())); -} - -bool BraceMatcher::shouldInsertMatchingText(const QChar lookAhead) const -{ - return lookAhead.isSpace() - || isQuote(lookAhead) - || isDelimiter(lookAhead) - || isClosingBrace(lookAhead); -} - -QString BraceMatcher::insertMatchingBrace(const QTextCursor &cursor, - const QString &text, - const QChar la, - int *skippedChars) const -{ - if (text.length() != 1) - return QString(); - - if (!shouldInsertMatchingText(cursor)) - return QString(); - - const QChar ch = text.at(0); - if (isQuote(ch)) { - if (la != ch) - return QString(ch); - ++*skippedChars; - return QString(); - } - - if (isOpeningBrace(ch)) - return QString(m_braceChars[ch]); - - if (isDelimiter(ch) || isClosingBrace(ch)) { - if (la == ch) - ++*skippedChars; - } - - return QString(); -} - -/*! - * Returns true if the character \a c was added as one of character types. - */ -bool BraceMatcher::isKnownChar(const QChar c) const -{ - return isQuote(c) || isDelimiter(c) || isOpeningBrace(c) || isClosingBrace(c); -} - -} // namespace Utils diff --git a/src/libs/utils/bracematcher.h b/src/libs/utils/bracematcher.h deleted file mode 100644 index e2fa0928d3d..00000000000 --- a/src/libs/utils/bracematcher.h +++ /dev/null @@ -1,66 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Konstantin Tokarev <annulen@yandex.ru> -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include "utils_global.h" - -#include <QChar> -#include <QSet> -#include <QMap> - -QT_BEGIN_NAMESPACE -class QString; -class QTextCursor; -QT_END_NAMESPACE - -namespace Utils { - -class QTCREATOR_UTILS_EXPORT BraceMatcher -{ -public: - void addBraceCharPair(const QChar opening, const QChar closing); - void addQuoteChar(const QChar quote); - void addDelimiterChar(const QChar delim); - - bool shouldInsertMatchingText(const QTextCursor &tc) const; - bool shouldInsertMatchingText(const QChar lookAhead) const; - - QString insertMatchingBrace(const QTextCursor &tc, const QString &text, - const QChar la, int *skippedChars) const; - - bool isOpeningBrace(const QChar c) const { return m_braceChars.contains(c); } - bool isClosingBrace(const QChar c) const { return m_braceChars.values().contains(c); } - bool isQuote(const QChar c) const { return m_quoteChars.contains(c); } - bool isDelimiter(const QChar c) const { return m_delimiterChars.contains(c); } - bool isKnownChar(const QChar c) const; - -private: - QMap<QChar, QChar> m_braceChars; - QSet<QChar> m_quoteChars; - QSet<QChar> m_delimiterChars; -}; - -} // namespace Utils diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri index c97245cbf25..84893298ee8 100644 --- a/src/libs/utils/utils-lib.pri +++ b/src/libs/utils/utils-lib.pri @@ -89,7 +89,6 @@ SOURCES += \ $$PWD/basetreeview.cpp \ $$PWD/qtcassert.cpp \ $$PWD/elfreader.cpp \ - $$PWD/bracematcher.cpp \ $$PWD/proxyaction.cpp \ $$PWD/elidinglabel.cpp \ $$PWD/hostosinfo.cpp \ @@ -197,7 +196,6 @@ HEADERS += \ $$PWD/appmainwindow.h \ $$PWD/basetreeview.h \ $$PWD/elfreader.h \ - $$PWD/bracematcher.h \ $$PWD/proxyaction.h \ $$PWD/hostosinfo.h \ $$PWD/osspecificaspects.h \ diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index b42f4aecc6d..2f33b82931a 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -50,8 +50,6 @@ Project { "basetreeview.h", "benchmarker.cpp", "benchmarker.h", - "bracematcher.cpp", - "bracematcher.h", "buildablehelperlibrary.cpp", "buildablehelperlibrary.h", "camelhumpmatcher.cpp", diff --git a/src/plugins/autotest/gtest/gtesttreeitem.cpp b/src/plugins/autotest/gtest/gtesttreeitem.cpp index b0f11830383..efd336e1efe 100644 --- a/src/plugins/autotest/gtest/gtesttreeitem.cpp +++ b/src/plugins/autotest/gtest/gtesttreeitem.cpp @@ -138,19 +138,22 @@ QList<TestConfiguration *> GTestTreeItem::getAllTestConfigurations() const const TestTreeItem *grandChild = child->childItem(grandChildRow); const QString &key = grandChild->proFile(); proFilesWithTestSets.insert(key, proFilesWithTestSets[key] + 1); - proFilesWithInternalTargets.insert(key, grandChild->internalTargets()); + proFilesWithInternalTargets[key].unite(grandChild->internalTargets()); } } QHash<QString, int>::ConstIterator it = proFilesWithTestSets.begin(); QHash<QString, int>::ConstIterator end = proFilesWithTestSets.end(); for ( ; it != end; ++it) { - GTestConfiguration *tc = new GTestConfiguration; - tc->setTestCaseCount(it.value()); - tc->setProjectFile(it.key()); - tc->setProject(project); - tc->setInternalTargets(proFilesWithInternalTargets.value(it.key())); - result << tc; + const QSet<QString> &internalTargets = proFilesWithInternalTargets[it.key()]; + for (const QString &target : internalTargets) { + GTestConfiguration *tc = new GTestConfiguration; + tc->setTestCaseCount(it.value()); + tc->setProjectFile(it.key()); + tc->setProject(project); + tc->setInternalTarget(target); + result << tc; + } } return result; @@ -184,8 +187,8 @@ QList<TestConfiguration *> GTestTreeItem::getSelectedTestConfigurations() const auto &testCases = proFilesWithCheckedTestSets[child->childItem(0)->proFile()]; testCases.filters.append(gtestFilter(child->state()).arg(child->name()).arg('*')); testCases.additionalTestCaseCount += grandChildCount - 1; - proFilesWithInternalTargets.insert(child->childItem(0)->proFile(), - child->internalTargets()); + proFilesWithInternalTargets[child->childItem(0)->proFile()].unite( + child->internalTargets()); break; } case Qt::PartiallyChecked: { @@ -194,8 +197,8 @@ QList<TestConfiguration *> GTestTreeItem::getSelectedTestConfigurations() const if (grandChild->checked() == Qt::Checked) { proFilesWithCheckedTestSets[grandChild->proFile()].filters.append( gtestFilter(child->state()).arg(child->name()).arg(grandChild->name())); - proFilesWithInternalTargets.insert(grandChild->proFile(), - grandChild->internalTargets()); + proFilesWithInternalTargets[grandChild->proFile()].unite( + grandChild->internalTargets()); } } break; @@ -206,13 +209,16 @@ QList<TestConfiguration *> GTestTreeItem::getSelectedTestConfigurations() const QHash<QString, TestCases>::ConstIterator it = proFilesWithCheckedTestSets.begin(); QHash<QString, TestCases>::ConstIterator end = proFilesWithCheckedTestSets.end(); for ( ; it != end; ++it) { - GTestConfiguration *tc = new GTestConfiguration; - tc->setTestCases(it.value().filters); - tc->setTestCaseCount(tc->testCaseCount() + it.value().additionalTestCaseCount); - tc->setProjectFile(it.key()); - tc->setProject(project); - tc->setInternalTargets(proFilesWithInternalTargets[it.key()]); - result << tc; + const QSet<QString> &internalTargets = proFilesWithInternalTargets[it.key()]; + for (const QString &target : internalTargets) { + GTestConfiguration *tc = new GTestConfiguration; + tc->setTestCases(it.value().filters); + tc->setTestCaseCount(tc->testCaseCount() + it.value().additionalTestCaseCount); + tc->setProjectFile(it.key()); + tc->setProject(project); + tc->setInternalTarget(target); + result << tc; + } } return result; diff --git a/src/plugins/autotest/testconfiguration.cpp b/src/plugins/autotest/testconfiguration.cpp index 34c8ef18135..46b38f19922 100644 --- a/src/plugins/autotest/testconfiguration.cpp +++ b/src/plugins/autotest/testconfiguration.cpp @@ -320,6 +320,12 @@ void TestConfiguration::setProject(Project *project) m_project = project; } +void TestConfiguration::setInternalTarget(const QString &target) +{ + m_buildTargets.clear(); + m_buildTargets.insert(target); +} + void TestConfiguration::setInternalTargets(const QSet<QString> &targets) { m_buildTargets = targets; diff --git a/src/plugins/autotest/testconfiguration.h b/src/plugins/autotest/testconfiguration.h index e3a0bcea3f9..224d7b19fe3 100644 --- a/src/plugins/autotest/testconfiguration.h +++ b/src/plugins/autotest/testconfiguration.h @@ -67,6 +67,7 @@ public: void setDisplayName(const QString &displayName); void setEnvironment(const Utils::Environment &env); void setProject(ProjectExplorer::Project *project); + void setInternalTarget(const QString &target); void setInternalTargets(const QSet<QString> &targets); void setOriginalRunConfiguration(ProjectExplorer::RunConfiguration *runConfig); diff --git a/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.cpp b/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.cpp index 62573c70fde..2b364a1d8c5 100644 --- a/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.cpp +++ b/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.cpp @@ -243,11 +243,14 @@ static bool isValidIdentifierChar(const QChar &character) int ActivationSequenceContextProcessor::findStartOfName( const TextEditor::AssistInterface *assistInterface, - int startPosition) + int startPosition, + NameCategory category) { int position = startPosition; QChar character; - if (position > 2 && assistInterface->characterAt(position - 1) == '>' + + if (category == NameCategory::Function + && position > 2 && assistInterface->characterAt(position - 1) == '>' && assistInterface->characterAt(position - 2) != '-') { uint unbalancedLessGreater = 1; --position; @@ -267,11 +270,12 @@ int ActivationSequenceContextProcessor::findStartOfName( } while (isValidIdentifierChar(character)); int prevPosition = skipPrecedingWhitespace(assistInterface, position); - if (assistInterface->characterAt(prevPosition) == ':' + if (category == NameCategory::Function + && assistInterface->characterAt(prevPosition) == ':' && assistInterface->characterAt(prevPosition - 1) == ':') { // Handle :: case - go recursive prevPosition = skipPrecedingWhitespace(assistInterface, prevPosition - 2); - return findStartOfName(assistInterface, prevPosition + 1); + return findStartOfName(assistInterface, prevPosition + 1, category); } return position + 1; diff --git a/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.h b/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.h index 6bb25da086f..bffd8d0d262 100644 --- a/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.h +++ b/src/plugins/clangcodemodel/clangactivationsequencecontextprocessor.h @@ -49,8 +49,10 @@ public: const QTextCursor &textCursor_forTestOnly() const; + enum class NameCategory { Function, NonFunction }; static int findStartOfName(const TextEditor::AssistInterface *assistInterface, - int startPosition); + int startPosition, + NameCategory category = NameCategory::NonFunction); static int skipPrecedingWhitespace(const TextEditor::AssistInterface *assistInterface, int startPosition); diff --git a/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp b/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp index bb8308eada5..1f3f5bfebb1 100644 --- a/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp +++ b/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp @@ -101,8 +101,8 @@ int ClangCompletionContextAnalyzer::startOfFunctionCall(int endOfOperator) const ExpressionUnderCursor euc(m_languageFeatures); index = euc.startOfFunctionCall(textCursor); index = ActivationSequenceContextProcessor::skipPrecedingWhitespace(m_interface, index); - const int functionNameStart = ActivationSequenceContextProcessor::findStartOfName(m_interface, - index); + const int functionNameStart = ActivationSequenceContextProcessor::findStartOfName( + m_interface, index, ActivationSequenceContextProcessor::NameCategory::Function); if (functionNameStart == -1) return -1; diff --git a/src/plugins/debugger/debugger.qbs b/src/plugins/debugger/debugger.qbs index 08faeb3d1e6..9a69fb289af 100644 --- a/src/plugins/debugger/debugger.qbs +++ b/src/plugins/debugger/debugger.qbs @@ -110,7 +110,6 @@ Project { files: [ "gdbengine.cpp", "gdbengine.h", "gdboptionspage.cpp", - "startgdbserverdialog.cpp", "startgdbserverdialog.h", ] } diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index d8ee01a58f6..e5b8d8fdbe6 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -61,7 +61,6 @@ #include "snapshothandler.h" #include "threadshandler.h" #include "commonoptionspage.h" -#include "gdb/startgdbserverdialog.h" #include "analyzer/analyzerconstants.h" #include "analyzer/analyzermanager.h" @@ -734,7 +733,6 @@ public: void updateDebugWithoutDeployMenu(); void startRemoteCdbSession(); - void startRemoteServerAndAttachToProcess(); void attachToRunningApplication(); void attachToUnstartedApplicationDialog(); void attachToQmlPort(); @@ -980,7 +978,6 @@ public: QAction *m_startAction = 0; QAction *m_debugWithoutDeployAction = 0; QAction *m_startAndDebugApplicationAction = 0; - QAction *m_startRemoteServerAction = 0; QAction *m_attachToRunningApplication = 0; QAction *m_attachToUnstartedApplication = 0; QAction *m_attachToQmlPortAction = 0; @@ -1506,10 +1503,6 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, act->setText(tr("Attach to Running Debug Server...")); connect(act, &QAction::triggered, this, &StartApplicationDialog::attachToRemoteServer); - act = m_startRemoteServerAction = new QAction(this); - act->setText(tr("Start Debug Server Attached to Process...")); - connect(act, &QAction::triggered, this, &DebuggerPluginPrivate::startRemoteServerAndAttachToProcess); - act = m_attachToRunningApplication = new QAction(this); act->setText(tr("Attach to Running Application...")); connect(act, &QAction::triggered, this, &DebuggerPluginPrivate::attachToRunningApplication); @@ -1583,11 +1576,6 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments, cmd->setAttribute(Command::CA_Hide); mstart->addAction(cmd, Constants::G_SPECIAL); - cmd = ActionManager::registerAction(m_startRemoteServerAction, - "Debugger.StartRemoteServer"); - cmd->setDescription(tr("Start Gdbserver")); - mstart->addAction(cmd, Constants::G_SPECIAL); - if (m_startRemoteCdbAction) { cmd = ActionManager::registerAction(m_startRemoteCdbAction, "Debugger.AttachRemoteCdb"); @@ -1991,30 +1979,48 @@ void DebuggerPluginPrivate::startRemoteCdbSession() debugger->startRunControl(); } -void DebuggerPluginPrivate::startRemoteServerAndAttachToProcess() +class RemoteAttachRunner : public DebuggerRunTool { - auto kitChooser = new DebuggerKitChooser(DebuggerKitChooser::AnyDebugging); - auto dlg = new DeviceProcessesDialog(kitChooser, ICore::dialogParent()); - dlg->addAcceptButton(DeviceProcessesDialog::tr("&Attach to Process")); - dlg->showAllDevices(); - if (dlg->exec() == QDialog::Rejected) { - delete dlg; - return; +public: + RemoteAttachRunner(RunControl *runControl, Kit *kit, int pid) + : DebuggerRunTool(runControl, kit) + { + IDevice::ConstPtr device = DeviceKitInformation::device(kit); + setDisplayName("AttachToRunningProcess"); + + portsGatherer = new GdbServerPortsGatherer(runControl); + portsGatherer->setUseGdbServer(true); + portsGatherer->setUseQmlServer(false); + portsGatherer->setDevice(device); + + auto gdbServer = new GdbServerRunner(runControl, portsGatherer); + gdbServer->setUseMulti(false); + gdbServer->addStartDependency(portsGatherer); + gdbServer->setDevice(device); + gdbServer->setAttachPid(ProcessHandle(pid)); + + addStartDependency(gdbServer); + + setStartMode(AttachToRemoteProcess); + setCloseMode(DetachAtClose); + + // setInferiorExecutable(localExecutable); + setUseContinueInsteadOfRun(true); + setContinueAfterAttach(false); } - dlg->setAttribute(Qt::WA_DeleteOnClose); - Kit *kit = kitChooser->currentKit(); - QTC_ASSERT(kit, return); - IDevice::ConstPtr device = DeviceKitInformation::device(kit); - QTC_ASSERT(device, return); + void start() final + { + setRemoteChannel(portsGatherer->gdbServerChannel()); + DebuggerRunTool::start(); + } - GdbServerStarter *starter = new GdbServerStarter(dlg, true); - starter->run(); -} + GdbServerPortsGatherer *portsGatherer; +}; void DebuggerPluginPrivate::attachToRunningApplication() { - auto kitChooser = new DebuggerKitChooser(DebuggerKitChooser::LocalDebugging); + auto kitChooser = new DebuggerKitChooser(DebuggerKitChooser::AnyDebugging); auto dlg = new DeviceProcessesDialog(kitChooser, ICore::dialogParent()); dlg->addAcceptButton(DeviceProcessesDialog::tr("&Attach to Process")); @@ -2030,11 +2036,14 @@ void DebuggerPluginPrivate::attachToRunningApplication() IDevice::ConstPtr device = DeviceKitInformation::device(kit); QTC_ASSERT(device, return); + DeviceProcessItem process = dlg->currentProcess(); + if (device->type() == PE::DESKTOP_DEVICE_TYPE) { - attachToRunningProcess(kit, dlg->currentProcess(), false); + attachToRunningProcess(kit, process, false); } else { - GdbServerStarter *starter = new GdbServerStarter(dlg, true); - starter->run(); + auto runControl = new RunControl(nullptr, ProjectExplorer::Constants::DEBUG_RUN_MODE); + auto debugger = new RemoteAttachRunner(runControl, kit, process.pid); + debugger->startRunControl(); } } diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index b16a94b4f1e..4b93b87cf4b 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -937,6 +937,8 @@ GdbServerPortsGatherer::GdbServerPortsGatherer(RunControl *runControl) this, &RunWorker::reportFailure); connect(&m_portsGatherer, &DeviceUsedPortsGatherer::portListReady, this, &GdbServerPortsGatherer::handlePortListReady); + + m_device = runControl->device(); } GdbServerPortsGatherer::~GdbServerPortsGatherer() @@ -945,26 +947,31 @@ GdbServerPortsGatherer::~GdbServerPortsGatherer() QString GdbServerPortsGatherer::gdbServerChannel() const { - const QString host = device()->sshParameters().host; + const QString host = m_device->sshParameters().host; return QString("%1:%2").arg(host).arg(m_gdbServerPort.number()); } QUrl GdbServerPortsGatherer::qmlServer() const { - QUrl server = device()->toolControlChannel(IDevice::QmlControlChannel); + QUrl server = m_device->toolControlChannel(IDevice::QmlControlChannel); server.setPort(m_qmlServerPort.number()); return server; } +void GdbServerPortsGatherer::setDevice(IDevice::ConstPtr device) +{ + m_device = device; +} + void GdbServerPortsGatherer::start() { appendMessage(tr("Checking available ports..."), NormalMessageFormat); - m_portsGatherer.start(device()); + m_portsGatherer.start(m_device); } void GdbServerPortsGatherer::handlePortListReady() { - Utils::PortList portList = device()->freePorts(); + Utils::PortList portList = m_device->freePorts(); appendMessage(tr("Found %n free ports.", nullptr, portList.count()), NormalMessageFormat); if (m_useGdbServer) { m_gdbServerPort = m_portsGatherer.getNextFreePort(&portList); @@ -990,19 +997,38 @@ GdbServerRunner::GdbServerRunner(RunControl *runControl, GdbServerPortsGatherer : SimpleTargetRunner(runControl), m_portsGatherer(portsGatherer) { setDisplayName("GdbServerRunner"); + if (runControl->runnable().is<StandardRunnable>()) + m_runnable = runControl->runnable().as<StandardRunnable>(); } GdbServerRunner::~GdbServerRunner() { } +void GdbServerRunner::setRunnable(const StandardRunnable &runnable) +{ + m_runnable = runnable; +} + +void GdbServerRunner::setUseMulti(bool on) +{ + m_useMulti = on; +} + +void GdbServerRunner::setAttachPid(ProcessHandle pid) +{ + m_pid = pid; +} + void GdbServerRunner::start() { QTC_ASSERT(m_portsGatherer, reportFailure(); return); - StandardRunnable r = runnable().as<StandardRunnable>(); - QStringList args = QtcProcess::splitArgs(r.commandLineArguments, OsTypeLinux); - QString command; + StandardRunnable gdbserver; + gdbserver.environment = m_runnable.environment; + gdbserver.workingDirectory = m_runnable.workingDirectory; + + QStringList args = QtcProcess::splitArgs(m_runnable.commandLineArguments, OsTypeLinux); const bool isQmlDebugging = m_portsGatherer->useQmlServer(); const bool isCppDebugging = m_portsGatherer->useGdbServer(); @@ -1011,21 +1037,24 @@ void GdbServerRunner::start() args.prepend(QmlDebug::qmlDebugTcpArguments(QmlDebug::QmlDebuggerServices, m_portsGatherer->qmlServerPort())); } - if (isQmlDebugging && !isCppDebugging) { - command = r.executable; + gdbserver.executable = m_runnable.executable; // FIXME: Case should not happen? } else { - command = device()->debugServerPath(); - if (command.isEmpty()) - command = "gdbserver"; + gdbserver.executable = device()->debugServerPath(); + if (gdbserver.executable.isEmpty()) + gdbserver.executable = "gdbserver"; args.clear(); - args.append(QString("--multi")); + if (m_useMulti) + args.append("--multi"); + if (m_pid.isValid()) + args.append("--attach"); args.append(QString(":%1").arg(m_portsGatherer->gdbServerPort().number())); + if (m_pid.isValid()) + args.append(QString::number(m_pid.pid())); } - r.executable = command; - r.commandLineArguments = QtcProcess::joinArgs(args, OsTypeLinux); + gdbserver.commandLineArguments = QtcProcess::joinArgs(args, OsTypeLinux); - setRunnable(r); + SimpleTargetRunner::setRunnable(gdbserver); appendMessage(tr("Starting gdbserver..."), NormalMessageFormat); diff --git a/src/plugins/debugger/debuggerruncontrol.h b/src/plugins/debugger/debuggerruncontrol.h index 4f17a23adad..50d2ed4b986 100644 --- a/src/plugins/debugger/debuggerruncontrol.h +++ b/src/plugins/debugger/debuggerruncontrol.h @@ -161,6 +161,8 @@ public: Utils::Port qmlServerPort() const { return m_qmlServerPort; } QUrl qmlServer() const; + void setDevice(ProjectExplorer::IDevice::ConstPtr device); + private: void start() override; void handlePortListReady(); @@ -170,6 +172,7 @@ private: bool m_useQmlServer = false; Utils::Port m_gdbServerPort; Utils::Port m_qmlServerPort; + ProjectExplorer::IDevice::ConstPtr m_device; }; class DEBUGGER_EXPORT GdbServerRunner : public ProjectExplorer::SimpleTargetRunner @@ -179,12 +182,20 @@ class DEBUGGER_EXPORT GdbServerRunner : public ProjectExplorer::SimpleTargetRunn public: explicit GdbServerRunner(ProjectExplorer::RunControl *runControl, GdbServerPortsGatherer *portsGatherer); + ~GdbServerRunner(); + void setRunnable(const ProjectExplorer::StandardRunnable &runnable); + void setUseMulti(bool on); + void setAttachPid(Utils::ProcessHandle pid); + private: void start() override; GdbServerPortsGatherer *m_portsGatherer; + ProjectExplorer::StandardRunnable m_runnable; + Utils::ProcessHandle m_pid; + bool m_useMulti = true; }; extern DEBUGGER_EXPORT const char GdbServerRunnerWorkerId[]; diff --git a/src/plugins/debugger/gdb/gdb.pri b/src/plugins/debugger/gdb/gdb.pri index 4e0883fa034..c6678d127d0 100644 --- a/src/plugins/debugger/gdb/gdb.pri +++ b/src/plugins/debugger/gdb/gdb.pri @@ -1,8 +1,6 @@ HEADERS += \ - $$PWD/gdbengine.h \ - $$PWD/startgdbserverdialog.h + $$PWD/gdbengine.h SOURCES += \ $$PWD/gdbengine.cpp \ - $$PWD/gdboptionspage.cpp \ - $$PWD/startgdbserverdialog.cpp + $$PWD/gdboptionspage.cpp diff --git a/src/plugins/debugger/gdb/gdbengine.cpp b/src/plugins/debugger/gdb/gdbengine.cpp index f4526977e31..ca46e3e2170 100644 --- a/src/plugins/debugger/gdb/gdbengine.cpp +++ b/src/plugins/debugger/gdb/gdbengine.cpp @@ -538,7 +538,7 @@ void GdbEngine::handleAsyncOutput(const QString &asyncClass, const GdbMi &result // symbols-loaded="0",thread-group="i1" QString id = result["id"].data(); if (!id.isEmpty()) - showStatusMessage(tr("Library %1 loaded").arg(id), 1000); + showStatusMessage(tr("Library %1 loaded.").arg(id), 1000); progressPing(); Module module; module.startAddress = 0; @@ -553,7 +553,7 @@ void GdbEngine::handleAsyncOutput(const QString &asyncClass, const GdbMi &result // host-name="/usr/lib/libdrm.so.2" QString id = result["id"].data(); progressPing(); - showStatusMessage(tr("Library %1 unloaded").arg(id), 1000); + showStatusMessage(tr("Library %1 unloaded.").arg(id), 1000); } else if (asyncClass == "thread-group-added") { // 7.1-symbianelf has "{id="i1"}" } else if (asyncClass == "thread-group-started") { @@ -563,13 +563,13 @@ void GdbEngine::handleAsyncOutput(const QString &asyncClass, const GdbMi &result progressPing(); // 7.1.50 has thread-group-started,id="i1",pid="3529" QString id = result["id"].data(); - showStatusMessage(tr("Thread group %1 created").arg(id), 1000); + showStatusMessage(tr("Thread group %1 created.").arg(id), 1000); notifyInferiorPid(result["pid"].toProcessHandle()); handleThreadGroupCreated(result); } else if (asyncClass == "thread-created") { //"{id="1",group-id="28902"}" QString id = result["id"].data(); - showStatusMessage(tr("Thread %1 created").arg(id), 1000); + showStatusMessage(tr("Thread %1 created.").arg(id), 1000); ThreadData thread; thread.id = ThreadId(id.toLong()); thread.groupId = result["group-id"].data(); @@ -577,18 +577,18 @@ void GdbEngine::handleAsyncOutput(const QString &asyncClass, const GdbMi &result } else if (asyncClass == "thread-group-exited") { // Archer has "{id="28902"}" QString id = result["id"].data(); - showStatusMessage(tr("Thread group %1 exited").arg(id), 1000); + showStatusMessage(tr("Thread group %1 exited.").arg(id), 1000); handleThreadGroupExited(result); } else if (asyncClass == "thread-exited") { //"{id="1",group-id="28902"}" QString id = result["id"].data(); QString groupid = result["group-id"].data(); - showStatusMessage(tr("Thread %1 in group %2 exited") + showStatusMessage(tr("Thread %1 in group %2 exited.") .arg(id).arg(groupid), 1000); threadsHandler()->removeThread(ThreadId(id.toLong())); } else if (asyncClass == "thread-selected") { QString id = result["id"].data(); - showStatusMessage(tr("Thread %1 selected").arg(id), 1000); + showStatusMessage(tr("Thread %1 selected.").arg(id), 1000); //"{id="2"}" } else if (asyncClass == "breakpoint-modified") { // New in FSF gdb since 2011-04-27. @@ -813,7 +813,7 @@ void GdbEngine::runCommand(const DebuggerCommand &command) return; } if (state() == InferiorRunOk) { - showStatusMessage(tr("Stopping temporarily"), 1000); + showStatusMessage(tr("Stopping temporarily."), 1000); m_onStop.append(cmd, wantContinue); requestInterruptInferior(); return; @@ -896,15 +896,15 @@ void GdbEngine::commandTimeout() int timeOut = m_commandTimer.interval(); //m_commandTimer.stop(); const QString msg = tr("The gdb process has not responded " - "to a command within %n second(s). This could mean it is stuck " + "to a command within %n seconds. This could mean it is stuck " "in an endless loop or taking longer than expected to perform " "the operation.\nYou can choose between waiting " "longer or aborting debugging.", 0, timeOut / 1000); QMessageBox *mb = showMessageBox(QMessageBox::Critical, - tr("GDB not responding"), msg, + tr("GDB Not Responding"), msg, QMessageBox::Ok | QMessageBox::Cancel); - mb->button(QMessageBox::Cancel)->setText(tr("Give GDB more time")); - mb->button(QMessageBox::Ok)->setText(tr("Stop debugging")); + mb->button(QMessageBox::Cancel)->setText(tr("Give GDB More Time")); + mb->button(QMessageBox::Ok)->setText(tr("Stop Debugging")); if (mb->exec() == QMessageBox::Ok) { showMessage("KILLING DEBUGGER AS REQUESTED BY USER"); // This is an undefined state, so we just pull the emergency brake. @@ -944,8 +944,8 @@ void GdbEngine::handleResultRecord(DebuggerResponse *response) // with helpers enabled. In this case we get a second response with // msg="Cannot find new threads: generic error" showMessage("APPLYING WORKAROUND #1"); - AsynchronousMessageBox::critical(tr("Executable failed"), msg); - showStatusMessage(tr("Process failed to start")); + AsynchronousMessageBox::critical(tr("Executable Failed"), msg); + showStatusMessage(tr("Process failed to start.")); //shutdown(); notifyInferiorIll(); } else if (msg == "\"finish\" not meaningful in the outermost frame.") { @@ -968,7 +968,7 @@ void GdbEngine::handleResultRecord(DebuggerResponse *response) //notifyInferiorIll(); //showStatusMessage(tr("Executable failed: %1").arg(msg)); //shutdown(); - //AsynchronousMessageBox::critical(tr("Executable failed"), msg); + //AsynchronousMessageBox::critical(tr("Executable Failed"), msg); } else if (msg.contains("Cannot insert breakpoint")) { // For breakpoints set by address to non-existent addresses we // might get something like "6^error,msg="Warning:\nCannot insert @@ -981,7 +981,7 @@ void GdbEngine::handleResultRecord(DebuggerResponse *response) // long as the breakpoints are enabled. // FIXME: Should we silently disable the offending breakpoints? showMessage("APPLYING WORKAROUND #5"); - AsynchronousMessageBox::critical(tr("Setting breakpoints failed"), msg); + AsynchronousMessageBox::critical(tr("Setting Breakpoints Failed"), msg); QTC_CHECK(state() == InferiorRunOk); notifyInferiorSpontaneousStop(); notifyEngineIll(); @@ -1155,7 +1155,7 @@ void GdbEngine::handleExecuteJumpToLine(const DebuggerResponse &response) notifyInferiorRunOk(); // Only needed for gdb < 7.0. } else if (response.resultClass == ResultError) { // Could be "Unreasonable jump request" or similar. - QString out = tr("Cannot jump. Stopped"); + QString out = tr("Cannot jump. Stopped."); QString msg = response.data["msg"].data(); if (!msg.isEmpty()) out += ". " + msg; @@ -1163,7 +1163,7 @@ void GdbEngine::handleExecuteJumpToLine(const DebuggerResponse &response) notifyInferiorRunFailed(); } else if (response.resultClass == ResultDone) { // This happens on old gdb. Trigger the effect of a '*stopped'. - showStatusMessage(tr("Jumped. Stopped")); + showStatusMessage(tr("Jumped. Stopped.")); notifyInferiorSpontaneousStop(); handleStop2(response.data); } @@ -1182,7 +1182,7 @@ void GdbEngine::handleExecuteRunToLine(const DebuggerResponse &response) //>~"testArray () at ../simple/app.cpp:241\n" //>~"241\t s[1] = \"b\";\n" //>122^done - showStatusMessage(tr("Target line hit. Stopped")); + showStatusMessage(tr("Target line hit, and therefore stopped.")); notifyInferiorRunOk(); } } @@ -1230,7 +1230,7 @@ void GdbEngine::handleStopResponse(const GdbMi &data) msg = tr("Application exited after receiving signal %1") .arg(data["signal-name"].toString()); } else { - msg = tr("Application exited normally"); + msg = tr("Application exited normally."); } // Only show the message. Ramp-down will be triggered by -thread-group-exited. showStatusMessage(msg); @@ -1330,11 +1330,12 @@ void GdbEngine::handleStopResponse(const GdbMi &data) notifyInferiorStopOk(); } else if (state() == EngineRunRequested) { // This is gdb 7+'s initial *stopped in response to attach that - // appears before the ^done is seen. + // appears before the ^done is seen for local setups. notifyEngineRunAndInferiorStopOk(); - if (terminal()) + if (terminal()) { continueInferiorInternal(); - return; + return; + } } else { QTC_CHECK(false); } @@ -1742,7 +1743,7 @@ void GdbEngine::handleInferiorShutdown(const DebuggerResponse &response) notifyInferiorShutdownOk(); return; } - AsynchronousMessageBox::critical(tr("Failed to shut down application"), + AsynchronousMessageBox::critical(tr("Failed to Shut Down Application"), msgInferiorStopFailed(msg)); notifyInferiorShutdownFailed(); } @@ -1811,7 +1812,7 @@ void GdbEngine::handleThreadGroupExited(const GdbMi &result) static QString msgNoGdbBinaryForToolChain(const Abi &tc) { - return GdbEngine::tr("There is no GDB binary available for binaries in format \"%1\"") + return GdbEngine::tr("There is no GDB binary available for binaries in format \"%1\".") .arg(tc.toString()); } @@ -3933,8 +3934,8 @@ void GdbEngine::loadInitScript() runCommand({"source " + script}); } else { AsynchronousMessageBox::warning( - tr("Cannot find debugger initialization script"), - tr("The debugger settings point to a script file at \"%1\" " + tr("Cannot Find Debugger Initialization Script"), + tr("The debugger settings point to a script file at \"%1\", " "which is not accessible. If a script file is not needed, " "consider clearing that entry to avoid this warning." ).arg(script)); @@ -4030,7 +4031,7 @@ void GdbEngine::handleAdapterStartFailed(const QString &msg, Id settingsIdHint) CHECK_STATE(EngineSetupOk); showMessage("ADAPTER START FAILED"); if (!msg.isEmpty() && !Internal::isTestRun()) { - const QString title = tr("Adapter start failed"); + const QString title = tr("Adapter Start Failed"); if (!settingsIdHint.isValid()) { ICore::showWarningWithOptions(title, msg); } else { @@ -4125,7 +4126,7 @@ void GdbEngine::notifyInferiorSetupFailedHelper(const QString &msg) return; // Adapter crashed meanwhile, so this notification is meaningless. } showMessage("INFERIOR START FAILED"); - AsynchronousMessageBox::critical(tr("Failed to start application"), msg); + AsynchronousMessageBox::critical(tr("Failed to Start Application"), msg); notifyInferiorSetupFailed(); } @@ -4196,17 +4197,17 @@ QString GdbEngine::msgInferiorStopFailed(const QString &why) QString GdbEngine::msgInferiorSetupOk() { - return tr("Application started"); + return tr("Application started."); } QString GdbEngine::msgInferiorRunOk() { - return tr("Application running"); + return tr("Application running."); } QString GdbEngine::msgAttachedToStoppedInferior() { - return tr("Attached to stopped application"); + return tr("Attached to stopped application."); } QString GdbEngine::msgConnectRemoteServerFailed(const QString &why) @@ -4267,7 +4268,11 @@ void GdbEngine::setupInferior() const DebuggerRunParameters &rp = runParameters(); - if (isAttachEngine()) { + if (rp.startMode == AttachToRemoteProcess) { + + notifyInferiorSetupOk(); + + } else if (isAttachEngine()) { // Task 254674 does not want to remove them //qq->breakHandler()->removeAllBreakpoints(); handleInferiorPrepared(); @@ -4377,7 +4382,6 @@ void GdbEngine::setupInferior() } else if (isPlainEngine()) { setEnvironmentVariables(); - const DebuggerRunParameters &rp = runParameters(); if (!rp.inferior.workingDirectory.isEmpty()) runCommand({"cd " + rp.inferior.workingDirectory}); if (!rp.inferior.commandLineArguments.isEmpty()) { @@ -4395,9 +4399,18 @@ void GdbEngine::runEngine() { CHECK_STATE(EngineRunRequested); - if (isAttachEngine()) { + const DebuggerRunParameters &rp = runParameters(); + + if (rp.startMode == AttachToRemoteProcess) { + + notifyEngineRunAndInferiorStopOk(); - const qint64 pid = runParameters().attachPID.pid(); + QString channel = rp.remoteChannel; + runCommand({"target remote " + channel}); + + } else if (isAttachEngine()) { + + const qint64 pid = rp.attachPID.pid(); showStatusMessage(tr("Attaching to process %1.").arg(pid)); runCommand({"attach " + QString::number(pid), [this](const DebuggerResponse &r) { handleAttach(r); }}); @@ -4454,12 +4467,13 @@ void GdbEngine::handleAttach(const DebuggerResponse &response) // Happens e.g. for "Attach to unstarted application" // We will get a '*stopped' later that we'll interpret as 'spontaneous' // So acknowledge the current state and put a delayed 'continue' in the pipe. - showMessage(tr("Attached to running application"), StatusBar); + showMessage(tr("Attached to running application."), StatusBar); notifyEngineRunAndInferiorRunOk(); } else { // InferiorStopOk, e.g. for "Attach to running application". // The *stopped came in between sending the 'attach' and // receiving its '^done'. + notifyEngineRunAndInferiorStopOk(); if (runParameters().continueAfterAttach) continueInferiorInternal(); } @@ -4528,7 +4542,7 @@ void GdbEngine::interruptInferior2() if (!ok) { // FIXME: Extra state needed? showMessage("NOTE: INFERIOR STOP NOT POSSIBLE"); - showStatusMessage(tr("Interrupting not possible")); + showStatusMessage(tr("Interrupting not possible.")); notifyInferiorRunOk(); } } @@ -4595,11 +4609,11 @@ void GdbEngine::handleFileExecAndSymbols(const DebuggerResponse &response) showMessage(tr("Symbols found."), StatusBar); handleInferiorPrepared(); } else { - QString msg = tr("No symbols found in core file <i>%1</i>.").arg(core) + QString msg = tr("No symbols found in the core file \"%1\".").arg(core) + ' ' + tr("This can be caused by a path length limitation " "in the core file.") - + ' ' + tr("Try to specify the binary using the " - "<i>Debug->Start Debugging->Attach to Core</i> dialog."); + + ' ' + tr("Try to specify the binary in " + "Debug > Start Debugging > Attach to Core."); notifyInferiorSetupFailedHelper(msg); } @@ -4663,6 +4677,7 @@ void GdbEngine::handleSetTargetAsync(const DebuggerResponse &response) void GdbEngine::callTargetRemote() { + CHECK_STATE(InferiorSetupRequested); QString channel = runParameters().remoteChannel; // Don't touch channels with explicitly set protocols. diff --git a/src/plugins/debugger/gdb/startgdbserverdialog.cpp b/src/plugins/debugger/gdb/startgdbserverdialog.cpp deleted file mode 100644 index a9596269ca5..00000000000 --- a/src/plugins/debugger/gdb/startgdbserverdialog.cpp +++ /dev/null @@ -1,234 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "startgdbserverdialog.h" - -#include <debugger/debuggerengine.h> -#include <debugger/debuggermainwindow.h> -#include <debugger/debuggerplugin.h> -#include <debugger/debuggerkitinformation.h> -#include <debugger/debuggerruncontrol.h> - -#include <coreplugin/icore.h> -#include <coreplugin/messagebox.h> -#include <projectexplorer/kitchooser.h> -#include <projectexplorer/devicesupport/deviceprocesslist.h> -#include <projectexplorer/devicesupport/deviceprocessesdialog.h> -#include <projectexplorer/devicesupport/deviceusedportsgatherer.h> -#include <ssh/sshremoteprocessrunner.h> -#include <utils/portlist.h> -#include <utils/qtcassert.h> - -#include <QFileInfo> - -using namespace Core; -using namespace ProjectExplorer; -using namespace QSsh; -using namespace Utils; - - -namespace Debugger { -namespace Internal { - -class StartGdbServerDialogPrivate -{ -public: - StartGdbServerDialogPrivate() : dialog(0), kit(0) {} - - DeviceProcessesDialog *dialog; - bool attachToServer; - DeviceProcessItem process; - Kit *kit; - IDevice::ConstPtr device; - - DeviceUsedPortsGatherer gatherer; - SshRemoteProcessRunner runner; -}; - -GdbServerStarter::GdbServerStarter(DeviceProcessesDialog *dlg, bool attachAfterServerStart) - : QObject(dlg) -{ - d = new StartGdbServerDialogPrivate; - d->dialog = dlg; - d->kit = dlg->kitChooser()->currentKit(); - d->process = dlg->currentProcess(); - d->device = DeviceKitInformation::device(d->kit); - d->attachToServer = attachAfterServerStart; -} - -GdbServerStarter::~GdbServerStarter() -{ - delete d; -} - -void GdbServerStarter::handleRemoteError(const QString &errorMsg) -{ - AsynchronousMessageBox::critical(tr("Remote Error"), errorMsg); -} - -void GdbServerStarter::portGathererError(const QString &text) -{ - logMessage(tr("Could not retrieve list of free ports:")); - logMessage(text); - logMessage(tr("Process aborted")); -} - -void GdbServerStarter::run() -{ - QTC_ASSERT(d->device, return); - connect(&d->gatherer, &DeviceUsedPortsGatherer::error, - this, &GdbServerStarter::portGathererError); - connect(&d->gatherer, &DeviceUsedPortsGatherer::portListReady, - this, &GdbServerStarter::portListReady); - d->gatherer.start(d->device); -} - -void GdbServerStarter::portListReady() -{ - PortList ports = d->device->freePorts(); - const Port port = d->gatherer.getNextFreePort(&ports); - if (!port.isValid()) { - QTC_ASSERT(false, /**/); - emit logMessage(tr("Process aborted")); - return; - } - - connect(&d->runner, &SshRemoteProcessRunner::connectionError, - this, &GdbServerStarter::handleConnectionError); - connect(&d->runner, &SshRemoteProcessRunner::processStarted, - this, &GdbServerStarter::handleProcessStarted); - connect(&d->runner, &SshRemoteProcessRunner::readyReadStandardOutput, - this, &GdbServerStarter::handleProcessOutputAvailable); - connect(&d->runner, &SshRemoteProcessRunner::readyReadStandardError, - this, &GdbServerStarter::handleProcessErrorOutput); - connect(&d->runner, &SshRemoteProcessRunner::processClosed, - this, &GdbServerStarter::handleProcessClosed); - - QByteArray gdbServerPath = d->device->debugServerPath().toUtf8(); - if (gdbServerPath.isEmpty()) - gdbServerPath = "gdbserver"; - QByteArray cmd = gdbServerPath + " --attach :" - + QByteArray::number(port.number()) + ' ' + QByteArray::number(d->process.pid); - logMessage(tr("Running command: %1").arg(QString::fromLatin1(cmd))); - d->runner.run(cmd, d->device->sshParameters()); -} - -void GdbServerStarter::handleConnectionError() -{ - logMessage(tr("Connection error: %1").arg(d->runner.lastConnectionErrorString())); -} - -void GdbServerStarter::handleProcessStarted() -{ - logMessage(tr("Starting gdbserver...")); -} - -void GdbServerStarter::handleProcessOutputAvailable() -{ - logMessage(QString::fromUtf8(d->runner.readAllStandardOutput().trimmed())); -} - -void GdbServerStarter::handleProcessErrorOutput() -{ - const QByteArray ba = d->runner.readAllStandardError(); - logMessage(QString::fromUtf8(ba.trimmed())); - // "Attached; pid = 16740" - // "Listening on port 10000" - foreach (const QByteArray &line, ba.split('\n')) { - if (line.startsWith("Listening on port")) { - const int port = line.mid(18).trimmed().toInt(); - logMessage(tr("Port %1 is now accessible.").arg(port)); - logMessage(tr("Server started on %1:%2") - .arg(d->device->sshParameters().host).arg(port)); - if (d->attachToServer) - attach(port); - } - } -} - -void GdbServerStarter::attach(int port) -{ - QString sysroot = SysRootKitInformation::sysRoot(d->kit).toString(); - QString binary; - QString localExecutable; - QString candidate = sysroot + d->process.exe; - if (QFileInfo::exists(candidate)) - localExecutable = candidate; - if (localExecutable.isEmpty()) { - binary = d->process.cmdLine.section(QLatin1Char(' '), 0, 0); - candidate = sysroot + QLatin1Char('/') + binary; - if (QFileInfo::exists(candidate)) - localExecutable = candidate; - } - if (localExecutable.isEmpty()) { - candidate = sysroot + QLatin1String("/usr/bin/") + binary; - if (QFileInfo::exists(candidate)) - localExecutable = candidate; - } - if (localExecutable.isEmpty()) { - candidate = sysroot + QLatin1String("/bin/") + binary; - if (QFileInfo::exists(candidate)) - localExecutable = candidate; - } - if (localExecutable.isEmpty()) { - AsynchronousMessageBox::warning(tr("Warning"), - tr("Cannot find local executable for remote process \"%1\".") - .arg(d->process.exe)); - return; - } - - QList<Abi> abis = Abi::abisOfBinary(FileName::fromString(localExecutable)); - if (abis.isEmpty()) { - AsynchronousMessageBox::warning(tr("Warning"), - tr("Cannot find ABI for remote process \"%1\".") - .arg(d->process.exe)); - return; - } - - QString remoteChannel = QString("%1:%2").arg(d->device->sshParameters().host).arg(port); - - auto runControl = new RunControl(nullptr, ProjectExplorer::Constants::DEBUG_RUN_MODE); - auto debugger = new DebuggerRunTool(runControl, d->kit); - debugger->setRemoteChannel(remoteChannel); - debugger->setRunControlName(tr("Remote: \"%1\"").arg(remoteChannel)); - debugger->setInferiorExecutable(localExecutable); - debugger->setStartMode(AttachToRemoteServer); - debugger->setCloseMode(KillAtClose); - - debugger->startRunControl(); -} - -void GdbServerStarter::handleProcessClosed(int status) -{ - logMessage(tr("Process gdbserver finished. Status: %1").arg(status)); -} - -void GdbServerStarter::logMessage(const QString &line) -{ - d->dialog->logMessage(line); -} - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/debugger/gdb/startgdbserverdialog.h b/src/plugins/debugger/gdb/startgdbserverdialog.h deleted file mode 100644 index c90cefe7abc..00000000000 --- a/src/plugins/debugger/gdb/startgdbserverdialog.h +++ /dev/null @@ -1,65 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#pragma once - -#include <QObject> - -namespace ProjectExplorer { class DeviceProcessesDialog; } - -namespace Debugger { -namespace Internal { - -class StartGdbServerDialogPrivate; - -class GdbServerStarter : public QObject -{ - Q_OBJECT - -public: - GdbServerStarter(ProjectExplorer::DeviceProcessesDialog *dlg, - bool attachAfterServerStart); - ~GdbServerStarter(); - - void run(); - -private: - void handleRemoteError(const QString &errorMessage); - void portGathererError(const QString &errorMessage); - void portListReady(); - - void handleProcessClosed(int); - void handleProcessErrorOutput(); - void handleProcessOutputAvailable(); - void handleProcessStarted(); - void handleConnectionError(); - - void attach(int port); - void logMessage(const QString &line); - StartGdbServerDialogPrivate *d; -}; - -} // namespace Internal -} // namespace Debugger diff --git a/src/plugins/genericprojectmanager/genericbuildconfiguration.cpp b/src/plugins/genericprojectmanager/genericbuildconfiguration.cpp index 461232e4c87..cc20c2e8608 100644 --- a/src/plugins/genericprojectmanager/genericbuildconfiguration.cpp +++ b/src/plugins/genericprojectmanager/genericbuildconfiguration.cpp @@ -37,6 +37,8 @@ #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> #include <projectexplorer/toolchain.h> +#include <qtsupport/baseqtversion.h> +#include <qtsupport/qtkitinformation.h> #include <utils/mimetypes/mimedatabase.h> #include <utils/pathchooser.h> @@ -196,6 +198,14 @@ BuildConfiguration::BuildType GenericBuildConfiguration::buildType() const return Unknown; } +void GenericBuildConfiguration::addToEnvironment(Utils::Environment &env) const +{ + prependCompilerPathToEnvironment(env); + const QtSupport::BaseQtVersion *qt = QtSupport::QtKitInformation::qtVersion(target()->kit()); + if (qt) + env.prependOrSetPath(qt->binPath().toString()); +} + //////////////////////////////////////////////////////////////////////////////////// // GenericBuildSettingsWidget //////////////////////////////////////////////////////////////////////////////////// diff --git a/src/plugins/genericprojectmanager/genericbuildconfiguration.h b/src/plugins/genericprojectmanager/genericbuildconfiguration.h index 75f5ceef5d4..54e375e88f7 100644 --- a/src/plugins/genericprojectmanager/genericbuildconfiguration.h +++ b/src/plugins/genericprojectmanager/genericbuildconfiguration.h @@ -52,6 +52,8 @@ public: BuildType buildType() const override; + void addToEnvironment(Utils::Environment &env) const final; + protected: GenericBuildConfiguration(ProjectExplorer::Target *parent, GenericBuildConfiguration *source); GenericBuildConfiguration(ProjectExplorer::Target *parent, Core::Id id); diff --git a/src/plugins/projectexplorer/buildconfiguration.cpp b/src/plugins/projectexplorer/buildconfiguration.cpp index 705d9b9b967..bd2bbaac51d 100644 --- a/src/plugins/projectexplorer/buildconfiguration.cpp +++ b/src/plugins/projectexplorer/buildconfiguration.cpp @@ -33,7 +33,10 @@ #include "kit.h" #include <projectexplorer/buildenvironmentwidget.h> +#include <projectexplorer/kitinformation.h> +#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectmacroexpander.h> +#include <projectexplorer/target.h> #include <extensionsystem/pluginmanager.h> #include <coreplugin/idocument.h> @@ -66,7 +69,7 @@ BuildConfiguration::BuildConfiguration(Target *target, Core::Id id) : bsl->setDefaultDisplayName(tr("Clean")); m_stepLists.append(bsl); - emitEnvironmentChanged(); + updateCacheAndEmitEnvironmentChanged(); connect(target, &Target::kitChanged, this, &BuildConfiguration::handleKitUpdate); @@ -88,7 +91,7 @@ BuildConfiguration::BuildConfiguration(Target *target, BuildConfiguration *sourc // otherwise BuildStepFactories might reject to set up a BuildStep for us // since we are not yet the derived class! - emitEnvironmentChanged(); + updateCacheAndEmitEnvironmentChanged(); connect(target, &Target::kitChanged, this, &BuildConfiguration::handleKitUpdate); @@ -168,7 +171,7 @@ bool BuildConfiguration::fromMap(const QVariantMap &map) m_userEnvironmentChanges = Utils::EnvironmentItem::fromStringList(map.value(QLatin1String(USER_ENVIRONMENT_CHANGES_KEY)).toStringList()); m_buildDirectory = Utils::FileName::fromString(map.value(QLatin1String(BUILDDIRECTORY_KEY)).toString()); - emitEnvironmentChanged(); + updateCacheAndEmitEnvironmentChanged(); qDeleteAll(m_stepLists); m_stepLists.clear(); @@ -200,7 +203,7 @@ bool BuildConfiguration::fromMap(const QVariantMap &map) return ProjectConfiguration::fromMap(map); } -void BuildConfiguration::emitEnvironmentChanged() +void BuildConfiguration::updateCacheAndEmitEnvironmentChanged() { Utils::Environment env = baseEnvironment(); env.modify(userEnvironmentChanges()); @@ -212,7 +215,7 @@ void BuildConfiguration::emitEnvironmentChanged() void BuildConfiguration::handleKitUpdate() { - emitEnvironmentChanged(); + updateCacheAndEmitEnvironmentChanged(); } void BuildConfiguration::emitBuildDirectoryChanged() @@ -238,8 +241,8 @@ Utils::Environment BuildConfiguration::baseEnvironment() const Utils::Environment result; if (useSystemEnvironment()) result = Utils::Environment::systemEnvironment(); - target()->kit()->addToEnvironment(result); addToEnvironment(result); + target()->kit()->addToEnvironment(result); return result; } @@ -261,7 +264,7 @@ void BuildConfiguration::setUseSystemEnvironment(bool b) if (useSystemEnvironment() == b) return; m_clearSystemEnvironment = !b; - emitEnvironmentChanged(); + updateCacheAndEmitEnvironmentChanged(); } void BuildConfiguration::addToEnvironment(Utils::Environment &env) const @@ -284,7 +287,7 @@ void BuildConfiguration::setUserEnvironmentChanges(const QList<Utils::Environmen if (m_userEnvironmentChanges == diff) return; m_userEnvironmentChanges = diff; - emitEnvironmentChanged(); + updateCacheAndEmitEnvironmentChanged(); } void BuildConfiguration::cloneSteps(BuildConfiguration *source) @@ -330,6 +333,25 @@ bool BuildConfiguration::isActive() const return target()->isActive() && target()->activeBuildConfiguration() == this; } +/*! + * Helper function that prepends the directory containing the C++ toolchain to + * PATH. This is used to in build configurations targeting broken build systems + * to provide hints about which compiler to use. + */ +void BuildConfiguration::prependCompilerPathToEnvironment(Utils::Environment &env) const +{ + const ToolChain *tc + = ToolChainKitInformation::toolChain(target()->kit(), + ProjectExplorer::Constants::CXX_LANGUAGE_ID); + + if (!tc) + return; + + const Utils::FileName compilerDir = tc->compilerCommand().parentDir(); + if (!compilerDir.isEmpty()) + env.prependOrSetPath(compilerDir.toString()); +} + /// // IBuildConfigurationFactory /// diff --git a/src/plugins/projectexplorer/buildconfiguration.h b/src/plugins/projectexplorer/buildconfiguration.h index cc30d2ba44d..109cc4481ef 100644 --- a/src/plugins/projectexplorer/buildconfiguration.h +++ b/src/plugins/projectexplorer/buildconfiguration.h @@ -90,6 +90,8 @@ public: bool isActive() const override; + void prependCompilerPathToEnvironment(Utils::Environment &env) const; + signals: void environmentChanged(); void buildDirectoryChanged(); @@ -101,7 +103,7 @@ protected: BuildConfiguration(Target *target, BuildConfiguration *source); void cloneSteps(BuildConfiguration *source); - void emitEnvironmentChanged(); + void updateCacheAndEmitEnvironmentChanged(); private: void handleKitUpdate(); diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index 884798ecf5e..2ed1a397e27 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -710,15 +710,16 @@ QList<HeaderPath> GccToolChain::systemHeaderPaths(const QStringList &cxxflags, void GccToolChain::addCommandPathToEnvironment(const FileName &command, Environment &env) { - if (!command.isEmpty()) - env.prependOrSetPath(command.parentDir().toString()); + const Utils::FileName compilerDir = command.parentDir(); + if (!compilerDir.isEmpty()) + env.prependOrSetPath(compilerDir.toString()); } GccToolChain::GccToolChain(const GccToolChain &) = default; void GccToolChain::addToEnvironment(Environment &env) const { - addCommandPathToEnvironment(m_compilerCommand, env); + Q_UNUSED(env); } FileNameList GccToolChain::suggestedMkspecList() const diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 679214925b3..3cc3625dfa7 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -2045,7 +2045,13 @@ void ProjectExplorerPluginPrivate::executeRunConfiguration(RunConfiguration *run QTC_ASSERT(producer, return); auto runControl = new RunControl(runConfiguration, runMode); - (void) producer(runControl); + + // A user needed interaction may have cancelled the run + // (by example asking for a process pid or server url). + if (!producer(runControl)) { + delete runControl; + return; + } emit m_instance->aboutToExecuteProject(runConfiguration->target()->project(), runMode); diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp index 9e3d3339c7b..b56f1d683ea 100644 --- a/src/plugins/projectexplorer/runconfiguration.cpp +++ b/src/plugins/projectexplorer/runconfiguration.cpp @@ -1384,24 +1384,14 @@ bool Runnable::canReUseOutputPane(const Runnable &other) const } -// FIXME: Remove once ApplicationLauncher signalling does not depend on device. -static bool isSynchronousLauncher(RunControl *runControl) -{ - RunConfiguration *runConfig = runControl->runConfiguration(); - Target *target = runConfig ? runConfig->target() : nullptr; - Kit *kit = target ? target->kit() : nullptr; - Core::Id deviceId = DeviceTypeKitInformation::deviceTypeId(kit); - return !deviceId.isValid() || deviceId == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE; -} - - // SimpleTargetRunner SimpleTargetRunner::SimpleTargetRunner(RunControl *runControl) : RunWorker(runControl) { setDisplayName("SimpleTargetRunner"); - m_runnable = runControl->runnable(); + m_runnable = runControl->runnable(); // Default value. Can be overridden using setRunnable. + m_device = runControl->device(); // Default value. Can be overridden using setDevice. } void SimpleTargetRunner::start() @@ -1409,7 +1399,8 @@ void SimpleTargetRunner::start() m_stopReported = false; m_launcher.disconnect(this); - const bool isDesktop = isSynchronousLauncher(runControl()); + const bool isDesktop = m_device.isNull() + || m_device->type() == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE; const QString rawDisplayName = m_runnable.displayName(); const QString displayName = isDesktop ? QDir::toNativeSeparators(rawDisplayName) @@ -1528,11 +1519,21 @@ void SimpleTargetRunner::onProcessError(QProcess::ProcessError error) } } +IDevice::ConstPtr SimpleTargetRunner::device() const +{ + return m_device; +} + void SimpleTargetRunner::setRunnable(const Runnable &runnable) { m_runnable = runnable; } +void SimpleTargetRunner::setDevice(const IDevice::ConstPtr &device) +{ + m_device = device; +} + // RunWorkerPrivate RunWorkerPrivate::RunWorkerPrivate(RunWorker *runWorker, RunControl *runControl) diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h index f938f3a93d8..ebbf557d2a6 100644 --- a/src/plugins/projectexplorer/runconfiguration.h +++ b/src/plugins/projectexplorer/runconfiguration.h @@ -522,6 +522,9 @@ public: void setRunnable(const Runnable &runnable); + void setDevice(const IDevice::ConstPtr &device); + IDevice::ConstPtr device() const; + protected: void start() override; void stop() override; @@ -533,6 +536,7 @@ private: ApplicationLauncher m_launcher; Runnable m_runnable; + IDevice::ConstPtr m_device; bool m_stopReported = false; }; diff --git a/src/plugins/pythoneditor/pythoneditorplugin.cpp b/src/plugins/pythoneditor/pythoneditorplugin.cpp index 29f55ad94dd..9290a2cd989 100644 --- a/src/plugins/pythoneditor/pythoneditorplugin.cpp +++ b/src/plugins/pythoneditor/pythoneditorplugin.cpp @@ -312,7 +312,10 @@ public: if (!canHandle(parent)) return false; PythonProject *project = static_cast<PythonProject *>(parent->project()); - return project->files(ProjectExplorer::Project::AllFiles).contains(scriptFromId(id)); + const QString script = scriptFromId(id); + if (script.endsWith(".pyqtc")) + return false; + return project->files(ProjectExplorer::Project::AllFiles).contains(script); } bool canRestore(Target *parent, const QVariantMap &map) const override @@ -475,8 +478,9 @@ void PythonProject::parseProject() class PythonFileNode : public FileNode { public: - PythonFileNode(const Utils::FileName &filePath, const QString &nodeDisplayName) - : FileNode(filePath, FileType::Source, false) + PythonFileNode(const Utils::FileName &filePath, const QString &nodeDisplayName, + FileType fileType = FileType::Source) + : FileNode(filePath, fileType, false) , m_displayName(nodeDisplayName) {} @@ -494,7 +498,8 @@ void PythonProject::refresh() auto newRoot = new PythonProjectNode(this); for (const QString &f : m_files) { const QString displayName = baseDir.relativeFilePath(f); - newRoot->addNestedNode(new PythonFileNode(FileName::fromString(f), displayName)); + FileType fileType = f.endsWith(".pyqtc") ? FileType::Project : FileType::Source; + newRoot->addNestedNode(new PythonFileNode(FileName::fromString(f), displayName, fileType)); } setRootProjectNode(newRoot); @@ -560,31 +565,11 @@ Project::RestoreResult PythonProject::fromMap(const QVariantMap &map, QString *e { Project::RestoreResult res = Project::fromMap(map, errorMessage); if (res == RestoreResult::Ok) { + refresh(); + Kit *defaultKit = KitManager::defaultKit(); if (!activeTarget() && defaultKit) addTarget(createTarget(defaultKit)); - - refresh(); - - QList<Target *> targetList = targets(); - foreach (Target *t, targetList) { - const QList<RunConfiguration *> runConfigs = t->runConfigurations(); - foreach (const QString &file, m_files) { - // skip the 'project' file - if (file.endsWith(".pyqtc")) - continue; - const Id id = idFromScript(file); - bool alreadyPresent = false; - foreach (RunConfiguration *runCfg, runConfigs) { - if (runCfg->id() == id) { - alreadyPresent = true; - break; - } - } - if (!alreadyPresent) - t->addRunConfiguration(IRunConfigurationFactory::createHelper<PythonRunConfiguration>(t, id)); - } - } } return res; diff --git a/src/plugins/qmakeandroidsupport/androidqmakebuildconfigurationfactory.cpp b/src/plugins/qmakeandroidsupport/androidqmakebuildconfigurationfactory.cpp index 805624c95f6..1de61ae19f1 100644 --- a/src/plugins/qmakeandroidsupport/androidqmakebuildconfigurationfactory.cpp +++ b/src/plugins/qmakeandroidsupport/androidqmakebuildconfigurationfactory.cpp @@ -129,7 +129,7 @@ void AndroidQmakeBuildConfiguration::manifestSaved() if (m_androidNdkPlatform == androidNdkPlatform) return; - emitEnvironmentChanged(); + updateCacheAndEmitEnvironmentChanged(); QMakeStep *qs = qmakeStep(); if (!qs) diff --git a/src/plugins/qmakeandroidsupport/androidqmakebuildconfigurationfactory.h b/src/plugins/qmakeandroidsupport/androidqmakebuildconfigurationfactory.h index 25783a05cb8..55e68bf7196 100644 --- a/src/plugins/qmakeandroidsupport/androidqmakebuildconfigurationfactory.h +++ b/src/plugins/qmakeandroidsupport/androidqmakebuildconfigurationfactory.h @@ -57,7 +57,7 @@ public: void addToEnvironment(Utils::Environment &env) const override; void manifestSaved(); - using BuildConfiguration::emitEnvironmentChanged; + using BuildConfiguration::updateCacheAndEmitEnvironmentChanged; private: mutable QString m_androidNdkPlatform; }; diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp index e95fcdd86ca..ec014c8fa9a 100644 --- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp +++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.cpp @@ -756,6 +756,14 @@ BuildConfiguration::BuildType QmakeBuildConfiguration::buildType() const return Release; } +void QmakeBuildConfiguration::addToEnvironment(Environment &env) const +{ + prependCompilerPathToEnvironment(env); + const BaseQtVersion *qt = QtKitInformation::qtVersion(target()->kit()); + if (qt) + env.prependOrSetPath(qt->binPath().toString()); +} + QmakeBuildConfiguration::LastKitState::LastKitState() { } QmakeBuildConfiguration::LastKitState::LastKitState(Kit *k) diff --git a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h index c526760c62e..87afc6c9376 100644 --- a/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h +++ b/src/plugins/qmakeprojectmanager/qmakebuildconfiguration.h @@ -99,6 +99,8 @@ public: BuildType buildType() const override; + void addToEnvironment(Utils::Environment &env) const override; + void emitProFileEvaluateNeeded(); signals: diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp index 881903d3c04..bfa6b2b0d53 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.cpp @@ -160,6 +160,11 @@ const DesignerActionManager &DesignerActionManagerView::designerActionManager() return m_designerActionManager; } +void DesignerActionManagerView::emitSelectionChanged() +{ + emit selectionChanged(!selectedModelNodes().isEmpty(), singleSelectedModelNode().isRootNode()); +} + /* We should consider compressing this. */ /* One update every 100ms should be enough. */ void DesignerActionManagerView::setupContext() diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h index fd705120c5f..e1e926639f6 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanagerview.h @@ -68,6 +68,7 @@ public: void setDesignerActionList(const QList<ActionInterface* > &designerActionList); DesignerActionManager &designerActionManager(); const DesignerActionManager &designerActionManager() const; + void emitSelectionChanged(); signals: void selectionChanged(bool itemsSelected, bool rootItemIsSelected); diff --git a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp index 52d548c7ba9..d1afece6d02 100644 --- a/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp +++ b/src/plugins/qmldesigner/components/formeditor/formeditorgraphicsview.cpp @@ -144,9 +144,9 @@ void FormEditorGraphicsView::keyReleaseEvent(QKeyEvent *event) void FormEditorGraphicsView::paintEvent(QPaintEvent *event) { - if (!m_blockPainting) { - QGraphicsView::paintEvent(event); - } else { + QGraphicsView::paintEvent(event); + + if (m_blockPainting) { QWidget::paintEvent(event); QPainter painter(viewport()); painter.drawPixmap(0, 0, m_lastUpdate); diff --git a/src/plugins/qmldesigner/components/integration/designdocument.cpp b/src/plugins/qmldesigner/components/integration/designdocument.cpp index 822156f53a8..f83af521cb8 100644 --- a/src/plugins/qmldesigner/components/integration/designdocument.cpp +++ b/src/plugins/qmldesigner/components/integration/designdocument.cpp @@ -45,6 +45,7 @@ #include <qtsupport/qtsupportconstants.h> #include <qtsupport/qtversionmanager.h> #include <coreplugin/idocument.h> +#include <coreplugin/editormanager/editormanager.h> #include <qmljs/qmljsmodelmanagerinterface.h> @@ -605,6 +606,13 @@ RewriterView *DesignDocument::rewriterView() const void DesignDocument::setEditor(Core::IEditor *editor) { m_textEditor = editor; + // if the user closed the file explicit we do not want to do anything with it anymore + connect(Core::EditorManager::instance(), &Core::EditorManager::editorAboutToClose, + this, [this](Core::IEditor *editor) { + if (m_textEditor.data() == editor) + m_textEditor.clear(); + }); + connect(editor->document(), &Core::IDocument::filePathChanged, this, &DesignDocument::updateFileName); diff --git a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp index a38a6b93aff..4cad2d08cc0 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp +++ b/src/plugins/qmldesigner/components/propertyeditor/propertyeditorview.cpp @@ -194,7 +194,8 @@ void PropertyEditorView::changeValue(const QString &name) if (qmlObjectNode.modelNode().metaInfo().propertyTypeName(propertyName) == "QUrl" || qmlObjectNode.modelNode().metaInfo().propertyTypeName(propertyName) == "url") { //turn absolute local file paths into relative paths QString filePath = castedValue.toUrl().toString(); - if (QFileInfo(filePath).exists() && QFileInfo(filePath).isAbsolute()) { + QFileInfo fi(filePath); + if (fi.exists() && fi.isAbsolute()) { QDir fileDir(QFileInfo(model()->fileUrl().toLocalFile()).absolutePath()); castedValue = QUrl(fileDir.relativeFilePath(filePath)); } diff --git a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp index dc080a2577a..60e5ed419bd 100644 --- a/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp +++ b/src/plugins/qmldesigner/components/stateseditor/stateseditorwidget.cpp @@ -133,7 +133,7 @@ void StatesEditorWidget::reloadQmlSource() setSource(QUrl::fromLocalFile(statesListQmlFilePath)); if (!rootObject()) { - Core::AsynchronousMessageBox::warning(tr("Cannot create QtQuick View"), + Core::AsynchronousMessageBox::warning(tr("Cannot Create QtQuick View"), tr("StatesEditorWidget: %1 cannot be created. " "Most likely QtQuick.Controls 1 are not installed.").arg(qmlSourcesPath())); return; diff --git a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp index e3e16e95d42..31fb93a03d8 100644 --- a/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp +++ b/src/plugins/qmldesigner/components/texteditor/texteditorview.cpp @@ -118,7 +118,11 @@ void TextEditorView::modelAboutToBeDetached(Model *model) m_widget->setTextEditor(0); - QmlDesignerPlugin::instance()->emitCurrentTextEditorChanged(QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor()); + // in case the user closed it explicit we do not want to do anything with the editor + if (TextEditor::BaseTextEditor *textEditor = + QmlDesignerPlugin::instance()->currentDesignDocument()->textEditor()) { + QmlDesignerPlugin::instance()->emitCurrentTextEditorChanged(textEditor); + } } void TextEditorView::importsChanged(const QList<Import> &/*addedImports*/, const QList<Import> &/*removedImports*/) diff --git a/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframes.h b/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframes.h index 2969d4dd9a9..06d3565fa28 100644 --- a/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframes.h +++ b/src/plugins/qmldesigner/designercore/include/qmltimelinekeyframes.h @@ -58,6 +58,11 @@ public: bool hasKeyframe(qreal frame); + qreal minActualFrame() const; + qreal maxActualFrame() const; + + const QList<ModelNode> keyframePositions() const; + static bool isValidKeyframe(const ModelNode &node); static QmlTimelineFrames keyframesForKeyframe(const ModelNode &node); }; diff --git a/src/plugins/qmldesigner/designercore/include/qmltimelinemutator.h b/src/plugins/qmldesigner/designercore/include/qmltimelinemutator.h index 10e00fc7b33..b1baf7d7fd6 100644 --- a/src/plugins/qmldesigner/designercore/include/qmltimelinemutator.h +++ b/src/plugins/qmldesigner/designercore/include/qmltimelinemutator.h @@ -52,6 +52,13 @@ public: qreal startFrame() const; qreal endFrame() const; qreal currentFrame() const; + qreal duration() const; + + qreal minActualFrame() const; + qreal maxActualFrame() const; + + QList<ModelNode> allTargets() const; + QList<QmlTimelineFrames> framesForTarget(const ModelNode &target) const; private: void addFramesIfNotExists(const ModelNode &node, const PropertyName &propertyName); diff --git a/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp b/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp index 58cbd68315a..aca95350555 100644 --- a/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp +++ b/src/plugins/qmldesigner/designercore/metainfo/metainfo.cpp @@ -187,6 +187,8 @@ void MetaInfo::clearGlobal() void MetaInfo::setPluginPaths(const QStringList &paths) { s_pluginDirs = paths; + global(); + clearGlobal(); } bool MetaInfo::isGlobal() const diff --git a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframes.cpp b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframes.cpp index 959f2ea3f66..b2cab942d33 100644 --- a/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframes.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmltimelinekeyframes.cpp @@ -34,6 +34,8 @@ #include <utils/qtcassert.h> +#include <limits> + namespace QmlDesigner { QmlTimelineFrames::QmlTimelineFrames() @@ -126,6 +128,42 @@ bool QmlTimelineFrames::hasKeyframe(qreal frame) return false; } +qreal QmlTimelineFrames::minActualFrame() const +{ + qreal min = std::numeric_limits<double>::max(); + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + QVariant value = childNode.variantProperty("frame").value(); + if (value.isValid() && value.toReal() < min) + min = value.toReal(); + } + + return min; +} + +qreal QmlTimelineFrames::maxActualFrame() const +{ + qreal max = std::numeric_limits<double>::min(); + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + QVariant value = childNode.variantProperty("frame").value(); + if (value.isValid() && value.toReal() > max) + max = value.toReal(); + } + + return max; +} + +const QList<ModelNode> QmlTimelineFrames::keyframePositions() const +{ + QList<ModelNode> returnValues; + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + QVariant value = childNode.variantProperty("frame").value(); + if (value.isValid()) + returnValues.append(childNode); + } + + return returnValues; +} + bool QmlTimelineFrames::isValidKeyframe(const ModelNode &node) { return isValidQmlModelNodeFacade(node) diff --git a/src/plugins/qmldesigner/designercore/model/qmltimelinemutator.cpp b/src/plugins/qmldesigner/designercore/model/qmltimelinemutator.cpp index a028b61890d..201650020ca 100644 --- a/src/plugins/qmldesigner/designercore/model/qmltimelinemutator.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmltimelinemutator.cpp @@ -36,6 +36,8 @@ #include <utils/qtcassert.h> +#include <limits> + namespace QmlDesigner { QmlTimelineMutator::QmlTimelineMutator() @@ -123,6 +125,73 @@ qreal QmlTimelineMutator::currentFrame() const return 0; } +qreal QmlTimelineMutator::duration() const +{ + return endFrame() - startFrame(); +} + +qreal QmlTimelineMutator::minActualFrame() const +{ + qreal min = std::numeric_limits<double>::max(); + + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + if (QmlTimelineFrames::isValidQmlTimelineFrames(childNode)) { + QmlTimelineFrames frames(childNode); + qreal value = frames.minActualFrame(); + if (value < min) + min = value; + } + } + + return min; +} + +qreal QmlTimelineMutator::maxActualFrame() const +{ + qreal max = std::numeric_limits<double>::min(); + + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + if (QmlTimelineFrames::isValidQmlTimelineFrames(childNode)) { + QmlTimelineFrames frames(childNode); + qreal value = frames.maxActualFrame(); + if (value > max) + max = value; + } + } + + return max; +} + +QList<ModelNode> QmlTimelineMutator::allTargets() const +{ + QList<ModelNode> result; + if (isValid()) { + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + if (QmlTimelineFrames::isValidQmlTimelineFrames(childNode)) { + const QmlTimelineFrames frames(childNode); + if (!result.contains(frames.target())) + result.append(frames.target()); + } + } + } + return result; +} + +QList<QmlTimelineFrames> QmlTimelineMutator::framesForTarget(const ModelNode &target) const +{ + QList<QmlTimelineFrames> result; + if (isValid()) { + for (const ModelNode &childNode : modelNode().defaultNodeListProperty().toModelNodeList()) { + if (QmlTimelineFrames::isValidQmlTimelineFrames(childNode)) { + const QmlTimelineFrames frames(childNode); + if (frames.target() == target) + result.append(frames); + } + } + } + return result; +} + void QmlTimelineMutator::addFramesIfNotExists(const ModelNode &node, const PropertyName &propertyName) { if (!isValid()) diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 3980fe7990e..d8ea45f1245 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -413,7 +413,6 @@ void QmlDesignerPlugin::deactivateAutoSynchronization() viewManager().detachViewsExceptRewriterAndComponetView(); viewManager().detachComponentView(); viewManager().detachRewriterView(); - emitCurrentTextEditorChanged(documentManager().currentDesignDocument()->textEditor()); documentManager().currentDesignDocument()->resetToDocumentModel(); } diff --git a/src/plugins/qmldesigner/shortcutmanager.cpp b/src/plugins/qmldesigner/shortcutmanager.cpp index 8b45efc8535..a6fd4fcf3b0 100644 --- a/src/plugins/qmldesigner/shortcutmanager.cpp +++ b/src/plugins/qmldesigner/shortcutmanager.cpp @@ -248,12 +248,15 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex m_copyAction.setEnabled(itemsSelected); }); - connect(Core::ICore::instance(), &Core::ICore::contextChanged, this, [this](const Core::Context &context){ + connect(Core::ICore::instance(), &Core::ICore::contextChanged, this, [&designerActionManager, this](const Core::Context &context){ if (!context.contains(Constants::C_QMLFORMEDITOR) && !context.contains(Constants::C_QMLNAVIGATOR)) { m_deleteAction.setEnabled(false); m_cutAction.setEnabled(false); m_copyAction.setEnabled(false); m_pasteAction.setEnabled(false); + } else { + designerActionManager.view()->emitSelectionChanged(); + } }); diff --git a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp index 2a3c81f26e8..9b98d2f13cb 100644 --- a/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp +++ b/src/plugins/qmlprojectmanager/qmlprojectrunconfiguration.cpp @@ -106,7 +106,7 @@ QString QmlProjectRunConfiguration::disabledReason() const { if (mainScript().isEmpty()) return tr("No script file to execute."); - if (!QFileInfo(executable()).exists()) + if (!QFileInfo::exists(executable())) return tr("No qmlviewer or qmlscene found."); return RunConfiguration::disabledReason(); } diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index 0b2f55f2bbc..6f38c5deedc 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -1374,7 +1374,6 @@ void BaseQtVersion::addToEnvironment(const Kit *k, Environment &env) const { Q_UNUSED(k); env.set(QLatin1String("QTDIR"), QDir::toNativeSeparators(qmakeProperty("QT_HOST_DATA"))); - env.prependOrSetPath(qmakeProperty("QT_HOST_BINS")); } // Some Qt versions may require environment settings for qmake to work diff --git a/src/tools/qml2puppet/qml2puppet/qml2puppet.pro b/src/tools/qml2puppet/qml2puppet/qml2puppet.pro index af8591e8ec6..9644cf4bca4 100644 --- a/src/tools/qml2puppet/qml2puppet/qml2puppet.pro +++ b/src/tools/qml2puppet/qml2puppet/qml2puppet.pro @@ -1,7 +1,7 @@ TARGET = qml2puppet TEMPLATE = app -QTC_LIB_DEPENDS += utils + include(../../../../qtcreator.pri) osx: DESTDIR = $$IDE_LIBEXEC_PATH/qmldesigner @@ -9,7 +9,6 @@ else: DESTDIR = $$IDE_LIBEXEC_PATH include(../../../rpath.pri) -include(../../../libs/qt-breakpad/qtbreakpad.pri) include(../../../../share/qtcreator/qml/qmlpuppet/qml2puppet/qml2puppet.pri) isEmpty(PRECOMPILED_HEADER):PRECOMPILED_HEADER = $$PWD/../../../shared/qtcreator_pch.h diff --git a/tests/system/shared/editor_utils.py b/tests/system/shared/editor_utils.py index e5a652aae96..1730fed46c0 100644 --- a/tests/system/shared/editor_utils.py +++ b/tests/system/shared/editor_utils.py @@ -423,3 +423,14 @@ def replaceLine(fileSpec, oldLine, newLine): type(editor, "<Backspace>") type(editor, newLine) return True + +def addTestableCodeAfterLine(editorObject, line, newCodeLines): + if not placeCursorToLine(editorObject, line): + return False + type(editorObject, "<Return>") + typeLines(editorObject, newCodeLines) + return True + +def saveAndExit(): + invokeMenuItem("File", "Save All") + invokeMenuItem("File", "Exit") diff --git a/tests/system/suite_HELP/tst_HELP05/test.py b/tests/system/suite_HELP/tst_HELP05/test.py index 1acfac0b542..2b84e6d7afc 100755 --- a/tests/system/suite_HELP/tst_HELP05/test.py +++ b/tests/system/suite_HELP/tst_HELP05/test.py @@ -37,10 +37,12 @@ def verifyInteractiveQMLHelp(lineText, helpText): type(editorArea, homeKey) else: type(editorArea, homeKey) + snooze(1) # call help type(editorArea, "<F1>") - test.verify(helpText in getHelpTitle(), - "Verifying if help is opened with documentation for '%s'." % helpText) + test.verify(waitFor('helpText in getHelpTitle()', 1000), + "Verifying if help is opened with documentation for '%s'.\nHelp title: %s" + % (helpText, getHelpTitle())) def main(): startApplication("qtcreator" + SettingsPath) @@ -52,10 +54,15 @@ def main(): addHelpDocumentation(qchs) # create qt quick application createNewQtQuickApplication(tempDir(), "SampleApp") + editorArea = waitForObject(":Qt Creator_QmlJSEditor::QmlJSTextEditorWidget") + # add basic MouseArea item to check it afterwards + codelines = ['MouseArea {', 'anchors.fill: parent', 'onClicked: Qt.quit()'] + if not addTestableCodeAfterLine(editorArea, 'title: qsTr("Hello World")', codelines): + saveAndExit() + return + invokeMenuItem("File", "Save All") # verify Rectangle help verifyInteractiveQMLHelp("Window {", "Window QML Type") - # go back to edit mode - switchViewTo(ViewConstants.EDIT) # verify MouseArea help verifyInteractiveQMLHelp("MouseArea {", "MouseArea QML Type") # exit diff --git a/tests/system/suite_QMLS/tst_QMLS01/test.py b/tests/system/suite_QMLS/tst_QMLS01/test.py index cb8f6447a2a..434b1ae47ea 100644 --- a/tests/system/suite_QMLS/tst_QMLS01/test.py +++ b/tests/system/suite_QMLS/tst_QMLS01/test.py @@ -108,13 +108,16 @@ def testSuggestionsManual(lineText, textToType, expectedText): __endTestSuggestions__(editorArea) return True -def saveAndExit(): - invokeMenuItem("File", "Save All") - invokeMenuItem("File", "Exit") - def main(): if not startQtCreatorWithNewAppAtQMLEditor(tempDir(), "SampleApp"): return + # add basic TextEdit item to check it afterwards + codelines = ['TextEdit {', 'text: "Enter something"', 'anchors.top: parent.top', + 'anchors.horizontalCenter: parent.horizontalCenter', 'anchors.topMargin: 20'] + editor = waitForObject(":Qt Creator_QmlJSEditor::QmlJSTextEditorWidget") + if not addTestableCodeAfterLine(editor, 'title: qsTr("Hello World")', codelines): + saveAndExit() + return # test "color: " suggestion usage with Enter key if not testSuggestionsAuto("TextEdit {", "col", "color:", "<Return>"): saveAndExit() @@ -123,11 +126,11 @@ def main(): if not testSuggestionsAuto("TextEdit {", "col", "color:", "<Tab>"): saveAndExit() return - # test "textChanged: " suggestion - automatic insert, because only one suggestion available + # test automatic insertion (prerequisite: only one suggestion available) shortcutToSuggestions = "<Ctrl+Space>" if platform.system() == "Darwin": shortcutToSuggestions = "<Meta+Space>" - if not testSuggestionsAuto("TextEdit {","baseu", "baseUrl:", shortcutToSuggestions): + if not testSuggestionsAuto("TextEdit {", "online", "onLineCountChanged:", shortcutToSuggestions): saveAndExit() return # change settings to manual insertion of suggestions diff --git a/tests/system/suite_QMLS/tst_QMLS02/test.py b/tests/system/suite_QMLS/tst_QMLS02/test.py index 78101fc08fe..687f53a53f5 100644 --- a/tests/system/suite_QMLS/tst_QMLS02/test.py +++ b/tests/system/suite_QMLS/tst_QMLS02/test.py @@ -27,9 +27,15 @@ source("../shared/qmls.py") source("../../shared/suites_qtta.py") def main(): - editorArea = startQtCreatorWithNewAppAtQMLEditor(tempDir(), "SampleApp", "TextEdit {") + editorArea = startQtCreatorWithNewAppAtQMLEditor(tempDir(), "SampleApp") if not editorArea: return + # add basic TextEdit item to check it afterwards + codelines = ['TextEdit {', 'text: "Enter something"', 'anchors.top: parent.top', + 'anchors.horizontalCenter: parent.horizontalCenter', 'anchors.topMargin: 20'] + if not addTestableCodeAfterLine(editorArea, 'title: qsTr("Hello World")', codelines): + saveAndExit() + return # write code with error (C should be lower case) testingCodeLine = 'Color : "blue"' type(editorArea, "<Return>") diff --git a/tests/system/suite_QMLS/tst_QMLS04/test.py b/tests/system/suite_QMLS/tst_QMLS04/test.py index 5dabdf7f0f2..6d4d9624775 100644 --- a/tests/system/suite_QMLS/tst_QMLS04/test.py +++ b/tests/system/suite_QMLS/tst_QMLS04/test.py @@ -27,9 +27,16 @@ source("../shared/qmls.py") def main(): projectDir = tempDir() - editorArea = startQtCreatorWithNewAppAtQMLEditor(projectDir, "SampleApp", "TextEdit {") + editorArea = startQtCreatorWithNewAppAtQMLEditor(projectDir, "SampleApp") if not editorArea: return + # add basic TextEdit item to check it afterwards + codelines = ['TextEdit {', 'id: textEdit', 'text: "Enter something"', 'anchors.top: parent.top', + 'anchors.horizontalCenter: parent.horizontalCenter', 'anchors.topMargin: 20'] + if not addTestableCodeAfterLine(editorArea, 'title: qsTr("Hello World")', codelines): + saveAndExit() + return + placeCursorToLine(editorArea, "TextEdit {") for i in range(5): type(editorArea, "<Left>") # invoke Refactoring - Move Component into separate file diff --git a/tests/system/suite_QMLS/tst_QMLS05/test.py b/tests/system/suite_QMLS/tst_QMLS05/test.py index 3db2f02f015..111bf68f391 100644 --- a/tests/system/suite_QMLS/tst_QMLS05/test.py +++ b/tests/system/suite_QMLS/tst_QMLS05/test.py @@ -26,7 +26,7 @@ source("../shared/qmls.py") def main(): - editorArea = startQtCreatorWithNewAppAtQMLEditor(tempDir(), "SampleApp", "TextEdit {") + editorArea = startQtCreatorWithNewAppAtQMLEditor(tempDir(), "SampleApp", "}") if not editorArea: return homeKey = "<Home>" diff --git a/tests/system/suite_QMLS/tst_QMLS06/test.py b/tests/system/suite_QMLS/tst_QMLS06/test.py index e114fd2f238..f4e9a12d92d 100644 --- a/tests/system/suite_QMLS/tst_QMLS06/test.py +++ b/tests/system/suite_QMLS/tst_QMLS06/test.py @@ -29,7 +29,14 @@ def main(): editorArea = startQtCreatorWithNewAppAtQMLEditor(tempDir(), "SampleApp", "}") if not editorArea: return + homeKey = "<Home>" + if platform.system() == "Darwin": + homeKey = "<Ctrl+Left>" + for i in range(2): + type(editorArea, homeKey) type(editorArea, "<Return>") + type(editorArea, "<Up>") + type(editorArea, "<Tab>") testingItemText = "Item { x: 10; y: 20; width: 10 }" type(editorArea, testingItemText) for i in range(30): diff --git a/tests/system/suite_editors/tst_qml_indent/test.py b/tests/system/suite_editors/tst_qml_indent/test.py index 585a6334c79..f05da16317e 100644 --- a/tests/system/suite_editors/tst_qml_indent/test.py +++ b/tests/system/suite_editors/tst_qml_indent/test.py @@ -42,24 +42,43 @@ def prepareQmlFile(): test.fatal("Could not open main.qml") return None editor = waitForObject(":Qt Creator_QmlJSEditor::QmlJSTextEditorWidget") + isDarwin = platform.system() == 'Darwin' for i in range(3): content = "%s" % editor.plainText - start = content.find("MouseArea {") - if not placeCursorToLine(editor, "MouseArea {"): + if not placeCursorToLine(editor, 'title: qsTr("Hello World")'): test.fatal("Couldn't find line(s) I'm looking for - QML file seems to " "have changed!\nLeaving test...") return None + # add some copyable code + if i == 0: + code = ["", "MouseArea {", "anchors.fill: parent", "onClicked: {", + "console.log(parent.title)"] + typeLines(editor, code) + # avoid having 'correctly' indented empty line + if isDarwin: + type(editor, "<Command+Shift+Left>") + else: + type(editor, "<Shift+Home>") + type(editor, "<Delete>") + # get back to the first entered line + for _ in range(5): + type(editor, "<Up>") + if isDarwin: + type(editor, "<Command+Right>") + else: + type(editor, "<End>") + else: + type(editor, "<Up>") type(editor, "<Right>") - type(editor, "<Up>") # mark until the end of file - if platform.system() == 'Darwin': + if isDarwin: markText(editor, "End") else: markText(editor, "Ctrl+End") # unmark the closing brace type(editor, "<Shift+Up>") type(editor, "<Ctrl+c>") - for j in range(10): + for _ in range(11): type(editor, "<Ctrl+v>") # assume the current editor content to be indented correctly originalText = "%s" % editor.plainText @@ -71,13 +90,12 @@ def prepareQmlFile(): def testReIndent(originalText): editor = waitForObject(":Qt Creator_QmlJSEditor::QmlJSTextEditorWidget") - correctIndented = len(originalText) - incorrectIndented = correctIndented + 4004 type(editor, "<Ctrl+a>") + filenameCombo = waitForObject(":Qt Creator_FilenameQComboBox") test.log("calling re-indent") starttime = datetime.utcnow() type(editor, "<Ctrl+i>") - waitFor("len(str(editor.plainText)) in (incorrectIndented, correctIndented)", 25000) + waitFor("str(filenameCombo.currentText).endswith('*')", 25000) endtime = datetime.utcnow() test.xverify(originalText == str(editor.plainText), "Verify that indenting restored the original text. " diff --git a/tests/system/suite_general/tst_default_settings/test.py b/tests/system/suite_general/tst_default_settings/test.py index 23487e4bd40..a4230fe17fa 100644 --- a/tests/system/suite_general/tst_default_settings/test.py +++ b/tests/system/suite_general/tst_default_settings/test.py @@ -184,7 +184,9 @@ def __getExpectedCompilers__(): expected.extend(__getWinCompilers__()) compilers = ["g++", "gcc"] if platform.system() in ('Linux', 'Darwin'): - compilers.extend(["g++-4.0", "g++-4.2", "clang++", "clang"]) + compilers.extend(["clang++", "clang"]) + compilers.extend(findAllFilesInPATH("*g++*")) + compilers.extend(findAllFilesInPATH("*gcc*")) if platform.system() == 'Darwin': xcodeClang = getOutputFromCmdline(["xcrun", "--find", "clang++"]).strip("\n") if xcodeClang and os.path.exists(xcodeClang) and xcodeClang not in expected: @@ -333,7 +335,6 @@ def __checkCreatedSettings__(settingsFolder): {os.path.join(folders[0], "qtversion.xml"):0}, {os.path.join(folders[0], "toolchains.xml"):0}]) folders.extend([os.path.join(folders[0], "generic-highlighter"), - os.path.join(folders[0], "json"), os.path.join(folders[0], "macros")]) for f in folders: test.verify(os.path.isdir(f), diff --git a/tests/unit/unittest/activationsequencecontextprocessor-test.cpp b/tests/unit/unittest/activationsequencecontextprocessor-test.cpp index 5ccc7d500a6..62a70d752cb 100644 --- a/tests/unit/unittest/activationsequencecontextprocessor-test.cpp +++ b/tests/unit/unittest/activationsequencecontextprocessor-test.cpp @@ -147,7 +147,9 @@ TEST(ActivationSequenceContextProcessor, TemplateFunctionLeftParen) TEST(ActivationSequenceContextProcessor, TemplateFunctionSecondParameter) { ClangCompletionAssistInterface interface("foo<X>(", 7); - int startOfname = ContextProcessor::findStartOfName(&interface, 6); + int startOfname = ContextProcessor::findStartOfName(&interface, + 6, + ContextProcessor::NameCategory::Function); ASSERT_THAT(startOfname, 0); } diff --git a/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp b/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp index 09938b2c5da..9047b091656 100644 --- a/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp +++ b/tests/unit/unittest/clangcompletioncontextanalyzer-test.cpp @@ -178,6 +178,13 @@ TEST_F(ClangCompletionContextAnalyzer, AfterSpace) ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText)); } +TEST_F(ClangCompletionContextAnalyzer, AfterQualification) +{ + auto analyzer = runAnalyzer(" Foo::@"); + + ASSERT_THAT(analyzer, HasResult(CCA::PassThroughToLibClang, 0, 0, positionInText)); +} + TEST_F(ClangCompletionContextAnalyzer, AtEndOfDotMember) { auto analyzer = runAnalyzer("o.mem@"); |