Merge remote-tracking branch 'origin/4.4'
Conflicts: src/tools/clangbackend/ipcsource/clangiasyncjob.cpp src/tools/clangbackend/ipcsource/clangjobrequest.cpp src/tools/clangbackend/ipcsource/clangjobrequest.h Change-Id: Ib8602530663813ade418f995dfd2a736908cfe75
@@ -111,6 +111,7 @@ phony.c
|
|||||||
/dist/gdb/qtcreator-*/
|
/dist/gdb/qtcreator-*/
|
||||||
/dist/gdb/source/
|
/dist/gdb/source/
|
||||||
/dist/gdb/staging/
|
/dist/gdb/staging/
|
||||||
|
/doc/qbs/
|
||||||
/doc/qtcreator/
|
/doc/qtcreator/
|
||||||
/doc/qtcreator-dev/
|
/doc/qtcreator-dev/
|
||||||
/doc/pluginhowto/html/
|
/doc/pluginhowto/html/
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 8.3 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 69 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 40 KiB |
@@ -57,16 +57,14 @@
|
|||||||
\li \l{http://www.oracle.com/technetwork/java/javase/downloads/index.html}
|
\li \l{http://www.oracle.com/technetwork/java/javase/downloads/index.html}
|
||||||
{Java SE Development Kit (JDK)} version 6, or later
|
{Java SE Development Kit (JDK)} version 6, or later
|
||||||
|
|
||||||
\li A build tool for building application packages for Android devices:
|
\li \l{http://www.gradle.org}{Gradle} for building application packages
|
||||||
|
for Android devices (APK). Gradle is delivered with Qt 5.9, and
|
||||||
|
later.
|
||||||
|
|
||||||
\list
|
\note Using Ant to build APKs is still possible when developing with
|
||||||
|
Qt 5.8 or earlier together with Android SDK tools versions 25.2.5 or
|
||||||
\li \l{http://ant.apache.org/bindownload.cgi}{Apache Ant} 1.8.0,
|
earlier, but Ant support has been deprecated since \QC 4.3 and will
|
||||||
or later
|
be removed in a future version.
|
||||||
|
|
||||||
\li \l{http://www.gradle.org}{Gradle}
|
|
||||||
|
|
||||||
\endlist
|
|
||||||
|
|
||||||
\li A tool chain for building applications for Android devices provided
|
\li A tool chain for building applications for Android devices provided
|
||||||
by the \l{http://developer.android.com/tools/sdk/ndk/index.html}
|
by the \l{http://developer.android.com/tools/sdk/ndk/index.html}
|
||||||
@@ -74,20 +72,10 @@
|
|||||||
|
|
||||||
\li \l{http://developer.android.com/sdk/index.html}{Android SDK Tools}
|
\li \l{http://developer.android.com/sdk/index.html}{Android SDK Tools}
|
||||||
|
|
||||||
After installing the Tools, update the Android SDK to get the API
|
After installing the Tools, you can use the
|
||||||
and tools packages required for development. You can use the
|
|
||||||
\l{http://developer.android.com/tools/help/android.html}{android}
|
|
||||||
tool that comes with the SDK Tools package. For example, on Ubuntu
|
|
||||||
the following command starts the SDK update:
|
|
||||||
|
|
||||||
\code
|
|
||||||
./android update sdk
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
\note The android tool is deprecated since SDK tools version 25.3.0.
|
|
||||||
Consider using
|
|
||||||
\l{https://developer.android.com/studio/command-line/sdkmanager.html}
|
\l{https://developer.android.com/studio/command-line/sdkmanager.html}
|
||||||
{sdkmanager} instead.
|
{sdkmanager} to get the API and tools packages required for
|
||||||
|
development.
|
||||||
|
|
||||||
\li On Windows, you also need the following:
|
\li On Windows, you also need the following:
|
||||||
|
|
||||||
@@ -108,33 +96,17 @@
|
|||||||
|
|
||||||
\endlist
|
\endlist
|
||||||
|
|
||||||
\section1 Selecting the APK Build Tool
|
|
||||||
|
|
||||||
On Android, applications are distributed in specially structured type of ZIP
|
|
||||||
packages called APK. You can use either Ant or Gradle to build APKs. Using
|
|
||||||
Gradle has the following benefits:
|
|
||||||
|
|
||||||
\list
|
|
||||||
|
|
||||||
\li It is 25 to 50 percent faster than Ant when rebuilding packages.
|
|
||||||
|
|
||||||
\li It is delivered with Qt 5.9.
|
|
||||||
|
|
||||||
\li It supports easily adding Android Extras libraries, such as
|
|
||||||
Google Play services or Android extension files (.obb) to your
|
|
||||||
project.
|
|
||||||
|
|
||||||
\endlist
|
|
||||||
|
|
||||||
\note Android SDK tools version 25.3.0 onwards do not contain Ant scripts
|
|
||||||
anymore. Therefore, Gradle builds are forced and you must use Qt 5.9 or
|
|
||||||
later when using these tools versions.
|
|
||||||
|
|
||||||
\section1 Setting Up the Development Environment
|
\section1 Setting Up the Development Environment
|
||||||
|
|
||||||
You must download and install the latest Android NDK and SDK, and update the
|
You must download and install the latest Android NDK and SDK, and update the
|
||||||
SDK to get the API and tools packages needed for development. In addition,
|
SDK to get the build and platform tools needed for development. Since
|
||||||
you must install Qt for Android as part of Qt 5.2, or later.
|
SDK tools version 25.3.0, \QC, you must use the
|
||||||
|
\l{https://developer.android.com/studio/command-line/sdkmanager.html}
|
||||||
|
{sdkmanager} command line tool for SDK package management and the
|
||||||
|
\l{https://developer.android.com/studio/command-line/avdmanager.html}
|
||||||
|
{avdmanager} tool for Android Virtual Device (AVD) management.
|
||||||
|
|
||||||
|
In addition, you must install Qt for Android as part of Qt 5.2, or later.
|
||||||
|
|
||||||
For more information, see \l{Qt for Android}.
|
For more information, see \l{Qt for Android}.
|
||||||
|
|
||||||
@@ -165,6 +137,9 @@
|
|||||||
\li To use Ant, add the path to the Ant executable in the
|
\li To use Ant, add the path to the Ant executable in the
|
||||||
\uicontrol {Ant executable} field.
|
\uicontrol {Ant executable} field.
|
||||||
|
|
||||||
|
\note Ant support has been deprecated since \QC 4.3. We
|
||||||
|
recommend that you use Gradle instead.
|
||||||
|
|
||||||
\endlist
|
\endlist
|
||||||
|
|
||||||
\li Select \uicontrol File > \uicontrol {New File or Project} > \uicontrol Application >
|
\li Select \uicontrol File > \uicontrol {New File or Project} > \uicontrol Application >
|
||||||
@@ -213,7 +188,7 @@
|
|||||||
|
|
||||||
\image qtcreator-android-select-devices.png "Select Android Devices dialog"
|
\image qtcreator-android-select-devices.png "Select Android Devices dialog"
|
||||||
|
|
||||||
Android Virtual Devices (AVD) are also listed. To create new AVDs, select
|
AVDs are also listed. To create new AVDs, select
|
||||||
\uicontrol {Create Android Virtual Device}.
|
\uicontrol {Create Android Virtual Device}.
|
||||||
|
|
||||||
To set a device as the default device for a particular Android architecture,
|
To set a device as the default device for a particular Android architecture,
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2017 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of the Qt Creator documentation.
|
||||||
|
**
|
||||||
|
** 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 Free Documentation License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU Free
|
||||||
|
** Documentation License version 1.3 as published by the Free Software
|
||||||
|
** Foundation and appearing in the file included in the packaging of
|
||||||
|
** this file. Please review the following information to ensure
|
||||||
|
** the GNU Free Documentation License version 1.3 requirements
|
||||||
|
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
//! [run settings android]
|
||||||
|
|
||||||
|
\section1 Specifying Run Settings for Android Devices
|
||||||
|
|
||||||
|
To run and debug an application on an Android device, you must create
|
||||||
|
connections from the development host to the device, as instructed in
|
||||||
|
\l {Connecting Android Devices}.
|
||||||
|
|
||||||
|
A default set of Android Activity manager (am) start options is applied when
|
||||||
|
starting applications. You can specify additional start options in the
|
||||||
|
\uicontrol {Activity manager start options} field. However, if the default
|
||||||
|
options conflict with the added options, the application might not start.
|
||||||
|
|
||||||
|
For example, to run the application as a particular user, enter the start
|
||||||
|
option \c {--user 10}, where \c 10 is the user ID of the user account.
|
||||||
|
|
||||||
|
\image qtcreator-android-run-settings.png
|
||||||
|
|
||||||
|
You can specify shell commands to run before the application is started and
|
||||||
|
after it is quit. For example, enter the following commands to unlock the
|
||||||
|
screen and to switch to the user account \c 10 on the device before running
|
||||||
|
the application:
|
||||||
|
|
||||||
|
\code
|
||||||
|
input keyevent 82
|
||||||
|
am switch-user 10
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
Enter the following commands to switch back to the default user, \c 0, and
|
||||||
|
to unlock the screen after the application is quit:
|
||||||
|
|
||||||
|
\code
|
||||||
|
am switch-user 0
|
||||||
|
input keyevent 82
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
//! [run settings android]
|
||||||
|
*/
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
** Copyright (C) 2017 The Qt Company Ltd.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the Qt Creator documentation.
|
** This file is part of the Qt Creator documentation.
|
||||||
@@ -55,6 +55,9 @@
|
|||||||
To specify settings for application packages, select \uicontrol Projects >
|
To specify settings for application packages, select \uicontrol Projects >
|
||||||
\uicontrol {Build Android APK} > \uicontrol Details.
|
\uicontrol {Build Android APK} > \uicontrol Details.
|
||||||
|
|
||||||
|
For more information about options that you have for running applications,
|
||||||
|
see \l {Specifying Run Settings for Android Devices}.
|
||||||
|
|
||||||
\section1 Creating Distributable APK Packages
|
\section1 Creating Distributable APK Packages
|
||||||
|
|
||||||
To copy Qt libraries and files to the project directory and to bundle them
|
To copy Qt libraries and files to the project directory and to bundle them
|
||||||
@@ -128,8 +131,9 @@
|
|||||||
|
|
||||||
\endlist
|
\endlist
|
||||||
|
|
||||||
The Gradle wrappers and scripts are bundled only if you use
|
The Gradle wrappers and scripts are bundled only if you use Gradle to build
|
||||||
\l{Selecting the APK Build Tool}{Gradle} to build the application packages.
|
the application packages. For more information, see
|
||||||
|
\l{Connecting Android Devices}.
|
||||||
|
|
||||||
To view the packages that the \c androiddeployqt tool created, select the
|
To view the packages that the \c androiddeployqt tool created, select the
|
||||||
\uicontrol {Open package location after build} check box.
|
\uicontrol {Open package location after build} check box.
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
\title Debugging Qt Quick Projects
|
\title Debugging Qt Quick Projects
|
||||||
|
|
||||||
\note You need Qt 4.8 or later to debug Qt Quick projects.
|
\note You need Qt 5.0 or later to debug Qt Quick projects.
|
||||||
|
|
||||||
For an example of how to debug Qt Quick Projects, see
|
For an example of how to debug Qt Quick Projects, see
|
||||||
\l{Debugging a Qt Quick Example Application}.
|
\l{Debugging a Qt Quick Example Application}.
|
||||||
@@ -55,17 +55,14 @@
|
|||||||
|
|
||||||
\list 1
|
\list 1
|
||||||
|
|
||||||
\li Debugging is enabled by default for Qt 4.8, or later. For Qt 4.7,
|
\li Debugging is enabled by default for Qt 5.0, or later.
|
||||||
select \uicontrol Projects, and then select the
|
|
||||||
\uicontrol {Enable QML debugging and profiling} check box in the \uicontrol qmake
|
|
||||||
section in \uicontrol {Build Steps}.
|
|
||||||
|
|
||||||
You might have to compile the library first, by selecting the
|
You might have to compile the library first, by selecting the
|
||||||
\uicontrol Compile link.
|
\uicontrol Compile link.
|
||||||
|
|
||||||
\image qml-link-debugging-library.png "Build Steps"
|
\image qml-link-debugging-library.png "Build Steps"
|
||||||
|
|
||||||
\note Debugging requires opening a socket at a well-known port,
|
\note Debugging requires opening a socket at a TCP port,
|
||||||
which presents a security risk. Anyone on the Internet could connect
|
which presents a security risk. Anyone on the Internet could connect
|
||||||
to the application that you are debugging and execute any JavaScript
|
to the application that you are debugging and execute any JavaScript
|
||||||
functions. Therefore, you must make sure that the port is properly
|
functions. Therefore, you must make sure that the port is properly
|
||||||
@@ -78,7 +75,7 @@
|
|||||||
\li Select \uicontrol {Build > Rebuild Project} to clean and rebuild the
|
\li Select \uicontrol {Build > Rebuild Project} to clean and rebuild the
|
||||||
project.
|
project.
|
||||||
|
|
||||||
\li To debug applications on devices, check that Qt 4.7.4, or later,
|
\li To debug applications on devices, check that Qt 5.0, or later,
|
||||||
libraries are installed on the device and
|
libraries are installed on the device and
|
||||||
\l{Running on Multiple Platforms}{select the corresponding kit for the device}
|
\l{Running on Multiple Platforms}{select the corresponding kit for the device}
|
||||||
before you start debugging.
|
before you start debugging.
|
||||||
@@ -107,9 +104,8 @@
|
|||||||
\li Execute JavaScript expressions to get information about the state of
|
\li Execute JavaScript expressions to get information about the state of
|
||||||
the application
|
the application
|
||||||
|
|
||||||
\li Change QML code and immediately see the changes at runtime
|
\li Inspect QML properties and JavaScript variables and change them
|
||||||
|
temporarily at runtime
|
||||||
\li Inspect QML code and change it temporarily at runtime
|
|
||||||
|
|
||||||
\endlist
|
\endlist
|
||||||
|
|
||||||
@@ -121,13 +117,8 @@
|
|||||||
parameters (if you build the application with \QC, it automatically
|
parameters (if you build the application with \QC, it automatically
|
||||||
uses the correct configuration):
|
uses the correct configuration):
|
||||||
|
|
||||||
\list
|
\c {CONFIG+=qml_debug}
|
||||||
|
|
||||||
\li Qt Quick 1: \c {CONFIG+=declarative_debug}
|
|
||||||
|
|
||||||
\li Qt Quick 2: \c {CONFIG+=qml_debug}
|
|
||||||
|
|
||||||
\endlist
|
|
||||||
|
|
||||||
\li Start the application with the following arguments:
|
\li Start the application with the following arguments:
|
||||||
|
|
||||||
@@ -210,7 +201,7 @@
|
|||||||
\section1 Inspecting User Interfaces
|
\section1 Inspecting User Interfaces
|
||||||
|
|
||||||
When you debug complex applications, you can jump to the position in code
|
When you debug complex applications, you can jump to the position in code
|
||||||
where an item is defined or you can zoom into the user interface.
|
where an item is defined.
|
||||||
|
|
||||||
In the selection mode, you can click items in the running
|
In the selection mode, you can click items in the running
|
||||||
application to jump to their definitions in the code. The properties of the
|
application to jump to their definitions in the code. The properties of the
|
||||||
@@ -222,18 +213,8 @@
|
|||||||
|
|
||||||
You can also view the item hierarchy in the running application:
|
You can also view the item hierarchy in the running application:
|
||||||
|
|
||||||
\list
|
Double-click an item in the running application to cycle through the item
|
||||||
|
stack at the cursor position.
|
||||||
\li When debugging Qt Quick 1 applications, right-click an item in the
|
|
||||||
running application to view the item hierarchy as a context menu.
|
|
||||||
|
|
||||||
\image qml-observer-context-menu.png "QML item hierarchy"
|
|
||||||
|
|
||||||
\li When debugging Qt Quick 2 applications, double-click an item in the
|
|
||||||
running application to cycle through the item stack at the cursor
|
|
||||||
position.
|
|
||||||
|
|
||||||
\endlist
|
|
||||||
|
|
||||||
To switch out of the selection mode, toggle the \uicontrol Select menu item.
|
To switch out of the selection mode, toggle the \uicontrol Select menu item.
|
||||||
|
|
||||||
|
|||||||
@@ -233,7 +233,7 @@
|
|||||||
in Windows. To check JSON data structure, copy the JSON schema file to the
|
in Windows. To check JSON data structure, copy the JSON schema file to the
|
||||||
above folder.
|
above folder.
|
||||||
|
|
||||||
\section1 Checking JavaScript and QML Syntax
|
\section2 Checking JavaScript and QML Syntax
|
||||||
|
|
||||||
To run the checks, select \uicontrol Tools > \uicontrol {QML/JS} >
|
To run the checks, select \uicontrol Tools > \uicontrol {QML/JS} >
|
||||||
\uicontrol {Run Checks} or press \key {Ctrl+Shift+C}. The results are shown
|
\uicontrol {Run Checks} or press \key {Ctrl+Shift+C}. The results are shown
|
||||||
@@ -241,7 +241,7 @@
|
|||||||
pane.
|
pane.
|
||||||
|
|
||||||
|
|
||||||
\section1 List of JavaScript and QML Checks
|
\section2 List of JavaScript and QML Checks
|
||||||
|
|
||||||
Many of the JavaScript checks are similar to the ones in Douglas Crockford's
|
Many of the JavaScript checks are similar to the ones in Douglas Crockford's
|
||||||
\l{http://www.jslint.com}{JSLint} tool.
|
\l{http://www.jslint.com}{JSLint} tool.
|
||||||
@@ -811,6 +811,12 @@
|
|||||||
To inspect QML and JavaScript properties, methods, and enums, move the
|
To inspect QML and JavaScript properties, methods, and enums, move the
|
||||||
cursor over them and select \uicontrol Tools > \uicontrol {QML/JS} >
|
cursor over them and select \uicontrol Tools > \uicontrol {QML/JS} >
|
||||||
\uicontrol {Inspect API for Element Under Cursor}.
|
\uicontrol {Inspect API for Element Under Cursor}.
|
||||||
|
|
||||||
|
\section1 Automatically Formatting QML/JS Files
|
||||||
|
|
||||||
|
To automatically format QML/JS files upon saving, select \uicontrol Tools >
|
||||||
|
\uicontrol Options > \uicontrol {Qt Quick} > \uicontrol {QML/JS Editing} >
|
||||||
|
\uicontrol {Enable auto format on file save}.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -148,20 +148,62 @@
|
|||||||
|
|
||||||
For more information, see \l{Expressing Supported iOS Versions}.
|
For more information, see \l{Expressing Supported iOS Versions}.
|
||||||
|
|
||||||
\section1 Testing on iOS Simulator
|
\section1 Testing on Simulator
|
||||||
|
|
||||||
If you do not have an iOS device or you do not want to create an account,
|
If you do not have an iOS device or you do not want to create an account,
|
||||||
you can test applications on
|
you can test applications on
|
||||||
\l{http://developer.apple.com/library/ios/documentation/IDEs/Conceptual/iOS_Simulator_Guide/Introduction/Introduction.html}
|
\l{http://developer.apple.com/library/ios/documentation/IDEs/Conceptual/iOS_Simulator_Guide/Introduction/Introduction.html}
|
||||||
{iOS Simulator}, which is installed as part of Xcode. Each Xcode version
|
{Simulator}, which is installed as part of Xcode. Each Xcode version
|
||||||
simulates a predefined set of hardware devices and software versions.
|
simulates a predefined set of hardware devices and software versions.
|
||||||
|
|
||||||
You can change the simulated hardware and software version in the run
|
You can change the simulated hardware and software version in the run
|
||||||
settings for the project. Select \uicontrol Projects > \uicontrol Run, and then select
|
settings for the project. Select \uicontrol Projects > \uicontrol Run, and then select
|
||||||
the device to simulate in the \uicontrol {Device type} field.
|
the device to simulate in the \uicontrol {Device type} field.
|
||||||
|
|
||||||
\QC uses the Xcode version set as current on the Mac computer.
|
\image qtcreator-ios-simulator-deploy.png
|
||||||
To check the version, enter the following command:
|
|
||||||
|
The simulator is started automatically when you run the application.
|
||||||
|
To start the simulator manually, select \uicontrol Preferences >
|
||||||
|
\uicontrol Devices > \uicontrol iOS > \uicontrol Start.
|
||||||
|
|
||||||
|
To take screenshots of the simulator, select \uicontrol Preferences >
|
||||||
|
\uicontrol Devices > \uicontrol iOS > \uicontrol Screenshot. The screenshots
|
||||||
|
are stored in the directory specified in the
|
||||||
|
\uicontrol {Screenshot directory} field.
|
||||||
|
|
||||||
|
\section2 Managing Simulators
|
||||||
|
|
||||||
|
The available simulators are listed in \uicontrol Preferences >
|
||||||
|
\uicontrol Devices > \uicontrol iOS.
|
||||||
|
|
||||||
|
\image qtcreator-ios-preferences.png
|
||||||
|
|
||||||
|
To create a new simulator instance:
|
||||||
|
|
||||||
|
\list
|
||||||
|
|
||||||
|
\li Select \uicontrol Create.
|
||||||
|
|
||||||
|
\li In the \uicontrol {Device type} field, select the device type from
|
||||||
|
a list of devices supported by the Xcode version set as current on
|
||||||
|
the Mac computer.
|
||||||
|
|
||||||
|
\li In the \uicontrol {OS version} field, select an OS version from a
|
||||||
|
list of OS versions supported by the selected device and the current
|
||||||
|
Xcode version.
|
||||||
|
|
||||||
|
\endlist
|
||||||
|
|
||||||
|
To rename the selected simulator, select \uicontrol Rename.
|
||||||
|
|
||||||
|
To reset the contents and settings of the selected simulators, select
|
||||||
|
\uicontrol Reset.
|
||||||
|
|
||||||
|
To delete the selected simulator, select \uicontrol Delete.
|
||||||
|
|
||||||
|
\section2 Checking Current Xcode Version
|
||||||
|
|
||||||
|
To check the current Xcode version, enter the following command:
|
||||||
|
|
||||||
\c {xcode-select --print-path}
|
\c {xcode-select --print-path}
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
/*!
|
/*!
|
||||||
//! [run settings embedded]
|
//! [run settings embedded]
|
||||||
|
|
||||||
\section2 Specifying Run Settings for Embedded Devices
|
\section1 Specifying Run Settings for Embedded Devices
|
||||||
|
|
||||||
To run and debug an application on an embedded device (commercial only), you
|
To run and debug an application on an embedded device (commercial only), you
|
||||||
must create connections from the development host to the device and add the
|
must create connections from the development host to the device and add the
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
/*!
|
/*!
|
||||||
//! [run settings linux]
|
//! [run settings linux]
|
||||||
|
|
||||||
\section2 Specifying Run Settings for Linux-Based Devices
|
\section1 Specifying Run Settings for Linux-Based Devices
|
||||||
|
|
||||||
To run and debug an application on a Linux-based device,
|
To run and debug an application on a Linux-based device,
|
||||||
you must create connections from the development
|
you must create connections from the development
|
||||||
|
|||||||
@@ -41,29 +41,16 @@
|
|||||||
{JSON-Based wizards} instead. XML wizards are deprecated and support for
|
{JSON-Based wizards} instead. XML wizards are deprecated and support for
|
||||||
them will be removed in future versions of \QC.
|
them will be removed in future versions of \QC.
|
||||||
|
|
||||||
To display the XML-based example wizards in \QC, rename
|
To see examples of XML-based wizards, select \uicontrol File >
|
||||||
\c {wizard_sample.xml} as \c {wizard.xml} in the
|
\uicontrol {New File or Project} > \uicontrol Library. For each wizard, an
|
||||||
\c {\share\qtcreator\templates\wizards\helloworld} and
|
icon (1), a display name (2), and a description (3) are displayed.
|
||||||
\c {\share\qtcreator\templates\wizards\listmodel} folders. After
|
|
||||||
you restart \QC, the \uicontrol {Custom Classes}
|
|
||||||
and \uicontrol {Custom Projects} categories (1) appear in the \uicontrol New
|
|
||||||
dialog. For each category, an icon (2), a display name (3), and a
|
|
||||||
description (4) are displayed.
|
|
||||||
|
|
||||||
\image qtcreator-custom-project-wizards.png "The New dialog with custom projects and classes"
|
\image qtcreator-custom-project-wizards.png "The New dialog"
|
||||||
|
|
||||||
Files can be generated by using either \l{Processing Template Files}
|
Files can be generated by using \l{Processing Template Files}{templates}.
|
||||||
{templates} or \l{Using Generator Scripts}{generator scripts}, where a
|
|
||||||
script is called to create the files.
|
|
||||||
|
|
||||||
\note The generator option mainly exists to accommodate existing generator
|
|
||||||
scripts or cases where complicated algorithmic logic is required when
|
|
||||||
generating files. Writing cross-platform scripts is inherently difficult,
|
|
||||||
and therefore, it is not recommended for new wizards.
|
|
||||||
|
|
||||||
XML-based wizard template directories contain an XML configuration file
|
XML-based wizard template directories contain an XML configuration file
|
||||||
called wizard.xml, the template source files, and optionally, the generator
|
called wizard.xml and the template source files.
|
||||||
script.
|
|
||||||
|
|
||||||
\section1 Creating XML-Based Project Wizards
|
\section1 Creating XML-Based Project Wizards
|
||||||
|
|
||||||
@@ -71,10 +58,11 @@
|
|||||||
|
|
||||||
\list 1
|
\list 1
|
||||||
|
|
||||||
\li Make a copy of the \c {share/qtcreator/templates/wizards/helloworld}
|
\li Make a copy of a folder in the \c share/qtcreator/templates/wizards/
|
||||||
or \c {share/qtcreator/templates/wizards/listmodel} folder.
|
folder that contains an XML-based wizard (\c codesnippet,
|
||||||
|
\c qtcreatorplugin, or \c qtquick2-extension).
|
||||||
|
|
||||||
\li Modify the wizard_example.xml file.
|
\li Modify the wizard.xml file.
|
||||||
|
|
||||||
\li The following code determines the type of the wizard and its place
|
\li The following code determines the type of the wizard and its place
|
||||||
in the \uicontrol New dialog:
|
in the \uicontrol New dialog:
|
||||||
@@ -89,8 +77,7 @@
|
|||||||
|
|
||||||
\list
|
\list
|
||||||
|
|
||||||
\li \c version is the version of the file contents. Do not modify
|
\li \c version is the version of the file contents.
|
||||||
this value.
|
|
||||||
|
|
||||||
\li \c kind specifies the type of the wizard: \c project or
|
\li \c kind specifies the type of the wizard: \c project or
|
||||||
\c class.
|
\c class.
|
||||||
@@ -146,12 +133,7 @@
|
|||||||
|
|
||||||
\endlist
|
\endlist
|
||||||
|
|
||||||
\li Files to be added to the project:
|
\li The following code specifies the files to add to the project:
|
||||||
|
|
||||||
\list
|
|
||||||
|
|
||||||
\li Template-based: The following code specifies the files to add to
|
|
||||||
the project:
|
|
||||||
\code
|
\code
|
||||||
<files>
|
<files>
|
||||||
<file source="main.cpp" openeditor="true" />
|
<file source="main.cpp" openeditor="true" />
|
||||||
@@ -183,41 +165,6 @@
|
|||||||
|
|
||||||
See also \l{Processing Template Files}.
|
See also \l{Processing Template Files}.
|
||||||
|
|
||||||
\li Generator-script: The following code specifies that the script
|
|
||||||
\c generate.pl is to be used to create the files:
|
|
||||||
|
|
||||||
\code
|
|
||||||
<generatorscript binary="generate.pl">
|
|
||||||
<argument value="--class-name=%ClassName%"/>
|
|
||||||
<argument value="--project-name=%ProjectName%"/>
|
|
||||||
<argument value="--header-suffix=%CppHeaderSuffix%" omit-empty="true"/>
|
|
||||||
<argument value="--source-suffix=%CppSourceSuffix%" omit-empty="true"/>
|
|
||||||
<argument value="--description=%Description%" omit-empty="true" write-file="true"/>
|
|
||||||
</generatorscript>
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
In each argument, the field placeholders are replaced by the
|
|
||||||
field values. There are additional boolean attributes which give
|
|
||||||
fine-grained control:
|
|
||||||
|
|
||||||
\list
|
|
||||||
|
|
||||||
\li \c omit-empty specifies that complete argument is to be
|
|
||||||
omitted when all placeholders expand to empty values. In
|
|
||||||
the above example, the option \c --source-suffix will
|
|
||||||
not be passed to the script if the value is empty.
|
|
||||||
|
|
||||||
\li \c write-file indicates that instead of the expanded
|
|
||||||
value, the value will be written to a temporary file and
|
|
||||||
its file name will be passed to the script instead. This
|
|
||||||
is useful for multi-line text fields.
|
|
||||||
|
|
||||||
\endlist
|
|
||||||
|
|
||||||
See also \l{Using Generator Scripts}.
|
|
||||||
|
|
||||||
\endlist
|
|
||||||
|
|
||||||
\li The following code creates a page that specifies settings for the project:
|
\li The following code creates a page that specifies settings for the project:
|
||||||
|
|
||||||
\code
|
\code
|
||||||
@@ -600,50 +547,4 @@
|
|||||||
of the base class. If the validation fails, a red label displaying the
|
of the base class. If the validation fails, a red label displaying the
|
||||||
message appears at the bottom of the wizard page.
|
message appears at the bottom of the wizard page.
|
||||||
|
|
||||||
\section1 Using Generator Scripts
|
|
||||||
|
|
||||||
The values entered in the wizard page are passed to the script
|
|
||||||
as command line arguments as defined by the wizard configuration file.
|
|
||||||
|
|
||||||
In addition, the script must implement a \c{--dry-run} command line option.
|
|
||||||
|
|
||||||
\QC needs to know the file names before the files are created to check
|
|
||||||
whether files with identical names already exist, for example. Therefore,
|
|
||||||
script file generation is a two-step process:
|
|
||||||
|
|
||||||
\list 1
|
|
||||||
|
|
||||||
\li Determine file names and attributes: The script is called with the
|
|
||||||
command line \c{--dry-run} option and the field values. It then prints
|
|
||||||
the relative path names of the files it intends to create, followed by
|
|
||||||
comma-separated attributes matching those of the \c{<file>} element, for
|
|
||||||
example:
|
|
||||||
|
|
||||||
\code
|
|
||||||
myclass.cpp,openeditor
|
|
||||||
myclass.h,openeditor
|
|
||||||
myproject.pro,openproject
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
\li Create files: The script is called with the parameters only in the
|
|
||||||
working directory. It then actually creates the files. If directories
|
|
||||||
are needed, the script should create them, too.
|
|
||||||
|
|
||||||
\endlist
|
|
||||||
|
|
||||||
The \c{scriptgeneratedproject} sample wizard illustrates the usage.
|
|
||||||
A typical script invocation for this example (obtained by running \QC with
|
|
||||||
\c{--customwizard-verbose}) looks as follows:
|
|
||||||
|
|
||||||
\code
|
|
||||||
generate.pl --class-name=TestClass --project-name=TestProject --header-suffix=h --source-suffix=cpp --description=/tmp/qtcreatorj26629.txt
|
|
||||||
\endcode
|
|
||||||
|
|
||||||
By default, the scripts are run in the directory corresponding to
|
|
||||||
\c %TargetPath%. This can be overridden by specifying the attribute
|
|
||||||
\c workingdirectory on the element \c generatorscript. For example, if the
|
|
||||||
script creates the project directory by itself, %Path% can be specified. In
|
|
||||||
that case, \c --dry-run should output the correct relative paths or absolute
|
|
||||||
paths constructed using the value of \c %Path%.
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -58,9 +58,11 @@
|
|||||||
|
|
||||||
\list 1
|
\list 1
|
||||||
|
|
||||||
\li Select \uicontrol Projects > \uicontrol {Code Style}.
|
\li Select \uicontrol Projects > \uicontrol {Project Settings} >
|
||||||
|
\uicontrol {Code Style}.
|
||||||
|
|
||||||
\li In the \uicontrol Language field, select \uicontrol C++ or \uicontrol {Qt Quick}.
|
\li In the \uicontrol Language field, select \uicontrol C++,
|
||||||
|
\uicontrol {Qt Quick}, or \uicontrol Nim.
|
||||||
|
|
||||||
\li In the \uicontrol {Current settings} field, select the settings to modify
|
\li In the \uicontrol {Current settings} field, select the settings to modify
|
||||||
and click \uicontrol Copy.
|
and click \uicontrol Copy.
|
||||||
|
|||||||
@@ -51,9 +51,8 @@
|
|||||||
|
|
||||||
\list 1
|
\list 1
|
||||||
|
|
||||||
\li In \uicontrol Projects, select a project.
|
\li Select \uicontrol Projects > \uicontrol {Project Settings} >
|
||||||
|
\uicontrol Dependencies.
|
||||||
\li Click \uicontrol Dependencies.
|
|
||||||
|
|
||||||
\li Select projects that must be built before the current project is
|
\li Select projects that must be built before the current project is
|
||||||
built.
|
built.
|
||||||
|
|||||||
@@ -50,7 +50,8 @@
|
|||||||
|
|
||||||
\list 1
|
\list 1
|
||||||
|
|
||||||
\li Select \uicontrol Projects > \uicontrol Editor.
|
\li Select \uicontrol Projects > \uicontrol {Project Settings} >
|
||||||
|
\uicontrol Editor.
|
||||||
|
|
||||||
\li In the \uicontrol {Editor settings} field, select \uicontrol {Custom}.
|
\li In the \uicontrol {Editor settings} field, select \uicontrol {Custom}.
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
/*!
|
/*!
|
||||||
//! [settings valgrind]
|
//! [settings valgrind]
|
||||||
|
|
||||||
\section2 Specifying Valgrind Settings
|
\section1 Specifying Valgrind Settings
|
||||||
|
|
||||||
\QC integrates \l{Analyzing Code}{Valgrind code analysis tools} for
|
\QC integrates \l{Analyzing Code}{Valgrind code analysis tools} for
|
||||||
detecting memory leaks and profiling function execution. You can configure
|
detecting memory leaks and profiling function execution. You can configure
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
/*!
|
/*!
|
||||||
//! [run settings debugger]
|
//! [run settings debugger]
|
||||||
|
|
||||||
\section2 Specifying Debugger Settings
|
\section1 Specifying Debugger Settings
|
||||||
|
|
||||||
\image qtquick-debugging-settings.png "Debugger Settings"
|
\image qtquick-debugging-settings.png "Debugger Settings"
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
/*!
|
/*!
|
||||||
//! [run settings desktop]
|
//! [run settings desktop]
|
||||||
|
|
||||||
\section2 Specifying Run Settings for Desktop Device Types
|
\section1 Specifying Run Settings for Desktop Device Types
|
||||||
|
|
||||||
You can specify command line arguments to be passed to the executable
|
You can specify command line arguments to be passed to the executable
|
||||||
and the working directory to use. The working directory defaults to
|
and the working directory to use. The working directory defaults to
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
\QC automatically adds run configurations for all targets specified in the
|
\QC automatically adds run configurations for all targets specified in the
|
||||||
CMake project file, \c {CMakeLists.txt}.
|
CMake project file, \c {CMakeLists.txt}.
|
||||||
|
|
||||||
\section2 Creating Run Configurations for Subprojects
|
\section1 Creating Run Configurations for Subprojects
|
||||||
|
|
||||||
To prevent \QC from automatically creating run configurations for SUBDIRS
|
To prevent \QC from automatically creating run configurations for SUBDIRS
|
||||||
projects, specify the following variable in the .pro file of the SUBDIRS
|
projects, specify the following variable in the .pro file of the SUBDIRS
|
||||||
@@ -65,6 +65,7 @@
|
|||||||
\include projects/creator-projects-settings-run-desktop.qdocinc run settings desktop
|
\include projects/creator-projects-settings-run-desktop.qdocinc run settings desktop
|
||||||
\include projects/creator-projects-settings-run-analyze.qdocinc settings valgrind
|
\include projects/creator-projects-settings-run-analyze.qdocinc settings valgrind
|
||||||
\include projects/creator-projects-settings-run-debug.qdocinc run settings debugger
|
\include projects/creator-projects-settings-run-debug.qdocinc run settings debugger
|
||||||
|
\include android/creator-projects-settings-run-android.qdocinc run settings android
|
||||||
\include linux-mobile/creator-projects-settings-run-linux.qdocinc run settings linux
|
\include linux-mobile/creator-projects-settings-run-linux.qdocinc run settings linux
|
||||||
\include qnx/creator-projects-settings-run-qnx.qdocinc run settings qnx
|
\include qnx/creator-projects-settings-run-qnx.qdocinc run settings qnx
|
||||||
\include linux-mobile/creator-projects-settings-run-b2qt.qdocinc run settings embedded
|
\include linux-mobile/creator-projects-settings-run-b2qt.qdocinc run settings embedded
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2016 The Qt Company Ltd.
|
** Copyright (C) 2017 The Qt Company Ltd.
|
||||||
** Contact: https://www.qt.io/licensing/
|
** Contact: https://www.qt.io/licensing/
|
||||||
**
|
**
|
||||||
** This file is part of the Qt Creator documentation.
|
** This file is part of the Qt Creator documentation.
|
||||||
@@ -107,6 +107,9 @@
|
|||||||
add debuggers to the list. For more information, see
|
add debuggers to the list. For more information, see
|
||||||
\l{Adding Debuggers}.
|
\l{Adding Debuggers}.
|
||||||
|
|
||||||
|
For Android kits, the \uicontrol {Android GDB server} field will
|
||||||
|
display the path to GDB server executable.
|
||||||
|
|
||||||
\li In the \uicontrol {Qt version} field, select the Qt version to use for
|
\li In the \uicontrol {Qt version} field, select the Qt version to use for
|
||||||
building the project. You can add Qt versions to the list if they
|
building the project. You can add Qt versions to the list if they
|
||||||
are installed on the development PC, but were not detected
|
are installed on the development PC, but were not detected
|
||||||
@@ -137,6 +140,10 @@
|
|||||||
\uicontrol Change to edit the variables of the CMake configuration
|
\uicontrol Change to edit the variables of the CMake configuration
|
||||||
for the kit.
|
for the kit.
|
||||||
|
|
||||||
|
\li In the \uicontrol {Additional Qbs profile settings} field, select
|
||||||
|
\uicontrol Change to add settings to Qbs build profiles. For more
|
||||||
|
information, see \l {Editing Build Profiles}.
|
||||||
|
|
||||||
\endlist
|
\endlist
|
||||||
|
|
||||||
\QC uses the \e {default kit} if it does not have enough information to
|
\QC uses the \e {default kit} if it does not have enough information to
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
/*!
|
/*!
|
||||||
//! [run settings qnx]
|
//! [run settings qnx]
|
||||||
|
|
||||||
\section2 Specifying Run Settings for QNX Devices
|
\section1 Specifying Run Settings for QNX Devices
|
||||||
|
|
||||||
To run and debug an application on a QNX device, you must
|
To run and debug an application on a QNX device, you must
|
||||||
create connections from the development PC to the device. Click
|
create connections from the development PC to the device. Click
|
||||||
|
|||||||
@@ -42,14 +42,13 @@
|
|||||||
. Select the icon to open the toolbar.
|
. Select the icon to open the toolbar.
|
||||||
|
|
||||||
To open toolbars immediately when you select a QML type, select
|
To open toolbars immediately when you select a QML type, select
|
||||||
\uicontrol{Tools > Options > Qt Quick > Qt Quick Toolbar > Always show Qt Quick
|
\uicontrol Tools > \uicontrol Options > \uicontrol {Qt Quick} >
|
||||||
Toolbar}.
|
\uicontrol {QML/JS Editing} > \uicontrol {Always show Qt Quick Toolbar}.
|
||||||
|
|
||||||
Drag the toolbar to pin it to another location. Select
|
Drag the toolbar to pin it to another location. Select
|
||||||
\inlineimage qml-toolbar-pin.png
|
\inlineimage qml-toolbar-pin.png
|
||||||
to unpin the toolbar and move it to its default location. To pin toolbars
|
to unpin the toolbar and move it to its default location. To pin toolbars
|
||||||
by default, select \uicontrol{Tools > Options > Qt Quick > Qt Quick Toolbar
|
by default, select \uicontrol {Pin Quick Toolbar}.
|
||||||
> Pin Quick Toolbar}.
|
|
||||||
|
|
||||||
\section1 Previewing Images
|
\section1 Previewing Images
|
||||||
|
|
||||||
|
|||||||
@@ -741,7 +741,7 @@ class Dumper(DumperBase):
|
|||||||
typeName = "'" + typeName + "'"
|
typeName = "'" + typeName + "'"
|
||||||
# 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
|
# 'class' is needed, see http://sourceware.org/bugzilla/show_bug.cgi?id=11912
|
||||||
#exp = '((class %s*)%s)->%s(%s)' % (typeName, value.laddress, function, arg)
|
#exp = '((class %s*)%s)->%s(%s)' % (typeName, value.laddress, function, arg)
|
||||||
addr = value.laddress
|
addr = value.address()
|
||||||
if addr is None:
|
if addr is None:
|
||||||
addr = self.pokeValue(value)
|
addr = self.pokeValue(value)
|
||||||
#warn('PTR: %s -> %s(%s)' % (value, function, addr))
|
#warn('PTR: %s -> %s(%s)' % (value, function, addr))
|
||||||
@@ -750,7 +750,7 @@ class Dumper(DumperBase):
|
|||||||
result = gdb.parse_and_eval(exp)
|
result = gdb.parse_and_eval(exp)
|
||||||
#warn(' -> %s' % result)
|
#warn(' -> %s' % result)
|
||||||
res = self.fromNativeValue(result)
|
res = self.fromNativeValue(result)
|
||||||
if value.laddress is None:
|
if value.address() is None:
|
||||||
self.releaseValue(addr)
|
self.releaseValue(addr)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@@ -1056,7 +1056,7 @@ class Dumper(DumperBase):
|
|||||||
typeName = typeName[0:pos]
|
typeName = typeName[0:pos]
|
||||||
if typeName in self.qqEditable and not simpleType:
|
if typeName in self.qqEditable and not simpleType:
|
||||||
#self.qqEditable[typeName](self, expr, value)
|
#self.qqEditable[typeName](self, expr, value)
|
||||||
expr = gdb.parse_and_eval(expr)
|
expr = self.parseAndEvaluate(expr)
|
||||||
self.qqEditable[typeName](self, expr, value)
|
self.qqEditable[typeName](self, expr, value)
|
||||||
else:
|
else:
|
||||||
cmd = 'set variable (%s)=%s' % (expr, value)
|
cmd = 'set variable (%s)=%s' % (expr, value)
|
||||||
|
|||||||
@@ -1558,7 +1558,8 @@ class Dumper(DumperBase):
|
|||||||
self.target.BreakpointDelete(bp.GetID())
|
self.target.BreakpointDelete(bp.GetID())
|
||||||
res = frame.SetPC(loc.GetLoadAddress())
|
res = frame.SetPC(loc.GetLoadAddress())
|
||||||
status = 'Jumped.' if res else 'Cannot jump.'
|
status = 'Jumped.' if res else 'Cannot jump.'
|
||||||
self.reportResult(self.describeStatus(status) + self.describeLocation(frame), args)
|
self.report(self.describeLocation(frame))
|
||||||
|
self.reportResult(self.describeStatus(status), args)
|
||||||
|
|
||||||
def breakList(self):
|
def breakList(self):
|
||||||
result = lldb.SBCommandReturnObject()
|
result = lldb.SBCommandReturnObject()
|
||||||
|
|||||||
@@ -46,6 +46,11 @@ def qform__QByteArray():
|
|||||||
return [Latin1StringFormat, SeparateLatin1StringFormat,
|
return [Latin1StringFormat, SeparateLatin1StringFormat,
|
||||||
Utf8StringFormat, SeparateUtf8StringFormat ]
|
Utf8StringFormat, SeparateUtf8StringFormat ]
|
||||||
|
|
||||||
|
def qedit__QByteArray(d, value, data):
|
||||||
|
d.call('void', value, 'resize', str(len(data)))
|
||||||
|
(base, size, alloc) = d.stringData(value)
|
||||||
|
d.setValues(base, 'char', [ord(c) for c in data])
|
||||||
|
|
||||||
def qdump__QByteArray(d, value):
|
def qdump__QByteArray(d, value):
|
||||||
data, size, alloc = d.byteArrayData(value)
|
data, size, alloc = d.byteArrayData(value)
|
||||||
d.check(alloc == 0 or (0 <= size and size <= alloc and alloc <= 100000000))
|
d.check(alloc == 0 or (0 <= size and size <= alloc and alloc <= 100000000))
|
||||||
@@ -1776,17 +1781,9 @@ def qdump__QVariant(d, value):
|
|||||||
|
|
||||||
def qedit__QVector(d, value, data):
|
def qedit__QVector(d, value, data):
|
||||||
values = data.split(',')
|
values = data.split(',')
|
||||||
size = len(values)
|
d.call('void', value, 'resize', str(len(values)))
|
||||||
d.call('void', value, 'resize', str(size))
|
base, vsize, valloc = d.vectorDataHelper(d.extractPointer(value))
|
||||||
innerType = value.type[0]
|
d.setValues(base, value.type[0].name, values)
|
||||||
try:
|
|
||||||
# Qt 5. Will fail on Qt 4 due to the missing 'offset' member.
|
|
||||||
offset = value['d']['offset']
|
|
||||||
base = value['d'].address() + offset
|
|
||||||
except:
|
|
||||||
# Qt 4.
|
|
||||||
base = value['p']['array'].pointer()
|
|
||||||
d.setValues(base, innerType, values)
|
|
||||||
|
|
||||||
|
|
||||||
def qform__QVector():
|
def qform__QVector():
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ def qdump__std__complex(d, value):
|
|||||||
innerType = value.type[0]
|
innerType = value.type[0]
|
||||||
(real, imag) = value.split('{%s}{%s}' % (innerType.name, innerType.name))
|
(real, imag) = value.split('{%s}{%s}' % (innerType.name, innerType.name))
|
||||||
d.putValue("(%s, %s)" % (real.display(), imag.display()))
|
d.putValue("(%s, %s)" % (real.display(), imag.display()))
|
||||||
|
d.putNumChild(2)
|
||||||
if d.isExpanded():
|
if d.isExpanded():
|
||||||
with Children(d, 2, childType=innerType):
|
with Children(d, 2, childType=innerType):
|
||||||
d.putSubItem("real", real)
|
d.putSubItem("real", real)
|
||||||
@@ -887,10 +888,10 @@ def qedit__std__vector(d, value, data):
|
|||||||
import gdb
|
import gdb
|
||||||
values = data.split(',')
|
values = data.split(',')
|
||||||
n = len(values)
|
n = len(values)
|
||||||
innerType = value.type[0]
|
innerType = value.type[0].name
|
||||||
cmd = "set $d = (%s*)calloc(sizeof(%s)*%s,1)" % (innerType, innerType, n)
|
cmd = "set $d = (%s*)calloc(sizeof(%s)*%s,1)" % (innerType, innerType, n)
|
||||||
gdb.execute(cmd)
|
gdb.execute(cmd)
|
||||||
cmd = "set {void*[3]}%s = {$d, $d+%s, $d+%s}" % (value.address, n, n)
|
cmd = "set {void*[3]}%s = {$d, $d+%s, $d+%s}" % (value.address(), n, n)
|
||||||
gdb.execute(cmd)
|
gdb.execute(cmd)
|
||||||
cmd = "set (%s[%d])*$d={%s}" % (innerType, n, data)
|
cmd = "set (%s[%d])*$d={%s}" % (innerType, n, data)
|
||||||
gdb.execute(cmd)
|
gdb.execute(cmd)
|
||||||
@@ -975,11 +976,23 @@ def qdump__std____debug__vector(d, value):
|
|||||||
|
|
||||||
|
|
||||||
def qedit__std__string(d, value, data):
|
def qedit__std__string(d, value, data):
|
||||||
d.call(value, "assign", '"%s"' % data.replace('"', '\\"'))
|
d.call('void', value, 'assign', '"%s"' % data.replace('"', '\\"'))
|
||||||
|
|
||||||
def qedit__string(d, expr, value):
|
def qedit__string(d, expr, value):
|
||||||
qedit__std__string(d, expr, value)
|
qedit__std__string(d, expr, value)
|
||||||
|
|
||||||
|
def qedit__std____cxx11__string(d, expr, value):
|
||||||
|
qedit__std__string(d, expr, value)
|
||||||
|
|
||||||
|
def qedit__std__wstring(d, value, data):
|
||||||
|
d.call('void', value, 'assign', 'L"%s"' % data.replace('"', '\\"'))
|
||||||
|
|
||||||
|
def qedit__wstring(d, expr, value):
|
||||||
|
qedit__std__wstring(d, expr, value)
|
||||||
|
|
||||||
|
def qedit__std____cxx11__wstring(d, expr, value):
|
||||||
|
qedit__std__wstring(d, expr, value)
|
||||||
|
|
||||||
def qdump__string(d, value):
|
def qdump__string(d, value):
|
||||||
qdump__std__string(d, value)
|
qdump__std__string(d, value)
|
||||||
|
|
||||||
|
|||||||
@@ -130,8 +130,6 @@ Item {
|
|||||||
exportMenuItem.enabled = !backendValue.isAttachedProperty()
|
exportMenuItem.enabled = !backendValue.isAttachedProperty()
|
||||||
}
|
}
|
||||||
|
|
||||||
onAboutToHide: menuLoader.active = false
|
|
||||||
|
|
||||||
Controls.MenuItem {
|
Controls.MenuItem {
|
||||||
text: qsTr("Reset")
|
text: qsTr("Reset")
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ QtcProduct {
|
|||||||
targetName: qtc.ide_app_target
|
targetName: qtc.ide_app_target
|
||||||
version: qtc.qtcreator_version
|
version: qtc.qtcreator_version
|
||||||
|
|
||||||
installDir: bundle.isBundle ? qtc.ide_app_path : qtc.ide_bin_path
|
property bool isBundle: qbs.targetOS.contains("darwin") && bundle.isBundle
|
||||||
installTags: bundle.isBundle ? ["bundle.content"] : base
|
installDir: isBundle ? qtc.ide_app_path : qtc.ide_bin_path
|
||||||
installSourceBase: bundle.isBundle ? buildDirectory : base
|
installTags: isBundle ? ["bundle.content"] : base
|
||||||
|
installSourceBase: isBundle ? buildDirectory : base
|
||||||
property bool qtcRunnable: true
|
property bool qtcRunnable: true
|
||||||
|
|
||||||
cpp.rpaths: qbs.targetOS.contains("macos") ? ["@executable_path/../Frameworks"]
|
cpp.rpaths: qbs.targetOS.contains("macos") ? ["@executable_path/../Frameworks"]
|
||||||
|
|||||||
@@ -1387,6 +1387,8 @@ void ClassOrNamespace::instantiateNestedClasses(ClassOrNamespace *enclosingTempl
|
|||||||
void ClassOrNamespace::NestedClassInstantiator::instantiate(ClassOrNamespace *enclosingTemplateClass,
|
void ClassOrNamespace::NestedClassInstantiator::instantiate(ClassOrNamespace *enclosingTemplateClass,
|
||||||
ClassOrNamespace *enclosingTemplateClassInstantiation)
|
ClassOrNamespace *enclosingTemplateClassInstantiation)
|
||||||
{
|
{
|
||||||
|
if (_alreadyConsideredNestedClassInstantiations.size() >= 3)
|
||||||
|
return;
|
||||||
if (_alreadyConsideredNestedClassInstantiations.contains(enclosingTemplateClass))
|
if (_alreadyConsideredNestedClassInstantiations.contains(enclosingTemplateClass))
|
||||||
return;
|
return;
|
||||||
_alreadyConsideredNestedClassInstantiations.insert(enclosingTemplateClass);
|
_alreadyConsideredNestedClassInstantiations.insert(enclosingTemplateClass);
|
||||||
|
|||||||
@@ -326,6 +326,11 @@ static bool allowAutoClosingBrace(const QTextCursor &cursor,
|
|||||||
|
|
||||||
int prevState;
|
int prevState;
|
||||||
const Tokens tokens = getTokens(cursor, prevState);
|
const Tokens tokens = getTokens(cursor, prevState);
|
||||||
|
|
||||||
|
const Token token = tokenAtPosition(tokens, cursor.positionInBlock());
|
||||||
|
if (token.isStringLiteral())
|
||||||
|
return false;
|
||||||
|
|
||||||
if (isAfterNamespaceDefinition(tokens, cursor.positionInBlock()))
|
if (isAfterNamespaceDefinition(tokens, cursor.positionInBlock()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|||||||
@@ -1781,6 +1781,9 @@ const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
|
|||||||
addMessage(ErrInvalidMember, idPart->identifierToken, propertyName, objectValue->className());
|
addMessage(ErrInvalidMember, idPart->identifierToken, propertyName, objectValue->className());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
// resolve references
|
||||||
|
if (const Reference *ref = value->asReference())
|
||||||
|
value = _context->lookupReference(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -104,30 +104,17 @@ namespace Utils {
|
|||||||
|
|
||||||
struct Context // Basic context containing element name string constants.
|
struct Context // Basic context containing element name string constants.
|
||||||
{
|
{
|
||||||
Context();
|
Context() {}
|
||||||
|
const QString qtCreatorElement = QString("qtcreator");
|
||||||
const QString qtCreatorElement;
|
const QString dataElement = QString("data");
|
||||||
const QString dataElement;
|
const QString variableElement = QString("variable");
|
||||||
const QString variableElement;
|
const QString typeAttribute = QString("type");
|
||||||
const QString typeAttribute;
|
const QString valueElement = QString("value");
|
||||||
const QString valueElement;
|
const QString valueListElement = QString("valuelist");
|
||||||
const QString valueListElement;
|
const QString valueMapElement = QString("valuemap");
|
||||||
const QString valueMapElement;
|
const QString keyAttribute = QString("key");
|
||||||
const QString keyAttribute;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Context::Context() :
|
|
||||||
qtCreatorElement(QLatin1String("qtcreator")),
|
|
||||||
dataElement(QLatin1String("data")),
|
|
||||||
variableElement(QLatin1String("variable")),
|
|
||||||
typeAttribute(QLatin1String("type")),
|
|
||||||
valueElement(QLatin1String("value")),
|
|
||||||
valueListElement(QLatin1String("valuelist")),
|
|
||||||
valueMapElement(QLatin1String("valuemap")),
|
|
||||||
keyAttribute(QLatin1String("key"))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ParseValueStackEntry
|
struct ParseValueStackEntry
|
||||||
{
|
{
|
||||||
explicit ParseValueStackEntry(QVariant::Type t = QVariant::Invalid, const QString &k = QString()) : type(t), key(k) {}
|
explicit ParseValueStackEntry(QVariant::Type t = QVariant::Invalid, const QString &k = QString()) : type(t), key(k) {}
|
||||||
@@ -225,7 +212,6 @@ QVariantMap ParseContext::parse(QFile &file)
|
|||||||
qWarning("Error reading %s:%d: %s", qPrintable(file.fileName()),
|
qWarning("Error reading %s:%d: %s", qPrintable(file.fileName()),
|
||||||
int(r.lineNumber()), qPrintable(r.errorString()));
|
int(r.lineNumber()), qPrintable(r.errorString()));
|
||||||
return QVariantMap();
|
return QVariantMap();
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
} // switch token
|
} // switch token
|
||||||
|
|||||||
@@ -689,6 +689,20 @@ void TreeItem::insertChild(int pos, TreeItem *item)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TreeItem::removeChildAt(int pos)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(0 <= pos && pos < m_children.count(), return);
|
||||||
|
|
||||||
|
if (m_model) {
|
||||||
|
QModelIndex idx = index();
|
||||||
|
m_model->beginRemoveRows(idx, pos, pos);
|
||||||
|
removeItemAt(pos);
|
||||||
|
m_model->endRemoveRows();
|
||||||
|
} else {
|
||||||
|
removeItemAt(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void TreeItem::removeChildren()
|
void TreeItem::removeChildren()
|
||||||
{
|
{
|
||||||
if (childCount() == 0)
|
if (childCount() == 0)
|
||||||
@@ -863,6 +877,15 @@ void TreeItem::clear()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TreeItem::removeItemAt(int pos)
|
||||||
|
{
|
||||||
|
TreeItem *item = m_children.at(pos);
|
||||||
|
item->m_model = nullptr;
|
||||||
|
item->m_parent = nullptr;
|
||||||
|
delete item;
|
||||||
|
m_children.removeAt(pos);
|
||||||
|
}
|
||||||
|
|
||||||
void TreeItem::expand()
|
void TreeItem::expand()
|
||||||
{
|
{
|
||||||
QTC_ASSERT(m_model, return);
|
QTC_ASSERT(m_model, return);
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ public:
|
|||||||
void prependChild(TreeItem *item);
|
void prependChild(TreeItem *item);
|
||||||
void appendChild(TreeItem *item);
|
void appendChild(TreeItem *item);
|
||||||
void insertChild(int pos, TreeItem *item);
|
void insertChild(int pos, TreeItem *item);
|
||||||
|
void removeChildAt(int pos);
|
||||||
void removeChildren();
|
void removeChildren();
|
||||||
void sortChildren(const std::function<bool(const TreeItem *, const TreeItem *)> &cmp);
|
void sortChildren(const std::function<bool(const TreeItem *, const TreeItem *)> &cmp);
|
||||||
void update();
|
void update();
|
||||||
@@ -90,6 +91,7 @@ private:
|
|||||||
void operator=(const TreeItem &) = delete;
|
void operator=(const TreeItem &) = delete;
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
void removeItemAt(int pos);
|
||||||
void propagateModel(BaseTreeModel *m);
|
void propagateModel(BaseTreeModel *m);
|
||||||
|
|
||||||
TreeItem *m_parent; // Not owned.
|
TreeItem *m_parent; // Not owned.
|
||||||
|
|||||||
@@ -432,6 +432,7 @@ void Wizard::showVariables()
|
|||||||
label->setWordWrap(true);
|
label->setWordWrap(true);
|
||||||
label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
|
label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
|
||||||
scrollArea->setWidget(label);
|
scrollArea->setWidget(label);
|
||||||
|
scrollArea->setWidgetResizable(true);
|
||||||
|
|
||||||
layout->addWidget(scrollArea);
|
layout->addWidget(scrollArea);
|
||||||
layout->addWidget(buttons);
|
layout->addWidget(buttons);
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ AndroidQmlProfilerSupport::AndroidQmlProfilerSupport(RunControl *runControl)
|
|||||||
setDisplayName("AndroidQmlProfilerSupport");
|
setDisplayName("AndroidQmlProfilerSupport");
|
||||||
|
|
||||||
auto runner = new AndroidRunner(runControl);
|
auto runner = new AndroidRunner(runControl);
|
||||||
addDependency(runner);
|
addStartDependency(runner);
|
||||||
|
|
||||||
auto profiler = runControl->createWorker(runControl->runMode());
|
auto profiler = runControl->createWorker(runControl->runMode());
|
||||||
profiler->addDependency(this);
|
profiler->addStartDependency(this);
|
||||||
|
|
||||||
connect(runner, &AndroidRunner::qmlServerReady, [this, runner, profiler](const QUrl &server) {
|
connect(runner, &AndroidRunner::qmlServerReady, [this, runner, profiler](const QUrl &server) {
|
||||||
profiler->recordData("QmlServerUrl", server);
|
profiler->recordData("QmlServerUrl", server);
|
||||||
|
|||||||
@@ -142,11 +142,12 @@ bool AndroidBuildApkStep::init(QList<const BuildStep *> &earlierSteps)
|
|||||||
if (!version)
|
if (!version)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (AndroidConfigurations::currentConfig().sdkToolsVersion() >= gradleScriptRevokedSdkVersion &&
|
const QVersionNumber sdkToolsVersion = AndroidConfigurations::currentConfig().sdkToolsVersion();
|
||||||
|
if (sdkToolsVersion >= gradleScriptRevokedSdkVersion &&
|
||||||
QVersionNumber::fromString(version->qtVersionString()) < gradleScriptsContainedQtVersion) {
|
QVersionNumber::fromString(version->qtVersionString()) < gradleScriptsContainedQtVersion) {
|
||||||
emit addOutput(tr("The installed SDK tools version (%1) does not include Gradle scripts. The "
|
emit addOutput(tr("The installed SDK tools version (%1) does not include Gradle scripts. The "
|
||||||
"minimum Qt version required for Gradle build to work is %2")
|
"minimum Qt version required for Gradle build to work is %2")
|
||||||
.arg(gradleScriptRevokedSdkVersion.toString())
|
.arg(sdkToolsVersion.toString())
|
||||||
.arg(gradleScriptsContainedQtVersion.toString()), OutputFormat::Stderr);
|
.arg(gradleScriptsContainedQtVersion.toString()), OutputFormat::Stderr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ AndroidDebugSupport::AndroidDebugSupport(RunControl *runControl)
|
|||||||
{
|
{
|
||||||
setDisplayName("AndroidDebugger");
|
setDisplayName("AndroidDebugger");
|
||||||
m_runner = new AndroidRunner(runControl);
|
m_runner = new AndroidRunner(runControl);
|
||||||
addDependency(m_runner);
|
addStartDependency(m_runner);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidDebugSupport::start()
|
void AndroidDebugSupport::start()
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ QSet<QString> GTestTreeItem::internalTargets() const
|
|||||||
const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject());
|
const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject());
|
||||||
for (const CppTools::ProjectPart::Ptr projectPart : projectInfo.projectParts()) {
|
for (const CppTools::ProjectPart::Ptr projectPart : projectInfo.projectParts()) {
|
||||||
if (projectPart->projectFile == proFile())
|
if (projectPart->projectFile == proFile())
|
||||||
result.insert(projectPart->buildSystemTarget);
|
result.insert(projectPart->buildSystemTarget + '|' + projectPart->projectFile);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,31 +193,10 @@ QList<TestConfiguration *> QuickTestTreeItem::getSelectedTestConfigurations() co
|
|||||||
|
|
||||||
QuickTestConfiguration *tc = nullptr;
|
QuickTestConfiguration *tc = nullptr;
|
||||||
QHash<QString, QuickTestConfiguration *> foundProFiles;
|
QHash<QString, QuickTestConfiguration *> foundProFiles;
|
||||||
// unnamed Quick Tests must be handled first
|
|
||||||
if (TestTreeItem *unnamed = unnamedQuickTests()) {
|
|
||||||
for (int childRow = 0, ccount = unnamed->childCount(); childRow < ccount; ++ childRow) {
|
|
||||||
const TestTreeItem *grandChild = unnamed->childItem(childRow);
|
|
||||||
const QString &proFile = grandChild->proFile();
|
|
||||||
if (foundProFiles.contains(proFile)) {
|
|
||||||
QTC_ASSERT(tc,
|
|
||||||
qWarning() << "Illegal state (unnamed Quick Test listed as named)";
|
|
||||||
return QList<TestConfiguration *>());
|
|
||||||
foundProFiles[proFile]->setTestCaseCount(tc->testCaseCount() + 1);
|
|
||||||
} else {
|
|
||||||
tc = new QuickTestConfiguration;
|
|
||||||
tc->setTestCaseCount(1);
|
|
||||||
tc->setUnnamedOnly(true);
|
|
||||||
tc->setProjectFile(proFile);
|
|
||||||
tc->setProject(project);
|
|
||||||
tc->setInternalTargets(grandChild->internalTargets());
|
|
||||||
foundProFiles.insert(proFile, tc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int row = 0, count = childCount(); row < count; ++row) {
|
for (int row = 0, count = childCount(); row < count; ++row) {
|
||||||
const TestTreeItem *child = childItem(row);
|
const TestTreeItem *child = childItem(row);
|
||||||
// unnamed Quick Tests have been handled separately already
|
// unnamed Quick Tests cannot get selected explicitly
|
||||||
if (child->name().isEmpty())
|
if (child->name().isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -239,15 +218,8 @@ QList<TestConfiguration *> QuickTestTreeItem::getSelectedTestConfigurations() co
|
|||||||
if (foundProFiles.contains(child->proFile())) {
|
if (foundProFiles.contains(child->proFile())) {
|
||||||
tc = foundProFiles[child->proFile()];
|
tc = foundProFiles[child->proFile()];
|
||||||
QStringList oldFunctions(tc->testCases());
|
QStringList oldFunctions(tc->testCases());
|
||||||
// if oldFunctions.size() is 0 this test configuration is used for at least one
|
oldFunctions << testFunctions;
|
||||||
// unnamed test case
|
tc->setTestCases(oldFunctions);
|
||||||
if (oldFunctions.size() == 0) {
|
|
||||||
tc->setTestCaseCount(tc->testCaseCount() + testFunctions.size());
|
|
||||||
tc->setUnnamedOnly(false);
|
|
||||||
} else {
|
|
||||||
oldFunctions << testFunctions;
|
|
||||||
tc->setTestCases(oldFunctions);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
tc = new QuickTestConfiguration;
|
tc = new QuickTestConfiguration;
|
||||||
tc->setTestCases(testFunctions);
|
tc->setTestCases(testFunctions);
|
||||||
@@ -321,7 +293,7 @@ QSet<QString> QuickTestTreeItem::internalTargets() const
|
|||||||
const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject());
|
const auto projectInfo = cppMM->projectInfo(ProjectExplorer::SessionManager::startupProject());
|
||||||
for (const CppTools::ProjectPart::Ptr projectPart : projectInfo.projectParts()) {
|
for (const CppTools::ProjectPart::Ptr projectPart : projectInfo.projectParts()) {
|
||||||
if (projectPart->projectFile == proFile()) {
|
if (projectPart->projectFile == proFile()) {
|
||||||
result.insert(projectPart->buildSystemTarget);
|
result.insert(projectPart->buildSystemTarget + '|' + projectPart->projectFile);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
#include <projectexplorer/buildconfiguration.h>
|
#include <projectexplorer/buildconfiguration.h>
|
||||||
#include <projectexplorer/buildtargetinfo.h>
|
#include <projectexplorer/buildtargetinfo.h>
|
||||||
|
#include <projectexplorer/deploymentdata.h>
|
||||||
#include <projectexplorer/environmentaspect.h>
|
#include <projectexplorer/environmentaspect.h>
|
||||||
#include <projectexplorer/kitinformation.h>
|
#include <projectexplorer/kitinformation.h>
|
||||||
#include <projectexplorer/runnables.h>
|
#include <projectexplorer/runnables.h>
|
||||||
@@ -40,6 +41,10 @@
|
|||||||
#include <projectexplorer/session.h>
|
#include <projectexplorer/session.h>
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
|
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
static Q_LOGGING_CATEGORY(LOG, "qtc.autotest.testconfiguration")
|
||||||
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
|
|
||||||
namespace Autotest {
|
namespace Autotest {
|
||||||
@@ -61,6 +66,13 @@ static bool isLocal(RunConfiguration *runConfiguration)
|
|||||||
return DeviceTypeKitInformation::deviceTypeId(kit) == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE;
|
return DeviceTypeKitInformation::deviceTypeId(kit) == ProjectExplorer::Constants::DESKTOP_DEVICE_TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString ensureExeEnding(const QString& file)
|
||||||
|
{
|
||||||
|
if (!Utils::HostOsInfo::isWindowsHost() || file.isEmpty() || file.toLower().endsWith(".exe"))
|
||||||
|
return file;
|
||||||
|
return Utils::HostOsInfo::withExecutableSuffix(file);
|
||||||
|
}
|
||||||
|
|
||||||
void TestConfiguration::completeTestInformation(int runMode)
|
void TestConfiguration::completeTestInformation(int runMode)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(!m_projectFile.isEmpty(), return);
|
QTC_ASSERT(!m_projectFile.isEmpty(), return);
|
||||||
@@ -73,8 +85,12 @@ void TestConfiguration::completeTestInformation(int runMode)
|
|||||||
Target *target = project->activeTarget();
|
Target *target = project->activeTarget();
|
||||||
if (!target)
|
if (!target)
|
||||||
return;
|
return;
|
||||||
|
qCDebug(LOG) << "ActiveTargetName\n " << target->displayName();
|
||||||
|
if (const auto kit = target->kit())
|
||||||
|
qCDebug(LOG) << "SupportedPlatforms\n " << kit->supportedPlatforms();
|
||||||
|
|
||||||
const QSet<QString> buildSystemTargets = m_buildTargets;
|
const QSet<QString> buildSystemTargets = m_buildTargets;
|
||||||
|
qCDebug(LOG) << "BuildSystemTargets\n " << buildSystemTargets;
|
||||||
const BuildTargetInfo targetInfo
|
const BuildTargetInfo targetInfo
|
||||||
= Utils::findOrDefault(target->applicationTargets().list,
|
= Utils::findOrDefault(target->applicationTargets().list,
|
||||||
[&buildSystemTargets] (const BuildTargetInfo &bti) {
|
[&buildSystemTargets] (const BuildTargetInfo &bti) {
|
||||||
@@ -82,44 +98,81 @@ void TestConfiguration::completeTestInformation(int runMode)
|
|||||||
const QStringList targWithProjectFile = b.split('|');
|
const QStringList targWithProjectFile = b.split('|');
|
||||||
if (targWithProjectFile.size() != 2) // some build targets might miss the project file
|
if (targWithProjectFile.size() != 2) // some build targets might miss the project file
|
||||||
return false;
|
return false;
|
||||||
return targWithProjectFile.at(0) == bti.targetName
|
return !bti.targetFilePath.isEmpty() && targWithProjectFile.at(0) == bti.targetName
|
||||||
&& targWithProjectFile.at(1).startsWith(bti.projectFilePath.toString());
|
&& targWithProjectFile.at(1).startsWith(bti.projectFilePath.toString());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
QString executable = targetInfo.targetFilePath.toString(); // empty if BTI default created
|
if (!QTC_GUARD(!targetInfo.targetFilePath.isEmpty())) { // empty if BTI default created
|
||||||
if (Utils::HostOsInfo::isWindowsHost() && !executable.isEmpty()
|
qCDebug(LOG) << "BuildTargetInfos";
|
||||||
&& !executable.toLower().endsWith(".exe")) {
|
for (const BuildTargetInfo &bti : target->applicationTargets().list)
|
||||||
executable = Utils::HostOsInfo::withExecutableSuffix(executable);
|
qCDebug(LOG) << " " << bti.targetName << bti.projectFilePath << bti.targetFilePath;
|
||||||
|
}
|
||||||
|
const QString localExecutable = ensureExeEnding(targetInfo.targetFilePath.toString());
|
||||||
|
|
||||||
|
QString buildBase;
|
||||||
|
if (auto buildConfig = target->activeBuildConfiguration()) {
|
||||||
|
buildBase = buildConfig->buildDirectory().toString();
|
||||||
|
const QString projBase = project->projectDirectory().toString();
|
||||||
|
if (m_projectFile.startsWith(projBase))
|
||||||
|
m_buildDir = QFileInfo(buildBase + m_projectFile.mid(projBase.length())).absolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (RunConfiguration *runConfig : target->runConfigurations()) {
|
// deployment information should get taken into account, but it pretty much seems as if
|
||||||
if (!isLocal(runConfig)) // TODO add device support
|
// each build system uses it differently
|
||||||
continue;
|
const DeploymentData &deployData = target->deploymentData();
|
||||||
|
const DeployableFile deploy = deployData.deployableForLocalFile(localExecutable);
|
||||||
|
// we might have a deployable executable
|
||||||
|
const QString deployedExecutable = ensureExeEnding((deploy.isValid() && deploy.isExecutable())
|
||||||
|
? QDir::cleanPath(deploy.remoteFilePath()) : localExecutable);
|
||||||
|
|
||||||
const QString bst = runConfig->buildSystemTarget() + '|' + m_projectFile;
|
qCDebug(LOG) << " LocalExecutable" << localExecutable;
|
||||||
if (buildSystemTargets.contains(bst)) {
|
qCDebug(LOG) << " DeployedExecutable" << deployedExecutable;
|
||||||
Runnable runnable = runConfig->runnable();
|
qCDebug(LOG) << "Iterating run configurations";
|
||||||
if (!runnable.is<StandardRunnable>())
|
for (RunConfiguration *runConfig : target->runConfigurations()) {
|
||||||
continue;
|
qCDebug(LOG) << "RunConfiguration" << runConfig->id();
|
||||||
StandardRunnable stdRunnable = runnable.as<StandardRunnable>();
|
if (!isLocal(runConfig)) { // TODO add device support
|
||||||
// TODO this might pick up the wrong executable
|
qCDebug(LOG) << " Skipped as not being local";
|
||||||
m_executableFile = stdRunnable.executable;
|
continue;
|
||||||
if (Utils::HostOsInfo::isWindowsHost() && !m_executableFile.isEmpty()
|
}
|
||||||
&& !m_executableFile.toLower().endsWith(".exe")) {
|
|
||||||
m_executableFile = Utils::HostOsInfo::withExecutableSuffix(m_executableFile);
|
Runnable runnable = runConfig->runnable();
|
||||||
}
|
if (!runnable.is<StandardRunnable>()) {
|
||||||
|
qCDebug(LOG) << " Skipped as not being a StandardRunnable";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
StandardRunnable stdRunnable = runnable.as<StandardRunnable>();
|
||||||
|
// not the best approach - but depending on the build system and whether the executables
|
||||||
|
// are going to get installed or not we have to soften the condition...
|
||||||
|
const QString ¤tExecutable = ensureExeEnding(stdRunnable.executable);
|
||||||
|
const QString currentBST = runConfig->buildSystemTarget() + '|';
|
||||||
|
qCDebug(LOG) << " CurrentExecutable" << currentExecutable;
|
||||||
|
qCDebug(LOG) << " BST of RunConfig" << currentBST;
|
||||||
|
const bool isQbs = runConfig->id().toString().startsWith("Qbs.RunConfiguration:"); // BAD!
|
||||||
|
if ((localExecutable == currentExecutable)
|
||||||
|
|| (deployedExecutable == currentExecutable)
|
||||||
|
|| (isQbs && Utils::anyOf(buildSystemTargets, [currentBST] (const QString &b) {
|
||||||
|
return b.startsWith(currentBST);
|
||||||
|
}))) {
|
||||||
|
qCDebug(LOG) << " Using this RunConfig.";
|
||||||
|
m_executableFile = currentExecutable;
|
||||||
m_displayName = runConfig->displayName();
|
m_displayName = runConfig->displayName();
|
||||||
m_workingDir = Utils::FileUtils::normalizePathName(stdRunnable.workingDirectory);
|
m_workingDir = Utils::FileUtils::normalizePathName(stdRunnable.workingDirectory);
|
||||||
m_environment = stdRunnable.environment;
|
m_environment = stdRunnable.environment;
|
||||||
m_project = project;
|
m_project = project;
|
||||||
if (runMode == TestRunner::Debug)
|
if (runMode == TestRunner::Debug)
|
||||||
m_runConfig = new TestRunConfiguration(runConfig->target(), this);
|
m_runConfig = new TestRunConfiguration(runConfig->target(), this);
|
||||||
if (m_executableFile == executable) // we can find a better runConfig if no match
|
break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunConfiguration for this target could be explicitly removed or not created at all
|
// RunConfiguration for this target could be explicitly removed or not created at all
|
||||||
if (m_displayName.isEmpty() && !executable.isEmpty()) {
|
// or we might have end up using the (wrong) path of a locally installed executable
|
||||||
|
// for this case try the original executable path of the BuildTargetInfo (the executable
|
||||||
|
// before installation) to have at least something to execute
|
||||||
|
if (m_executableFile.isEmpty() && !localExecutable.isEmpty())
|
||||||
|
m_executableFile = localExecutable;
|
||||||
|
if (m_displayName.isEmpty() && !m_executableFile.isEmpty()) {
|
||||||
|
qCDebug(LOG) << " Fallback";
|
||||||
// we failed to find a valid runconfiguration - but we've got the executable already
|
// we failed to find a valid runconfiguration - but we've got the executable already
|
||||||
if (auto rc = target->activeRunConfiguration()) {
|
if (auto rc = target->activeRunConfiguration()) {
|
||||||
if (isLocal(rc)) { // FIXME for now only Desktop support
|
if (isLocal(rc)) { // FIXME for now only Desktop support
|
||||||
@@ -127,26 +180,21 @@ void TestConfiguration::completeTestInformation(int runMode)
|
|||||||
if (runnable.is<StandardRunnable>()) {
|
if (runnable.is<StandardRunnable>()) {
|
||||||
StandardRunnable stdRunnable = runnable.as<StandardRunnable>();
|
StandardRunnable stdRunnable = runnable.as<StandardRunnable>();
|
||||||
m_environment = stdRunnable.environment;
|
m_environment = stdRunnable.environment;
|
||||||
m_executableFile = executable;
|
|
||||||
m_project = project;
|
m_project = project;
|
||||||
m_guessedConfiguration = true;
|
m_guessedConfiguration = true;
|
||||||
m_guessedFrom = rc->displayName();
|
m_guessedFrom = rc->displayName();
|
||||||
if (runMode == TestRunner::Debug)
|
if (runMode == TestRunner::Debug)
|
||||||
m_runConfig = new TestRunConfiguration(rc->target(), this);
|
m_runConfig = new TestRunConfiguration(rc->target(), this);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
qCDebug(LOG) << "not using the fallback as the current active run configuration "
|
||||||
|
"appears to be non-Desktop";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auto buildConfig = target->activeBuildConfiguration()) {
|
|
||||||
const QString buildBase = buildConfig->buildDirectory().toString();
|
|
||||||
const QString projBase = project->projectDirectory().toString();
|
|
||||||
if (m_projectFile.startsWith(projBase))
|
|
||||||
m_buildDir = QFileInfo(buildBase + m_projectFile.mid(projBase.length())).absolutePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_displayName.isEmpty()) // happens e.g. when guessing the TestConfiguration or error
|
if (m_displayName.isEmpty()) // happens e.g. when guessing the TestConfiguration or error
|
||||||
m_displayName = buildSystemTargets.isEmpty() ? "unknown" : (*buildSystemTargets.begin()).split('|').first();
|
m_displayName = (*buildSystemTargets.begin()).split('|').first();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -291,7 +291,7 @@ QSet<QString> TestTreeItem::internalTargets() const
|
|||||||
const QList<CppTools::ProjectPart::Ptr> projectParts = cppMM->projectPart(filePath());
|
const QList<CppTools::ProjectPart::Ptr> projectParts = cppMM->projectPart(filePath());
|
||||||
QSet<QString> targets;
|
QSet<QString> targets;
|
||||||
for (const CppTools::ProjectPart::Ptr part : projectParts)
|
for (const CppTools::ProjectPart::Ptr part : projectParts)
|
||||||
targets.insert(part->buildSystemTarget);
|
targets.insert(part->buildSystemTarget + '|' + part->projectFile);
|
||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ BareMetalDebugSupport::BareMetalDebugSupport(RunControl *runControl)
|
|||||||
r.commandLineArguments = Utils::QtcProcess::joinArgs(p->arguments(), Utils::HostOsInfo::hostOs());
|
r.commandLineArguments = Utils::QtcProcess::joinArgs(p->arguments(), Utils::HostOsInfo::hostOs());
|
||||||
m_gdbServer = new SimpleTargetRunner(runControl);
|
m_gdbServer = new SimpleTargetRunner(runControl);
|
||||||
m_gdbServer->setRunnable(r);
|
m_gdbServer->setRunnable(r);
|
||||||
addDependency(m_gdbServer);
|
addStartDependency(m_gdbServer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,10 +32,10 @@
|
|||||||
#include <cplusplus/Token.h>
|
#include <cplusplus/Token.h>
|
||||||
|
|
||||||
#include <texteditor/completionsettings.h>
|
#include <texteditor/completionsettings.h>
|
||||||
#include <texteditor/textdocument.h>
|
|
||||||
#include <texteditor/texteditor.h>
|
|
||||||
#include <texteditor/texteditorsettings.h>
|
#include <texteditor/texteditorsettings.h>
|
||||||
|
|
||||||
|
#include <QTextCursor>
|
||||||
|
|
||||||
using namespace CPlusPlus;
|
using namespace CPlusPlus;
|
||||||
using namespace ClangBackEnd;
|
using namespace ClangBackEnd;
|
||||||
|
|
||||||
@@ -66,6 +66,14 @@ bool ClangAssistProposalItem::implicitlyApplies() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void moveToPrevChar(TextEditor::TextDocumentManipulatorInterface &manipulator,
|
||||||
|
QTextCursor &cursor)
|
||||||
|
{
|
||||||
|
cursor.movePosition(QTextCursor::PreviousCharacter);
|
||||||
|
while (manipulator.characterAt(cursor.position()).isSpace())
|
||||||
|
cursor.movePosition(QTextCursor::PreviousCharacter);
|
||||||
|
}
|
||||||
|
|
||||||
void ClangAssistProposalItem::apply(TextEditor::TextDocumentManipulatorInterface &manipulator,
|
void ClangAssistProposalItem::apply(TextEditor::TextDocumentManipulatorInterface &manipulator,
|
||||||
int basePosition) const
|
int basePosition) const
|
||||||
{
|
{
|
||||||
@@ -78,12 +86,11 @@ void ClangAssistProposalItem::apply(TextEditor::TextDocumentManipulatorInterface
|
|||||||
bool setAutoCompleteSkipPos = false;
|
bool setAutoCompleteSkipPos = false;
|
||||||
int currentPosition = manipulator.currentPosition();
|
int currentPosition = manipulator.currentPosition();
|
||||||
|
|
||||||
bool autoParenthesesEnabled = true;
|
|
||||||
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
|
if (m_completionOperator == T_SIGNAL || m_completionOperator == T_SLOT) {
|
||||||
extraCharacters += QLatin1Char(')');
|
extraCharacters += QLatin1Char(')');
|
||||||
if (m_typedCharacter == QLatin1Char('(')) // Eat the opening parenthesis
|
if (m_typedCharacter == QLatin1Char('(')) // Eat the opening parenthesis
|
||||||
m_typedCharacter = QChar();
|
m_typedCharacter = QChar();
|
||||||
} else if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind) {
|
} else if (ccr.completionKind() == CodeCompletion::KeywordCompletionKind) {
|
||||||
CompletionChunksToTextConverter converter;
|
CompletionChunksToTextConverter converter;
|
||||||
converter.setupForKeywords();
|
converter.setupForKeywords();
|
||||||
|
|
||||||
@@ -116,7 +123,17 @@ void ClangAssistProposalItem::apply(TextEditor::TextDocumentManipulatorInterface
|
|||||||
cursor.movePosition(QTextCursor::PreviousWord);
|
cursor.movePosition(QTextCursor::PreviousWord);
|
||||||
while (manipulator.characterAt(cursor.position()) == ':')
|
while (manipulator.characterAt(cursor.position()) == ':')
|
||||||
cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 2);
|
cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 2);
|
||||||
if (manipulator.characterAt(cursor.position()) != '&') {
|
|
||||||
|
// Move to the last character in the previous word
|
||||||
|
cursor.movePosition(QTextCursor::NextWord);
|
||||||
|
moveToPrevChar(manipulator, cursor);
|
||||||
|
bool abandonParen = false;
|
||||||
|
if (manipulator.characterAt(cursor.position()) == '&') {
|
||||||
|
moveToPrevChar(manipulator, cursor);
|
||||||
|
const QChar prevChar = manipulator.characterAt(cursor.position());
|
||||||
|
abandonParen = QString("(;,{}").contains(prevChar);
|
||||||
|
}
|
||||||
|
if (!abandonParen) {
|
||||||
if (completionSettings.m_spaceAfterFunctionName)
|
if (completionSettings.m_spaceAfterFunctionName)
|
||||||
extraCharacters += QLatin1Char(' ');
|
extraCharacters += QLatin1Char(' ');
|
||||||
extraCharacters += QLatin1Char('(');
|
extraCharacters += QLatin1Char('(');
|
||||||
@@ -136,13 +153,13 @@ void ClangAssistProposalItem::apply(TextEditor::TextDocumentManipulatorInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the function takes no arguments, automatically place the closing parenthesis
|
// If the function takes no arguments, automatically place the closing parenthesis
|
||||||
if (!isOverloaded() && !ccr.hasParameters() && skipClosingParenthesis) {
|
if (!hasOverloadsWithParameters() && !ccr.hasParameters() && skipClosingParenthesis) {
|
||||||
extraCharacters += QLatin1Char(')');
|
extraCharacters += QLatin1Char(')');
|
||||||
if (endWithSemicolon) {
|
if (endWithSemicolon) {
|
||||||
extraCharacters += semicolon;
|
extraCharacters += semicolon;
|
||||||
m_typedCharacter = QChar();
|
m_typedCharacter = QChar();
|
||||||
}
|
}
|
||||||
} else if (autoParenthesesEnabled) {
|
} else {
|
||||||
const QChar lookAhead = manipulator.characterAt(manipulator.currentPosition() + 1);
|
const QChar lookAhead = manipulator.characterAt(manipulator.currentPosition() + 1);
|
||||||
if (MatchingText::shouldInsertMatchingText(lookAhead)) {
|
if (MatchingText::shouldInsertMatchingText(lookAhead)) {
|
||||||
extraCharacters += QLatin1Char(')');
|
extraCharacters += QLatin1Char(')');
|
||||||
@@ -157,19 +174,6 @@ void ClangAssistProposalItem::apply(TextEditor::TextDocumentManipulatorInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (autoInsertBrackets && data().canConvert<CompleteFunctionDeclaration>()) {
|
|
||||||
if (m_typedChar == QLatin1Char('('))
|
|
||||||
m_typedChar = QChar();
|
|
||||||
|
|
||||||
// everything from the closing parenthesis on are extra chars, to
|
|
||||||
// make sure an auto-inserted ")" gets replaced by ") const" if necessary
|
|
||||||
int closingParen = toInsert.lastIndexOf(QLatin1Char(')'));
|
|
||||||
extraChars = toInsert.mid(closingParen);
|
|
||||||
toInsert.truncate(closingParen);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append an unhandled typed character, adjusting cursor offset when it had been adjusted before
|
// Append an unhandled typed character, adjusting cursor offset when it had been adjusted before
|
||||||
@@ -315,21 +319,21 @@ quint64 ClangAssistProposalItem::hash() const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ClangAssistProposalItem::hasOverloadsWithParameters() const
|
||||||
|
{
|
||||||
|
return m_hasOverloadsWithParameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClangAssistProposalItem::setHasOverloadsWithParameters(bool hasOverloadsWithParameters)
|
||||||
|
{
|
||||||
|
m_hasOverloadsWithParameters = hasOverloadsWithParameters;
|
||||||
|
}
|
||||||
|
|
||||||
void ClangAssistProposalItem::keepCompletionOperator(unsigned compOp)
|
void ClangAssistProposalItem::keepCompletionOperator(unsigned compOp)
|
||||||
{
|
{
|
||||||
m_completionOperator = compOp;
|
m_completionOperator = compOp;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClangAssistProposalItem::isOverloaded() const
|
|
||||||
{
|
|
||||||
return !m_overloads.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClangAssistProposalItem::addOverload(const CodeCompletion &ccr)
|
|
||||||
{
|
|
||||||
m_overloads.append(ccr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClangAssistProposalItem::setCodeCompletion(const CodeCompletion &codeCompletion)
|
void ClangAssistProposalItem::setCodeCompletion(const CodeCompletion &codeCompletion)
|
||||||
{
|
{
|
||||||
m_codeCompletion = codeCompletion;
|
m_codeCompletion = codeCompletion;
|
||||||
@@ -342,4 +346,3 @@ const ClangBackEnd::CodeCompletion &ClangAssistProposalItem::codeCompletion() co
|
|||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace ClangCodeModel
|
} // namespace ClangCodeModel
|
||||||
|
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ public:
|
|||||||
|
|
||||||
void keepCompletionOperator(unsigned compOp);
|
void keepCompletionOperator(unsigned compOp);
|
||||||
|
|
||||||
bool isOverloaded() const;
|
bool hasOverloadsWithParameters() const;
|
||||||
void addOverload(const ClangBackEnd::CodeCompletion &ccr);
|
void setHasOverloadsWithParameters(bool hasOverloadsWithParameters);
|
||||||
|
|
||||||
void setCodeCompletion(const ClangBackEnd::CodeCompletion &codeCompletion);
|
void setCodeCompletion(const ClangBackEnd::CodeCompletion &codeCompletion);
|
||||||
const ClangBackEnd::CodeCompletion &codeCompletion() const;
|
const ClangBackEnd::CodeCompletion &codeCompletion() const;
|
||||||
@@ -62,6 +62,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
ClangBackEnd::CodeCompletion m_codeCompletion;
|
ClangBackEnd::CodeCompletion m_codeCompletion;
|
||||||
QList<ClangBackEnd::CodeCompletion> m_overloads;
|
QList<ClangBackEnd::CodeCompletion> m_overloads;
|
||||||
|
bool m_hasOverloadsWithParameters = false;
|
||||||
QString m_text;
|
QString m_text;
|
||||||
unsigned m_completionOperator;
|
unsigned m_completionOperator;
|
||||||
mutable QChar m_typedCharacter;
|
mutable QChar m_typedCharacter;
|
||||||
|
|||||||
@@ -89,7 +89,8 @@ QList<AssistProposalItemInterface *> toAssistProposalItems(const CodeCompletions
|
|||||||
|
|
||||||
ClangAssistProposalItem *item = items.value(name, 0);
|
ClangAssistProposalItem *item = items.value(name, 0);
|
||||||
if (item) {
|
if (item) {
|
||||||
item->addOverload(codeCompletion);
|
if (codeCompletion.hasParameters())
|
||||||
|
item->setHasOverloadsWithParameters(true);
|
||||||
} else {
|
} else {
|
||||||
item = new ClangAssistProposalItem;
|
item = new ClangAssistProposalItem;
|
||||||
items.insert(name, item);
|
items.insert(name, item);
|
||||||
|
|||||||
@@ -799,7 +799,7 @@ void ClangCodeCompletionTest::testCompleteGlobals()
|
|||||||
ProjectLessCompletionTest t("globalCompletion.cpp");
|
ProjectLessCompletionTest t("globalCompletion.cpp");
|
||||||
|
|
||||||
QVERIFY(hasItem(t.proposal, "globalVariable", "int globalVariable"));
|
QVERIFY(hasItem(t.proposal, "globalVariable", "int globalVariable"));
|
||||||
QVERIFY(hasItem(t.proposal, "globalFunction", "void globalFunction ()"));
|
QVERIFY(hasItem(t.proposal, "globalFunction", "void globalFunction()"));
|
||||||
QVERIFY(hasItem(t.proposal, "GlobalClass", "GlobalClass"));
|
QVERIFY(hasItem(t.proposal, "GlobalClass", "GlobalClass"));
|
||||||
QVERIFY(hasItem(t.proposal, "class", "class")); // Keyword
|
QVERIFY(hasItem(t.proposal, "class", "class")); // Keyword
|
||||||
QVERIFY(hasSnippet(t.proposal, "class")); // Snippet
|
QVERIFY(hasSnippet(t.proposal, "class")); // Snippet
|
||||||
|
|||||||
@@ -80,7 +80,11 @@ ClangStaticAnalyzerToolRunner::ClangStaticAnalyzerToolRunner(RunControl *runCont
|
|||||||
RunConfiguration *runConfiguration = runControl->runConfiguration();
|
RunConfiguration *runConfiguration = runControl->runConfiguration();
|
||||||
auto tool = ClangStaticAnalyzerTool::instance();
|
auto tool = ClangStaticAnalyzerTool::instance();
|
||||||
tool->stopAction()->disconnect();
|
tool->stopAction()->disconnect();
|
||||||
connect(tool->stopAction(), &QAction::triggered, runControl, &RunControl::initiateStop);
|
connect(tool->stopAction(), &QAction::triggered, runControl, [&] {
|
||||||
|
initiateStop();
|
||||||
|
appendMessage(tr("Clang Static Analyzer stopped by user."),
|
||||||
|
Utils::NormalMessageFormat);
|
||||||
|
});
|
||||||
tool->handleWorkerStart(this);
|
tool->handleWorkerStart(this);
|
||||||
|
|
||||||
ProjectInfo projectInfoBeforeBuild = tool->projectInfoBeforeBuild();
|
ProjectInfo projectInfoBeforeBuild = tool->projectInfoBeforeBuild();
|
||||||
@@ -605,8 +609,6 @@ void ClangStaticAnalyzerToolRunner::stop()
|
|||||||
}
|
}
|
||||||
m_runners.clear();
|
m_runners.clear();
|
||||||
m_unitsToProcess.clear();
|
m_unitsToProcess.clear();
|
||||||
appendMessage(tr("Clang Static Analyzer stopped by user."),
|
|
||||||
Utils::NormalMessageFormat);
|
|
||||||
m_progress.reportFinished();
|
m_progress.reportFinished();
|
||||||
ClangStaticAnalyzerTool::instance()->onEngineFinished(m_success);
|
ClangStaticAnalyzerTool::instance()->onEngineFinished(m_success);
|
||||||
reportStopped();
|
reportStopped();
|
||||||
|
|||||||
@@ -37,18 +37,16 @@ using namespace CMakeProjectManager;
|
|||||||
using namespace CMakeProjectManager::Internal;
|
using namespace CMakeProjectManager::Internal;
|
||||||
|
|
||||||
CMakeInputsNode::CMakeInputsNode(const Utils::FileName &cmakeLists) :
|
CMakeInputsNode::CMakeInputsNode(const Utils::FileName &cmakeLists) :
|
||||||
ProjectExplorer::ProjectNode(CMakeInputsNode::inputsPathFromCMakeListsPath(cmakeLists))
|
ProjectExplorer::ProjectNode(cmakeLists, generateId(cmakeLists))
|
||||||
{
|
{
|
||||||
setPriority(Node::DefaultPriority - 10); // Bottom most!
|
setPriority(Node::DefaultPriority - 10); // Bottom most!
|
||||||
setDisplayName(QCoreApplication::translate("CMakeFilesProjectNode", "CMake Modules"));
|
setDisplayName(QCoreApplication::translate("CMakeFilesProjectNode", "CMake Modules"));
|
||||||
setIcon(QIcon(":/projectexplorer/images/session.png")); // TODO: Use a better icon!
|
setIcon(QIcon(":/projectexplorer/images/session.png")); // TODO: Use a better icon!
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::FileName CMakeInputsNode::inputsPathFromCMakeListsPath(const Utils::FileName &cmakeLists)
|
QByteArray CMakeInputsNode::generateId(const Utils::FileName &inputFile)
|
||||||
{
|
{
|
||||||
Utils::FileName result = cmakeLists;
|
return inputFile.toString().toUtf8() + "/cmakeInputs";
|
||||||
result.appendPath("cmakeInputs"); // cmakeLists is a file, so this can not exist on disk
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CMakeInputsNode::showInSimpleTree() const
|
bool CMakeInputsNode::showInSimpleTree() const
|
||||||
@@ -91,13 +89,18 @@ QString CMakeProjectNode::tooltip() const
|
|||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
CMakeTargetNode::CMakeTargetNode(const Utils::FileName &directory) :
|
CMakeTargetNode::CMakeTargetNode(const Utils::FileName &directory, const QString &target) :
|
||||||
ProjectExplorer::ProjectNode(directory)
|
ProjectExplorer::ProjectNode(directory, generateId(directory, target))
|
||||||
{
|
{
|
||||||
setPriority(Node::DefaultProjectPriority + 900);
|
setPriority(Node::DefaultProjectPriority + 900);
|
||||||
setIcon(QIcon(":/projectexplorer/images/build.png")); // TODO: Use proper icon!
|
setIcon(QIcon(":/projectexplorer/images/build.png")); // TODO: Use proper icon!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray CMakeTargetNode::generateId(const Utils::FileName &directory, const QString &target)
|
||||||
|
{
|
||||||
|
return directory.toString().toUtf8() + "///::///" + target.toUtf8();
|
||||||
|
}
|
||||||
|
|
||||||
bool CMakeTargetNode::showInSimpleTree() const
|
bool CMakeTargetNode::showInSimpleTree() const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class CMakeInputsNode : public ProjectExplorer::ProjectNode
|
|||||||
public:
|
public:
|
||||||
CMakeInputsNode(const Utils::FileName &cmakeLists);
|
CMakeInputsNode(const Utils::FileName &cmakeLists);
|
||||||
|
|
||||||
static Utils::FileName inputsPathFromCMakeListsPath(const Utils::FileName &cmakeLists);
|
static QByteArray generateId(const Utils::FileName &inputFile);
|
||||||
|
|
||||||
bool showInSimpleTree() const final;
|
bool showInSimpleTree() const final;
|
||||||
};
|
};
|
||||||
@@ -60,7 +60,9 @@ public:
|
|||||||
class CMakeTargetNode : public ProjectExplorer::ProjectNode
|
class CMakeTargetNode : public ProjectExplorer::ProjectNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CMakeTargetNode(const Utils::FileName &directory);
|
CMakeTargetNode(const Utils::FileName &directory, const QString &target);
|
||||||
|
|
||||||
|
static QByteArray generateId(const Utils::FileName &directory, const QString &target);
|
||||||
|
|
||||||
void setTargetInformation(const QList<Utils::FileName> &artifacts, const QString &type);
|
void setTargetInformation(const QList<Utils::FileName> &artifacts, const QString &type);
|
||||||
|
|
||||||
|
|||||||
@@ -250,9 +250,12 @@ static void addCMakeVFolder(FolderNode *base, const Utils::FileName &basePath, i
|
|||||||
{
|
{
|
||||||
if (files.isEmpty())
|
if (files.isEmpty())
|
||||||
return;
|
return;
|
||||||
auto folder = new VirtualFolderNode(basePath, priority);
|
FolderNode *folder = base;
|
||||||
folder->setDisplayName(displayName);
|
if (!displayName.isEmpty()) {
|
||||||
base->addNode(folder);
|
folder = new VirtualFolderNode(basePath, priority);
|
||||||
|
folder->setDisplayName(displayName);
|
||||||
|
base->addNode(folder);
|
||||||
|
}
|
||||||
folder->addNestedNodes(files);
|
folder->addNestedNodes(files);
|
||||||
for (FolderNode *fn : folder->folderNodes())
|
for (FolderNode *fn : folder->folderNodes())
|
||||||
fn->compress();
|
fn->compress();
|
||||||
@@ -268,9 +271,7 @@ static void addCMakeInputs(FolderNode *root,
|
|||||||
ProjectNode *cmakeVFolder = new CMakeInputsNode(root->filePath());
|
ProjectNode *cmakeVFolder = new CMakeInputsNode(root->filePath());
|
||||||
root->addNode(cmakeVFolder);
|
root->addNode(cmakeVFolder);
|
||||||
|
|
||||||
addCMakeVFolder(cmakeVFolder, sourceDir, 1000,
|
addCMakeVFolder(cmakeVFolder, sourceDir, 1000, QString(), sourceInputs);
|
||||||
QCoreApplication::translate("CMakeProjectManager::Internal::ServerModeReader", "<Source Directory>"),
|
|
||||||
sourceInputs);
|
|
||||||
addCMakeVFolder(cmakeVFolder, buildDir, 100,
|
addCMakeVFolder(cmakeVFolder, buildDir, 100,
|
||||||
QCoreApplication::translate("CMakeProjectManager::Internal::ServerModeReader", "<Build Directory>"),
|
QCoreApplication::translate("CMakeProjectManager::Internal::ServerModeReader", "<Build Directory>"),
|
||||||
buildInputs);
|
buildInputs);
|
||||||
@@ -336,7 +337,7 @@ void ServerModeReader::updateCodeModel(CppTools::RawProjectParts &rpps)
|
|||||||
|
|
||||||
CppTools::RawProjectPart rpp;
|
CppTools::RawProjectPart rpp;
|
||||||
rpp.setProjectFileLocation(fg->target->sourceDirectory.toString() + "/CMakeLists.txt");
|
rpp.setProjectFileLocation(fg->target->sourceDirectory.toString() + "/CMakeLists.txt");
|
||||||
rpp.setBuildSystemTarget(fg->target->name + '|' + rpp.projectFile);
|
rpp.setBuildSystemTarget(fg->target->name);
|
||||||
rpp.setDisplayName(fg->target->name + QString::number(counter));
|
rpp.setDisplayName(fg->target->name + QString::number(counter));
|
||||||
rpp.setDefines(defineArg.toUtf8());
|
rpp.setDefines(defineArg.toUtf8());
|
||||||
rpp.setIncludePaths(includes);
|
rpp.setIncludePaths(includes);
|
||||||
@@ -757,12 +758,13 @@ static CMakeTargetNode *createTargetNode(const QHash<Utils::FileName, ProjectNod
|
|||||||
ProjectNode *cmln = cmakeListsNodes.value(dir);
|
ProjectNode *cmln = cmakeListsNodes.value(dir);
|
||||||
QTC_ASSERT(cmln, return nullptr);
|
QTC_ASSERT(cmln, return nullptr);
|
||||||
|
|
||||||
Utils::FileName targetName = dir;
|
QByteArray targetId = CMakeTargetNode::generateId(dir, displayName);
|
||||||
targetName.appendPath(".target::" + displayName);
|
|
||||||
|
|
||||||
CMakeTargetNode *tn = static_cast<CMakeTargetNode *>(cmln->projectNode(targetName));
|
CMakeTargetNode *tn = static_cast<CMakeTargetNode *>(cmln->findNode([&targetId](const Node *n) {
|
||||||
|
return n->id() == targetId;
|
||||||
|
}));
|
||||||
if (!tn) {
|
if (!tn) {
|
||||||
tn = new CMakeTargetNode(targetName);
|
tn = new CMakeTargetNode(dir, displayName);
|
||||||
cmln->addNode(tn);
|
cmln->addNode(tn);
|
||||||
}
|
}
|
||||||
tn->setDisplayName(displayName);
|
tn->setDisplayName(displayName);
|
||||||
@@ -849,7 +851,7 @@ void ServerModeReader::addFileGroups(ProjectNode *targetRoot,
|
|||||||
otherFileNodes.append(fn);
|
otherFileNodes.append(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
addCMakeVFolder(targetRoot, sourceDirectory, 1000, tr("<Source Directory>"), sourceFileNodes);
|
addCMakeVFolder(targetRoot, sourceDirectory, 1000, QString(), sourceFileNodes);
|
||||||
addCMakeVFolder(targetRoot, buildDirectory, 100, tr("<Build Directory>"), buildFileNodes);
|
addCMakeVFolder(targetRoot, buildDirectory, 100, tr("<Build Directory>"), buildFileNodes);
|
||||||
addCMakeVFolder(targetRoot, Utils::FileName(), 10, tr("<Other Locations>"), otherFileNodes);
|
addCMakeVFolder(targetRoot, Utils::FileName(), 10, tr("<Other Locations>"), otherFileNodes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ void TeaLeafReader::updateCodeModel(CppTools::RawProjectParts &rpps)
|
|||||||
includePaths += m_parameters.buildDirectory.toString();
|
includePaths += m_parameters.buildDirectory.toString();
|
||||||
CppTools::RawProjectPart rpp;
|
CppTools::RawProjectPart rpp;
|
||||||
rpp.setProjectFileLocation(cbt.sourceDirectory.toString() + "/CMakeLists.txt");
|
rpp.setProjectFileLocation(cbt.sourceDirectory.toString() + "/CMakeLists.txt");
|
||||||
rpp.setBuildSystemTarget(cbt.title + '|' + rpp.projectFile);
|
rpp.setBuildSystemTarget(cbt.title);
|
||||||
rpp.setIncludePaths(includePaths);
|
rpp.setIncludePaths(includePaths);
|
||||||
|
|
||||||
CppTools::RawProjectPartFlags cProjectFlags;
|
CppTools::RawProjectPartFlags cProjectFlags;
|
||||||
|
|||||||
@@ -210,6 +210,8 @@ SearchResultWidget::SearchResultWidget(QWidget *parent) :
|
|||||||
this, &SearchResultWidget::handleReplaceButton);
|
this, &SearchResultWidget::handleReplaceButton);
|
||||||
connect(m_replaceButton, &QAbstractButton::clicked,
|
connect(m_replaceButton, &QAbstractButton::clicked,
|
||||||
this, &SearchResultWidget::handleReplaceButton);
|
this, &SearchResultWidget::handleReplaceButton);
|
||||||
|
connect(m_replaceTextEdit, &QLineEdit::textChanged,
|
||||||
|
this, &SearchResultWidget::handleReplaceEditTextChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchResultWidget::~SearchResultWidget()
|
SearchResultWidget::~SearchResultWidget()
|
||||||
@@ -304,6 +306,7 @@ void SearchResultWidget::setSupportsReplace(bool replaceSupported, const QString
|
|||||||
|
|
||||||
void SearchResultWidget::setTextToReplace(const QString &textToReplace)
|
void SearchResultWidget::setTextToReplace(const QString &textToReplace)
|
||||||
{
|
{
|
||||||
|
m_replaceText = textToReplace;
|
||||||
m_replaceTextEdit->setText(textToReplace);
|
m_replaceTextEdit->setText(textToReplace);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,7 +409,6 @@ void SearchResultWidget::goToPrevious()
|
|||||||
void SearchResultWidget::restart()
|
void SearchResultWidget::restart()
|
||||||
{
|
{
|
||||||
m_replaceTextEdit->setEnabled(false);
|
m_replaceTextEdit->setEnabled(false);
|
||||||
m_replaceButton->setEnabled(false);
|
|
||||||
m_searchResultTreeView->clear();
|
m_searchResultTreeView->clear();
|
||||||
m_count = 0;
|
m_count = 0;
|
||||||
Id sizeWarningId(SIZE_WARNING_ID);
|
Id sizeWarningId(SIZE_WARNING_ID);
|
||||||
@@ -416,6 +418,7 @@ void SearchResultWidget::restart()
|
|||||||
m_searchAgainButton->setVisible(false);
|
m_searchAgainButton->setVisible(false);
|
||||||
m_messageWidget->setVisible(false);
|
m_messageWidget->setVisible(false);
|
||||||
updateMatchesFoundLabel();
|
updateMatchesFoundLabel();
|
||||||
|
handleReplaceEditTextChanged();
|
||||||
emit restarted();
|
emit restarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,7 +439,6 @@ void SearchResultWidget::finishSearch(bool canceled)
|
|||||||
m_infoBar.removeInfo(sizeWarningId);
|
m_infoBar.removeInfo(sizeWarningId);
|
||||||
m_infoBar.enableInfo(sizeWarningId);
|
m_infoBar.enableInfo(sizeWarningId);
|
||||||
m_replaceTextEdit->setEnabled(m_count > 0);
|
m_replaceTextEdit->setEnabled(m_count > 0);
|
||||||
m_replaceButton->setEnabled(m_count > 0);
|
|
||||||
m_preserveCaseCheck->setEnabled(m_count > 0);
|
m_preserveCaseCheck->setEnabled(m_count > 0);
|
||||||
m_cancelButton->setVisible(false);
|
m_cancelButton->setVisible(false);
|
||||||
m_messageWidget->setVisible(canceled);
|
m_messageWidget->setVisible(canceled);
|
||||||
@@ -461,6 +463,15 @@ void SearchResultWidget::cancelAfterSizeWarning()
|
|||||||
emit paused(false);
|
emit paused(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SearchResultWidget::handleReplaceEditTextChanged()
|
||||||
|
{
|
||||||
|
const bool enabled = m_replaceTextEdit->text() != m_replaceText;
|
||||||
|
m_replaceButton->setEnabled(enabled);
|
||||||
|
m_replaceButton->setToolTip(enabled
|
||||||
|
? QString()
|
||||||
|
: tr("Cannot replace because replacement text is unchanged."));
|
||||||
|
}
|
||||||
|
|
||||||
void SearchResultWidget::handleJumpToSearchResult(const SearchResultItem &item)
|
void SearchResultWidget::handleJumpToSearchResult(const SearchResultItem &item)
|
||||||
{
|
{
|
||||||
emit activated(item);
|
emit activated(item);
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ private:
|
|||||||
void setShowReplaceUI(bool visible);
|
void setShowReplaceUI(bool visible);
|
||||||
void continueAfterSizeWarning();
|
void continueAfterSizeWarning();
|
||||||
void cancelAfterSizeWarning();
|
void cancelAfterSizeWarning();
|
||||||
|
void handleReplaceEditTextChanged();
|
||||||
|
|
||||||
QList<SearchResultItem> checkedItems() const;
|
QList<SearchResultItem> checkedItems() const;
|
||||||
void updateMatchesFoundLabel();
|
void updateMatchesFoundLabel();
|
||||||
@@ -141,6 +142,7 @@ private:
|
|||||||
bool m_isShowingReplaceUI = false;
|
bool m_isShowingReplaceUI = false;
|
||||||
bool m_searchAgainSupported = false;
|
bool m_searchAgainSupported = false;
|
||||||
bool m_replaceSupported = false;
|
bool m_replaceSupported = false;
|
||||||
|
QString m_replaceText;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // Internal
|
} // Internal
|
||||||
|
|||||||
@@ -249,8 +249,7 @@ void CppEditorPlugin::test_autoComplete_data()
|
|||||||
QString expectedText;
|
QString expectedText;
|
||||||
int skippedChar = 0;
|
int skippedChar = 0;
|
||||||
|
|
||||||
// We always expect to get a closing char in an empty file
|
if (fc == EmptyFile && isOpeningChar(c) && c != QLatin1Char('{'))
|
||||||
if (fc == EmptyFile && isOpeningChar(c))
|
|
||||||
expectedText = closingChar(c);
|
expectedText = closingChar(c);
|
||||||
|
|
||||||
if (fc == InBetween) {
|
if (fc == InBetween) {
|
||||||
|
|||||||
@@ -457,8 +457,6 @@ void CppEditorWidget::renameSymbolUnderCursor()
|
|||||||
|
|
||||||
void CppEditorWidget::renameSymbolUnderCursorBuiltin()
|
void CppEditorWidget::renameSymbolUnderCursorBuiltin()
|
||||||
{
|
{
|
||||||
d->m_useSelectionsUpdater.abortSchedule();
|
|
||||||
|
|
||||||
updateSemanticInfo(d->m_cppEditorDocument->recalculateSemanticInfo(),
|
updateSemanticInfo(d->m_cppEditorDocument->recalculateSemanticInfo(),
|
||||||
/*updateUseSelectionSynchronously=*/ true);
|
/*updateUseSelectionSynchronously=*/ true);
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,8 @@ void CppUseSelectionsUpdater::update(CallType callType)
|
|||||||
|
|
||||||
m_runnerWatcher->setFuture(cppEditorDocument->cursorInfo(params));
|
m_runnerWatcher->setFuture(cppEditorDocument->cursorInfo(params));
|
||||||
} else { // synchronous case
|
} else { // synchronous case
|
||||||
|
abortSchedule();
|
||||||
|
|
||||||
const int startRevision = cppEditorDocument->document()->revision();
|
const int startRevision = cppEditorDocument->document()->revision();
|
||||||
QFuture<CursorInfo> future = cppEditorDocument->cursorInfo(params);
|
QFuture<CursorInfo> future = cppEditorDocument->cursorInfo(params);
|
||||||
|
|
||||||
|
|||||||
@@ -520,14 +520,14 @@ bool CdbEngine::launchCDB(const DebuggerRunParameters &sp, QString *errorMessage
|
|||||||
const QFileInfo extensionFi(CdbEngine::extensionLibraryName(cdbIs64Bit));
|
const QFileInfo extensionFi(CdbEngine::extensionLibraryName(cdbIs64Bit));
|
||||||
if (!extensionFi.isFile()) {
|
if (!extensionFi.isFile()) {
|
||||||
*errorMessage = tr("Internal error: The extension %1 cannot be found.\n"
|
*errorMessage = tr("Internal error: The extension %1 cannot be found.\n"
|
||||||
"If you have updated Qt Creator via Maintenance Tool you may "
|
"If you have updated Qt Creator via Maintenance Tool, you may "
|
||||||
"need to rerun the Tool and select \"Add or remove components\""
|
"need to rerun the Tool and select \"Add or remove components\" "
|
||||||
"and then select the\n"
|
"and then select the\n"
|
||||||
"Qt > Tools > Qt Creator > Qt Creator CDB Debugger Support component.\n"
|
"Qt > Tools > Qt Creator > Qt Creator CDB Debugger Support component.\n"
|
||||||
"If you build Qt Creator from sources and want to use a cdb executable"
|
"If you build Qt Creator from sources and want to use a CDB executable "
|
||||||
"with another bitness than your Qt Creator build,\n"
|
"with another bitness than your Qt Creator build,\n"
|
||||||
"you will need to build a separate cdbextension with the "
|
"you will need to build a separate CDB extension with the "
|
||||||
"same bitness as the cdb you want to use.").
|
"same bitness as the CDB you want to use.").
|
||||||
arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
|
arg(QDir::toNativeSeparators(extensionFi.absoluteFilePath()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1030,39 +1030,37 @@ RunControl *DebuggerEnginePrivate::runControl() const
|
|||||||
|
|
||||||
void DebuggerEngine::notifyEngineIll()
|
void DebuggerEngine::notifyEngineIll()
|
||||||
{
|
{
|
||||||
runControl()->initiateStop();
|
|
||||||
return;
|
|
||||||
//#ifdef WITH_BENCHMARK
|
//#ifdef WITH_BENCHMARK
|
||||||
// CALLGRIND_STOP_INSTRUMENTATION;
|
// CALLGRIND_STOP_INSTRUMENTATION;
|
||||||
// CALLGRIND_DUMP_STATS;
|
// CALLGRIND_DUMP_STATS;
|
||||||
//#endif
|
//#endif
|
||||||
// showMessage("NOTE: ENGINE ILL ******");
|
showMessage("NOTE: ENGINE ILL ******");
|
||||||
// runTool()->startDying();
|
runTool()->startDying();
|
||||||
// d->m_lastGoodState = d->m_state;
|
d->m_lastGoodState = d->m_state;
|
||||||
// switch (state()) {
|
switch (state()) {
|
||||||
// case InferiorRunRequested:
|
case InferiorRunRequested:
|
||||||
// case InferiorRunOk:
|
case InferiorRunOk:
|
||||||
// // The engine does not look overly ill right now, so attempt to
|
// The engine does not look overly ill right now, so attempt to
|
||||||
// // properly interrupt at least once. If that fails, we are on the
|
// properly interrupt at least once. If that fails, we are on the
|
||||||
// // shutdown path due to d->m_targetState anyways.
|
// shutdown path due to d->m_targetState anyways.
|
||||||
// setState(InferiorStopRequested, true);
|
setState(InferiorStopRequested, true);
|
||||||
// showMessage("ATTEMPT TO INTERRUPT INFERIOR");
|
showMessage("ATTEMPT TO INTERRUPT INFERIOR");
|
||||||
// interruptInferior();
|
interruptInferior();
|
||||||
// break;
|
break;
|
||||||
// case InferiorStopRequested:
|
case InferiorStopRequested:
|
||||||
// notifyInferiorStopFailed();
|
notifyInferiorStopFailed();
|
||||||
// break;
|
break;
|
||||||
// case InferiorStopOk:
|
case InferiorStopOk:
|
||||||
// showMessage("FORWARDING STATE TO InferiorShutdownFailed");
|
showMessage("FORWARDING STATE TO InferiorShutdownFailed");
|
||||||
// setState(InferiorShutdownFailed, true);
|
setState(InferiorShutdownFailed, true);
|
||||||
// if (isMasterEngine())
|
if (isMasterEngine())
|
||||||
// d->queueShutdownEngine();
|
d->queueShutdownEngine();
|
||||||
// break;
|
break;
|
||||||
// default:
|
default:
|
||||||
// if (isMasterEngine())
|
if (isMasterEngine())
|
||||||
// d->queueShutdownEngine();
|
d->queueShutdownEngine();
|
||||||
// break;
|
break;
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerEngine::notifyEngineSpontaneousShutdown()
|
void DebuggerEngine::notifyEngineSpontaneousShutdown()
|
||||||
|
|||||||
@@ -1351,7 +1351,7 @@ bool DebuggerPluginPrivate::initialize(const QStringList &arguments,
|
|||||||
connect(m_registerView, &BaseTreeView::aboutToShow,
|
connect(m_registerView, &BaseTreeView::aboutToShow,
|
||||||
this, &DebuggerPluginPrivate::reloadRegisters,
|
this, &DebuggerPluginPrivate::reloadRegisters,
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
m_registerWindow = addSearch(m_registerView, tr("&Registers"), DOCKWIDGET_REGISTER);
|
m_registerWindow = addSearch(m_registerView, tr("Reg&isters"), DOCKWIDGET_REGISTER);
|
||||||
|
|
||||||
m_stackView = new StackTreeView;
|
m_stackView = new StackTreeView;
|
||||||
m_stackView->setSettings(settings, "Debugger.StackView");
|
m_stackView->setSettings(settings, "Debugger.StackView");
|
||||||
@@ -3561,8 +3561,10 @@ void setPerspectiveEnabled(const QByteArray &perspectiveId, bool enabled)
|
|||||||
|
|
||||||
void selectPerspective(const QByteArray &perspectiveId)
|
void selectPerspective(const QByteArray &perspectiveId)
|
||||||
{
|
{
|
||||||
if (dd->m_mainWindow->currentPerspective() == perspectiveId)
|
if (ModeManager::currentMode() == MODE_DEBUG
|
||||||
|
&& dd->m_mainWindow->currentPerspective() == perspectiveId) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Work-around aslong as the GammaRay integration does not use the same setup,
|
// FIXME: Work-around aslong as the GammaRay integration does not use the same setup,
|
||||||
if (perspectiveId.isEmpty())
|
if (perspectiveId.isEmpty())
|
||||||
|
|||||||
@@ -256,8 +256,8 @@ DebuggerEngine *createEngine(DebuggerEngineType cppEngineType,
|
|||||||
if (cppEngine) {
|
if (cppEngine) {
|
||||||
engine = createQmlCppEngine(cppEngine, useTerminal);
|
engine = createQmlCppEngine(cppEngine, useTerminal);
|
||||||
} else {
|
} else {
|
||||||
errors->append(DebuggerPlugin::tr("The slave debugging engine required for combined "
|
errors->append(DebuggerPlugin::tr("The debugging engine required for combined "
|
||||||
"QML/C++-Debugging could not be created: %1"));
|
"QML/C++ debugging could not be created: %1"));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -266,7 +266,7 @@ DebuggerEngine *createEngine(DebuggerEngineType cppEngineType,
|
|||||||
.arg(engineTypeName(et)));
|
.arg(engineTypeName(et)));
|
||||||
}
|
}
|
||||||
if (!engine)
|
if (!engine)
|
||||||
errors->append(DebuggerPlugin::tr("Unable to create a debugger engine of the type \"%1\"").
|
errors->append(DebuggerPlugin::tr("Unable to create a debugging engine of the type \"%1\"").
|
||||||
arg(engineTypeName(et)));
|
arg(engineTypeName(et)));
|
||||||
return engine;
|
return engine;
|
||||||
}
|
}
|
||||||
@@ -282,7 +282,7 @@ static bool fixupParameters(DebuggerRunParameters &rp, RunControl *runControl, Q
|
|||||||
|
|
||||||
// Extract as much as possible from available RunConfiguration.
|
// Extract as much as possible from available RunConfiguration.
|
||||||
const Runnable runnable = runConfig->runnable();
|
const Runnable runnable = runConfig->runnable();
|
||||||
if (runnable.is<StandardRunnable>()) {
|
if (rp.needFixup && runnable.is<StandardRunnable>()) {
|
||||||
// FIXME: Needed for core dump which stores the executable in inferior, but not in runConfig
|
// FIXME: Needed for core dump which stores the executable in inferior, but not in runConfig
|
||||||
// executable.
|
// executable.
|
||||||
const QString prevExecutable = rp.inferior.executable;
|
const QString prevExecutable = rp.inferior.executable;
|
||||||
@@ -295,7 +295,7 @@ static bool fixupParameters(DebuggerRunParameters &rp, RunControl *runControl, Q
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We might get an executable from a local PID.
|
// We might get an executable from a local PID.
|
||||||
if (rp.inferior.executable.isEmpty() && rp.attachPID.isValid()) {
|
if (rp.needFixup && rp.inferior.executable.isEmpty() && rp.attachPID.isValid()) {
|
||||||
foreach (const DeviceProcessItem &p, DeviceProcessList::localProcesses()) {
|
foreach (const DeviceProcessItem &p, DeviceProcessList::localProcesses()) {
|
||||||
if (p.pid == rp.attachPID.pid()) {
|
if (p.pid == rp.attachPID.pid()) {
|
||||||
rp.inferior.executable = p.exe;
|
rp.inferior.executable = p.exe;
|
||||||
@@ -313,14 +313,16 @@ static bool fixupParameters(DebuggerRunParameters &rp, RunControl *runControl, Q
|
|||||||
if (!envBinary.isEmpty())
|
if (!envBinary.isEmpty())
|
||||||
rp.debugger.executable = QString::fromLocal8Bit(envBinary);
|
rp.debugger.executable = QString::fromLocal8Bit(envBinary);
|
||||||
|
|
||||||
if (auto envAspect = runConfig->extraAspect<EnvironmentAspect>()) {
|
if (rp.needFixup) {
|
||||||
rp.inferior.environment = envAspect->environment(); // Correct.
|
if (auto envAspect = runConfig->extraAspect<EnvironmentAspect>()) {
|
||||||
rp.stubEnvironment = rp.inferior.environment; // FIXME: Wrong, but contains DYLD_IMAGE_SUFFIX
|
rp.inferior.environment = envAspect->environment(); // Correct.
|
||||||
|
rp.stubEnvironment = rp.inferior.environment; // FIXME: Wrong, but contains DYLD_IMAGE_SUFFIX
|
||||||
|
|
||||||
// Copy over DYLD_IMAGE_SUFFIX etc
|
// Copy over DYLD_IMAGE_SUFFIX etc
|
||||||
for (auto var : QStringList({"DYLD_IMAGE_SUFFIX", "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH"}))
|
for (auto var : QStringList({"DYLD_IMAGE_SUFFIX", "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH"}))
|
||||||
if (rp.inferior.environment.hasKey(var))
|
if (rp.inferior.environment.hasKey(var))
|
||||||
rp.debugger.environment.set(var, rp.inferior.environment.value(var));
|
rp.debugger.environment.set(var, rp.inferior.environment.value(var));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Project *project = runConfig->target()->project()) {
|
if (Project *project = runConfig->target()->project()) {
|
||||||
rp.projectSourceDirectory = project->projectDirectory().toString();
|
rp.projectSourceDirectory = project->projectDirectory().toString();
|
||||||
@@ -623,7 +625,7 @@ RunConfiguration *dummyRunConfigForKit(ProjectExplorer::Kit *kit)
|
|||||||
QTC_ASSERT(kit, return nullptr); // Caller needs to look for a suitable kit.
|
QTC_ASSERT(kit, return nullptr); // Caller needs to look for a suitable kit.
|
||||||
Project *project = SessionManager::startupProject();
|
Project *project = SessionManager::startupProject();
|
||||||
Target *target = project ? project->target(kit) : nullptr;
|
Target *target = project ? project->target(kit) : nullptr;
|
||||||
if (!target) {
|
if (!target || !target->activeRunConfiguration()) {
|
||||||
project = new DummyProject;
|
project = new DummyProject;
|
||||||
target = project->createTarget(kit);
|
target = project->createTarget(kit);
|
||||||
}
|
}
|
||||||
@@ -685,7 +687,7 @@ void GdbServerPortsGatherer::handlePortListReady()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reportStarted();
|
reportDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
// GdbServerRunner
|
// GdbServerRunner
|
||||||
@@ -731,7 +733,7 @@ void GdbServerRunner::start()
|
|||||||
|
|
||||||
setRunnable(r);
|
setRunnable(r);
|
||||||
|
|
||||||
appendMessage(tr("Starting GDBserver..."), NormalMessageFormat);
|
appendMessage(tr("Starting gdbserver..."), NormalMessageFormat);
|
||||||
|
|
||||||
SimpleTargetRunner::start();
|
SimpleTargetRunner::start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ public:
|
|||||||
Utils::ProcessHandle attachPID;
|
Utils::ProcessHandle attachPID;
|
||||||
QStringList solibSearchPath;
|
QStringList solibSearchPath;
|
||||||
bool useTerminal = false;
|
bool useTerminal = false;
|
||||||
|
bool needFixup = true; // FIXME: Make false the default...
|
||||||
|
|
||||||
// Used by Qml debugging.
|
// Used by Qml debugging.
|
||||||
TcpServerConnection qmlServer;
|
TcpServerConnection qmlServer;
|
||||||
|
|||||||
@@ -430,7 +430,7 @@ LogWindow::LogWindow(QWidget *parent)
|
|||||||
setMinimumHeight(60);
|
setMinimumHeight(60);
|
||||||
|
|
||||||
showOutput(LogWarning,
|
showOutput(LogWarning,
|
||||||
tr("NOTE: This log contains possibly confidential information about your machine, "
|
tr("Note: This log contains possibly confidential information about your machine, "
|
||||||
"environment variables, in-memory data of the processes you are debugging, and more. "
|
"environment variables, in-memory data of the processes you are debugging, and more. "
|
||||||
"It is never transferred over the internet by Qt Creator, and only stored "
|
"It is never transferred over the internet by Qt Creator, and only stored "
|
||||||
"to disk if you manually use the respective option from the context menu, or through "
|
"to disk if you manually use the respective option from the context menu, or through "
|
||||||
|
|||||||
@@ -856,7 +856,7 @@ void DiffEditor::Internal::DiffEditorPlugin::testMakePatch()
|
|||||||
QFETCH(bool, lastChunk);
|
QFETCH(bool, lastChunk);
|
||||||
QFETCH(QString, patchText);
|
QFETCH(QString, patchText);
|
||||||
|
|
||||||
QString result = DiffUtils::makePatch(sourceChunk, leftFileName, rightFileName, lastChunk);
|
const QString result = DiffUtils::makePatch(sourceChunk, leftFileName, rightFileName, lastChunk);
|
||||||
|
|
||||||
QCOMPARE(result, patchText);
|
QCOMPARE(result, patchText);
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "differ.h"
|
#include "differ.h"
|
||||||
|
|
||||||
#include "texteditor/fontsettings.h"
|
#include "texteditor/fontsettings.h"
|
||||||
|
#include "utils/asconst.h"
|
||||||
|
|
||||||
#include <QFutureInterfaceBase>
|
#include <QFutureInterfaceBase>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
@@ -485,6 +486,34 @@ QString DiffUtils::makePatch(const ChunkData &chunkData,
|
|||||||
return diffText;
|
return diffText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString leftFileName(const FileData &fileData, unsigned formatFlags)
|
||||||
|
{
|
||||||
|
QString diffText;
|
||||||
|
QTextStream str(&diffText);
|
||||||
|
if (fileData.fileOperation == FileData::NewFile) {
|
||||||
|
str << "/dev/null";
|
||||||
|
} else {
|
||||||
|
if (formatFlags & DiffUtils::AddLevel)
|
||||||
|
str << "a/";
|
||||||
|
str << fileData.leftFileInfo.fileName;
|
||||||
|
}
|
||||||
|
return diffText;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString rightFileName(const FileData &fileData, unsigned formatFlags)
|
||||||
|
{
|
||||||
|
QString diffText;
|
||||||
|
QTextStream str(&diffText);
|
||||||
|
if (fileData.fileOperation == FileData::DeleteFile) {
|
||||||
|
str << "/dev/null";
|
||||||
|
} else {
|
||||||
|
if (formatFlags & DiffUtils::AddLevel)
|
||||||
|
str << "b/";
|
||||||
|
str << fileData.rightFileInfo.fileName;
|
||||||
|
}
|
||||||
|
return diffText;
|
||||||
|
}
|
||||||
|
|
||||||
QString DiffUtils::makePatch(const QList<FileData> &fileDataList, unsigned formatFlags)
|
QString DiffUtils::makePatch(const QList<FileData> &fileDataList, unsigned formatFlags)
|
||||||
{
|
{
|
||||||
QString diffText;
|
QString diffText;
|
||||||
@@ -496,26 +525,36 @@ QString DiffUtils::makePatch(const QList<FileData> &fileDataList, unsigned forma
|
|||||||
str << "diff --git a/" << fileData.leftFileInfo.fileName
|
str << "diff --git a/" << fileData.leftFileInfo.fileName
|
||||||
<< " b/" << fileData.rightFileInfo.fileName << '\n';
|
<< " b/" << fileData.rightFileInfo.fileName << '\n';
|
||||||
}
|
}
|
||||||
|
if (fileData.fileOperation == FileData::NewFile
|
||||||
|
|| fileData.fileOperation == FileData::DeleteFile) { // git only?
|
||||||
|
if (fileData.fileOperation == FileData::NewFile)
|
||||||
|
str << "new";
|
||||||
|
else
|
||||||
|
str << "deleted";
|
||||||
|
str << " file mode 100644\n";
|
||||||
|
}
|
||||||
|
str << "index " << fileData.leftFileInfo.typeInfo << ".." << fileData.rightFileInfo.typeInfo;
|
||||||
|
if (fileData.fileOperation == FileData::ChangeFile)
|
||||||
|
str << " 100644";
|
||||||
|
str << "\n";
|
||||||
|
|
||||||
if (fileData.binaryFiles) {
|
if (fileData.binaryFiles) {
|
||||||
str << "Binary files ";
|
str << "Binary files ";
|
||||||
if (formatFlags & AddLevel)
|
str << leftFileName(fileData, formatFlags);
|
||||||
str << "a/";
|
str << " and ";
|
||||||
str << fileData.leftFileInfo.fileName << " and ";
|
str << rightFileName(fileData, formatFlags);
|
||||||
if (formatFlags & AddLevel)
|
str << " differ\n";
|
||||||
str << "b/";
|
|
||||||
str << fileData.rightFileInfo.fileName << " differ\n";
|
|
||||||
} else {
|
} else {
|
||||||
str << "--- ";
|
if (!fileData.chunks.isEmpty()) {
|
||||||
if (formatFlags & AddLevel)
|
str << "--- ";
|
||||||
str << "a/";
|
str << leftFileName(fileData, formatFlags) << "\n";
|
||||||
str << fileData.leftFileInfo.fileName << "\n+++ ";
|
str << "+++ ";
|
||||||
if (formatFlags & AddLevel)
|
str << rightFileName(fileData, formatFlags) << "\n";
|
||||||
str << "b/";
|
for (int j = 0; j < fileData.chunks.count(); j++) {
|
||||||
str << fileData.rightFileInfo.fileName << '\n';
|
str << makePatch(fileData.chunks.at(j),
|
||||||
for (int j = 0; j < fileData.chunks.count(); j++) {
|
(j == fileData.chunks.count() - 1)
|
||||||
str << makePatch(fileData.chunks.at(j),
|
&& fileData.lastChunkAtTheEndOfFile);
|
||||||
(j == fileData.chunks.count() - 1)
|
}
|
||||||
&& fileData.lastChunkAtTheEndOfFile);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -695,58 +734,128 @@ static QList<RowData> readLines(QStringRef patch,
|
|||||||
outputRightDiffList).rows;
|
outputRightDiffList).rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QStringRef readLine(QStringRef text, QStringRef *remainingText, bool *hasNewLine)
|
||||||
|
{
|
||||||
|
const QChar newLine('\n');
|
||||||
|
const int indexOfFirstNewLine = text.indexOf(newLine);
|
||||||
|
if (indexOfFirstNewLine < 0) {
|
||||||
|
if (remainingText)
|
||||||
|
*remainingText = QStringRef();
|
||||||
|
if (hasNewLine)
|
||||||
|
*hasNewLine = false;
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasNewLine)
|
||||||
|
*hasNewLine = true;
|
||||||
|
|
||||||
|
if (remainingText)
|
||||||
|
*remainingText = text.mid(indexOfFirstNewLine + 1);
|
||||||
|
|
||||||
|
return text.left(indexOfFirstNewLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool detectChunkData(QStringRef chunkDiff,
|
||||||
|
ChunkData *chunkData,
|
||||||
|
QStringRef *remainingPatch)
|
||||||
|
{
|
||||||
|
bool hasNewLine;
|
||||||
|
const QStringRef chunkLine = readLine(chunkDiff, remainingPatch, &hasNewLine);
|
||||||
|
|
||||||
|
const QLatin1String leftPosMarker("@@ -");
|
||||||
|
const QLatin1String rightPosMarker(" +");
|
||||||
|
const QLatin1String optionalHintMarker(" @@");
|
||||||
|
|
||||||
|
const int leftPosIndex = chunkLine.indexOf(leftPosMarker);
|
||||||
|
if (leftPosIndex != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const int rightPosIndex = chunkLine.indexOf(rightPosMarker, leftPosIndex + leftPosMarker.size());
|
||||||
|
if (rightPosIndex < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const int optionalHintIndex = chunkLine.indexOf(optionalHintMarker, rightPosIndex + rightPosMarker.size());
|
||||||
|
if (optionalHintIndex < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const int leftPosStart = leftPosIndex + leftPosMarker.size();
|
||||||
|
const int leftPosLength = rightPosIndex - leftPosStart;
|
||||||
|
QStringRef leftPos = chunkLine.mid(leftPosStart, leftPosLength);
|
||||||
|
|
||||||
|
const int rightPosStart = rightPosIndex + rightPosMarker.size();
|
||||||
|
const int rightPosLength = optionalHintIndex - rightPosStart;
|
||||||
|
QStringRef rightPos = chunkLine.mid(rightPosStart, rightPosLength);
|
||||||
|
|
||||||
|
const int optionalHintStart = optionalHintIndex + optionalHintMarker.size();
|
||||||
|
const int optionalHintLength = chunkLine.size() - optionalHintStart;
|
||||||
|
const QStringRef optionalHint = chunkLine.mid(optionalHintStart, optionalHintLength);
|
||||||
|
|
||||||
|
const QChar comma(',');
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
const int leftCommaIndex = leftPos.indexOf(comma);
|
||||||
|
if (leftCommaIndex >= 0)
|
||||||
|
leftPos = leftPos.left(leftCommaIndex);
|
||||||
|
const int leftLineNumber = leftPos.toString().toInt(&ok);
|
||||||
|
if (!ok)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const int rightCommaIndex = rightPos.indexOf(comma);
|
||||||
|
if (rightCommaIndex >= 0)
|
||||||
|
rightPos = rightPos.left(rightCommaIndex);
|
||||||
|
const int rightLineNumber = rightPos.toString().toInt(&ok);
|
||||||
|
if (!ok)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
chunkData->leftStartingLineNumber = leftLineNumber - 1;
|
||||||
|
chunkData->rightStartingLineNumber = rightLineNumber - 1;
|
||||||
|
chunkData->contextInfo = optionalHint.toString();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static QList<ChunkData> readChunks(QStringRef patch,
|
static QList<ChunkData> readChunks(QStringRef patch,
|
||||||
bool *lastChunkAtTheEndOfFile,
|
bool *lastChunkAtTheEndOfFile,
|
||||||
bool *ok)
|
bool *ok)
|
||||||
{
|
{
|
||||||
const QRegularExpression chunkRegExp(
|
|
||||||
// beginning of the line
|
|
||||||
"(?:\\n|^)"
|
|
||||||
// @@ -leftPos[,leftCount] +rightPos[,rightCount] @@
|
|
||||||
"@@ -(\\d+)(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@"
|
|
||||||
// optional hint (e.g. function name)
|
|
||||||
"(\\ +[^\\n]*)?"
|
|
||||||
// end of line (need to be followed by text line)
|
|
||||||
"\\n");
|
|
||||||
|
|
||||||
bool readOk = false;
|
|
||||||
|
|
||||||
QList<ChunkData> chunkDataList;
|
QList<ChunkData> chunkDataList;
|
||||||
|
int position = -1;
|
||||||
|
|
||||||
QRegularExpressionMatch match = chunkRegExp.match(patch);
|
QVector<int> startingPositions; // store starting positions of @@
|
||||||
if (match.hasMatch() && match.capturedStart() == 0) {
|
if (patch.startsWith(QStringLiteral("@@ -")))
|
||||||
int endOfLastChunk = 0;
|
startingPositions.append(position + 1);
|
||||||
do {
|
|
||||||
const int pos = match.capturedStart();
|
|
||||||
const int leftStartingPos = match.capturedRef(1).toInt();
|
|
||||||
const int rightStartingPos = match.capturedRef(2).toInt();
|
|
||||||
const QString contextInfo = match.captured(3);
|
|
||||||
if (endOfLastChunk > 0) {
|
|
||||||
QStringRef lines = patch.mid(endOfLastChunk,
|
|
||||||
pos - endOfLastChunk);
|
|
||||||
chunkDataList.last().rows = readLines(lines,
|
|
||||||
false,
|
|
||||||
lastChunkAtTheEndOfFile,
|
|
||||||
&readOk);
|
|
||||||
if (!readOk)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
endOfLastChunk = match.capturedEnd();
|
|
||||||
ChunkData chunkData;
|
|
||||||
chunkData.leftStartingLineNumber = leftStartingPos - 1;
|
|
||||||
chunkData.rightStartingLineNumber = rightStartingPos - 1;
|
|
||||||
chunkData.contextInfo = contextInfo;
|
|
||||||
chunkDataList.append(chunkData);
|
|
||||||
match = chunkRegExp.match(patch, endOfLastChunk);
|
|
||||||
} while (match.hasMatch());
|
|
||||||
|
|
||||||
if (endOfLastChunk > 0) {
|
while ((position = patch.indexOf(QStringLiteral("\n@@ -"), position + 1)) >= 0)
|
||||||
QStringRef lines = patch.mid(endOfLastChunk);
|
startingPositions.append(position + 1);
|
||||||
chunkDataList.last().rows = readLines(lines,
|
|
||||||
true,
|
const QChar newLine('\n');
|
||||||
lastChunkAtTheEndOfFile,
|
bool readOk = true;
|
||||||
&readOk);
|
|
||||||
}
|
const int count = startingPositions.count();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
const int chunkStart = startingPositions.at(i);
|
||||||
|
const int chunkEnd = (i < count - 1)
|
||||||
|
// drop the newline before the next chunk start
|
||||||
|
? startingPositions.at(i + 1) - 1
|
||||||
|
// drop the possible newline by the end of patch
|
||||||
|
: (patch.at(patch.count() - 1) == newLine ? patch.count() - 1 : patch.count());
|
||||||
|
|
||||||
|
// extract just one chunk
|
||||||
|
const QStringRef chunkDiff = patch.mid(chunkStart, chunkEnd - chunkStart);
|
||||||
|
|
||||||
|
ChunkData chunkData;
|
||||||
|
QStringRef lines;
|
||||||
|
readOk = detectChunkData(chunkDiff, &chunkData, &lines);
|
||||||
|
|
||||||
|
if (!readOk)
|
||||||
|
break;
|
||||||
|
|
||||||
|
chunkData.rows = readLines(lines, i == (startingPositions.size() - 1),
|
||||||
|
lastChunkAtTheEndOfFile, &readOk);
|
||||||
|
if (!readOk)
|
||||||
|
break;
|
||||||
|
|
||||||
|
chunkDataList.append(chunkData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ok)
|
if (ok)
|
||||||
@@ -881,293 +990,315 @@ static QList<FileData> readDiffPatch(QStringRef patch,
|
|||||||
return fileDataList;
|
return fileDataList;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool fileNameEnd(const QChar &c)
|
// The git diff patch format (ChangeFile, NewFile, DeleteFile)
|
||||||
|
// 0. <some text lines to skip, e.g. show description>\n
|
||||||
|
// 1. diff --git a/[fileName] b/[fileName]\n
|
||||||
|
// 2a. new file mode [fileModeNumber]\n
|
||||||
|
// 2b. deleted file mode [fileModeNumber]\n
|
||||||
|
// 2c. old mode [oldFileModeNumber]\n
|
||||||
|
// new mode [newFileModeNumber]\n
|
||||||
|
// 2d. <Nothing, only in case of ChangeFile>
|
||||||
|
// 3a. index [leftIndexSha]..[rightIndexSha] <optionally: octalNumber>
|
||||||
|
// 3b. <Nothing, only in case of ChangeFile, "Dirty submodule" case>
|
||||||
|
// 4a. <Nothing more, only possible in case of NewFile or DeleteFile> ???
|
||||||
|
// 4b. \nBinary files [leftFileNameOrDevNull] and [rightFileNameOrDevNull] differ
|
||||||
|
// 4c. --- [leftFileNameOrDevNull]\n
|
||||||
|
// +++ [rightFileNameOrDevNull]\n
|
||||||
|
// <Chunks>
|
||||||
|
|
||||||
|
// The git diff patch format (CopyFile, RenameFile)
|
||||||
|
// 0. [some text lines to skip, e.g. show description]\n
|
||||||
|
// 1. diff --git a/[leftFileName] b/[rightFileName]\n
|
||||||
|
// 2. [dis]similarity index [0-100]%\n
|
||||||
|
// [copy / rename] from [leftFileName]\n
|
||||||
|
// [copy / rename] to [rightFileName]
|
||||||
|
// 3a. <Nothing more, only when similarity index was 100%>
|
||||||
|
// 3b. index [leftIndexSha]..[rightIndexSha] <optionally: octalNumber>
|
||||||
|
// 4. --- [leftFileNameOrDevNull]\n
|
||||||
|
// +++ [rightFileNameOrDevNull]\n
|
||||||
|
// <Chunks>
|
||||||
|
|
||||||
|
static bool detectIndexAndBinary(QStringRef patch,
|
||||||
|
FileData *fileData,
|
||||||
|
QStringRef *remainingPatch)
|
||||||
{
|
{
|
||||||
return c == QLatin1Char('\n') || c == QLatin1Char('\t');
|
bool hasNewLine;
|
||||||
|
*remainingPatch = patch;
|
||||||
|
|
||||||
|
if (remainingPatch->isEmpty() && (fileData->fileOperation == FileData::CopyFile
|
||||||
|
|| fileData->fileOperation == FileData::RenameFile)) {
|
||||||
|
// in case of 100% similarity we don't have more lines in the patch
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringRef afterNextLine;
|
||||||
|
// index [leftIndexSha]..[rightIndexSha] <optionally: octalNumber>
|
||||||
|
const QStringRef nextLine = readLine(patch, &afterNextLine, &hasNewLine);
|
||||||
|
|
||||||
|
const QLatin1String indexHeader("index ");
|
||||||
|
|
||||||
|
if (nextLine.startsWith(indexHeader)) {
|
||||||
|
const QStringRef indices = nextLine.mid(indexHeader.size());
|
||||||
|
const int dotsPosition = indices.indexOf(QStringLiteral(".."));
|
||||||
|
if (dotsPosition < 0)
|
||||||
|
return false;
|
||||||
|
fileData->leftFileInfo.typeInfo = indices.left(dotsPosition).toString();
|
||||||
|
|
||||||
|
// if there is no space we take the remaining string
|
||||||
|
const int spacePosition = indices.indexOf(QChar::Space, dotsPosition + 2);
|
||||||
|
const int length = spacePosition < 0 ? -1 : spacePosition - dotsPosition - 2;
|
||||||
|
fileData->rightFileInfo.typeInfo = indices.mid(dotsPosition + 2, length).toString();
|
||||||
|
|
||||||
|
*remainingPatch = afterNextLine;
|
||||||
|
} else if (fileData->fileOperation != FileData::ChangeFile) {
|
||||||
|
// no index only in case of ChangeFile,
|
||||||
|
// the dirty submodule diff case, see "Dirty Submodule" test:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainingPatch->isEmpty() && (fileData->fileOperation == FileData::NewFile
|
||||||
|
|| fileData->fileOperation == FileData::DeleteFile)) {
|
||||||
|
// OK in case of empty file
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString devNull("/dev/null");
|
||||||
|
const QString leftFileName = fileData->fileOperation == FileData::NewFile
|
||||||
|
? devNull : QLatin1String("a/") + fileData->leftFileInfo.fileName;
|
||||||
|
const QString rightFileName = fileData->fileOperation == FileData::DeleteFile
|
||||||
|
? devNull : QLatin1String("b/") + fileData->rightFileInfo.fileName;
|
||||||
|
|
||||||
|
const QString binaryLine = QLatin1String("Binary files ")
|
||||||
|
+ leftFileName + QLatin1String(" and ")
|
||||||
|
+ rightFileName + QLatin1String(" differ");
|
||||||
|
|
||||||
|
if (*remainingPatch == binaryLine) {
|
||||||
|
fileData->binaryFiles = true;
|
||||||
|
*remainingPatch = QStringRef();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString leftStart = QLatin1String("--- ") + leftFileName;
|
||||||
|
QStringRef afterMinuses;
|
||||||
|
// --- leftFileName
|
||||||
|
const QStringRef minuses = readLine(*remainingPatch, &afterMinuses, &hasNewLine);
|
||||||
|
if (!hasNewLine)
|
||||||
|
return false; // we need to have at least one more line
|
||||||
|
|
||||||
|
if (!minuses.startsWith(leftStart))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const QString rightStart = QLatin1String("+++ ") + rightFileName;
|
||||||
|
QStringRef afterPluses;
|
||||||
|
// +++ rightFileName
|
||||||
|
const QStringRef pluses = readLine(afterMinuses, &afterPluses, &hasNewLine);
|
||||||
|
if (!hasNewLine)
|
||||||
|
return false; // we need to have at least one more line
|
||||||
|
|
||||||
|
if (!pluses.startsWith(rightStart))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*remainingPatch = afterPluses;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static FileData readGitHeaderAndChunks(QStringRef headerAndChunks,
|
static bool extractCommonFileName(QStringRef fileNames, QStringRef *fileName)
|
||||||
const QString &fileName,
|
|
||||||
bool *ok)
|
|
||||||
{
|
{
|
||||||
FileData fileData;
|
// we should have 1 space between filenames
|
||||||
fileData.leftFileInfo.fileName = fileName;
|
if (fileNames.size() % 2 == 0)
|
||||||
fileData.rightFileInfo.fileName = fileName;
|
return false;
|
||||||
|
|
||||||
QStringRef patch = headerAndChunks;
|
if (!fileNames.startsWith(QStringLiteral("a/")))
|
||||||
bool readOk = false;
|
return false;
|
||||||
|
|
||||||
const QString devNull(QLatin1String("/dev/null"));
|
// drop the space in between
|
||||||
|
const int fileNameSize = fileNames.size() / 2;
|
||||||
|
if (!fileNames.mid(fileNameSize).startsWith(" b/"))
|
||||||
|
return false;
|
||||||
|
|
||||||
// will be followed by: index 0000000..shasha, file "a" replaced by "/dev/null", @@ -0,0 +m,n @@
|
// drop "a/"
|
||||||
// new file mode octal
|
const QStringRef leftFileName = fileNames.mid(2, fileNameSize - 2);
|
||||||
const QRegularExpression newFileMode("^new file mode \\d+\\n");
|
|
||||||
|
|
||||||
// will be followed by: index shasha..0000000, file "b" replaced by "/dev/null", @@ -m,n +0,0 @@
|
// drop the first filename + " b/"
|
||||||
// deleted file mode octal
|
const QStringRef rightFileName = fileNames.mid(fileNameSize + 3, fileNameSize - 2);
|
||||||
const QRegularExpression deletedFileMode("^deleted file mode \\d+\\n");
|
|
||||||
|
|
||||||
const QRegularExpression modeChangeRegExp("^old mode \\d+\\nnew mode \\d+\\n");
|
if (leftFileName != rightFileName)
|
||||||
|
return false;
|
||||||
|
|
||||||
// index cap1..cap2(optionally: octal)
|
*fileName = leftFileName;
|
||||||
const QRegularExpression indexRegExp("^index (\\w+)\\.{2}(\\w+)(?: \\d+)?(\\n|$)");
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
QString leftFileName = QLatin1String("a/") + fileName;
|
static bool detectFileData(QStringRef patch,
|
||||||
QString rightFileName = QLatin1String("b/") + fileName;
|
FileData *fileData,
|
||||||
|
QStringRef *remainingPatch) {
|
||||||
|
bool hasNewLine;
|
||||||
|
|
||||||
const QRegularExpressionMatch newFileMatch = newFileMode.match(patch);
|
QStringRef afterDiffGit;
|
||||||
if (newFileMatch.hasMatch() && newFileMatch.capturedStart() == 0) {
|
// diff --git a/leftFileName b/rightFileName
|
||||||
fileData.fileOperation = FileData::NewFile;
|
const QStringRef diffGit = readLine(patch, &afterDiffGit, &hasNewLine);
|
||||||
leftFileName = devNull;
|
if (!hasNewLine)
|
||||||
patch = patch.mid(newFileMatch.capturedEnd());
|
return false; // we need to have at least one more line
|
||||||
} else {
|
|
||||||
const QRegularExpressionMatch deletedFileMatch = deletedFileMode.match(patch);
|
const QLatin1String gitHeader("diff --git ");
|
||||||
if (deletedFileMatch.hasMatch() && deletedFileMatch.capturedStart() == 0) {
|
const QStringRef fileNames = diffGit.mid(gitHeader.size());
|
||||||
fileData.fileOperation = FileData::DeleteFile;
|
QStringRef commonFileName;
|
||||||
rightFileName = devNull;
|
if (extractCommonFileName(fileNames, &commonFileName)) {
|
||||||
patch = patch.mid(deletedFileMatch.capturedEnd());
|
// change / new / delete
|
||||||
|
|
||||||
|
fileData->fileOperation = FileData::ChangeFile;
|
||||||
|
fileData->leftFileInfo.fileName = fileData->rightFileInfo.fileName = commonFileName.toString();
|
||||||
|
|
||||||
|
QStringRef afterSecondLine;
|
||||||
|
const QStringRef secondLine = readLine(afterDiffGit, &afterSecondLine, &hasNewLine);
|
||||||
|
if (!hasNewLine)
|
||||||
|
return false; // we need to have at least one more line
|
||||||
|
|
||||||
|
if (secondLine.startsWith(QStringLiteral("new file mode "))) {
|
||||||
|
fileData->fileOperation = FileData::NewFile;
|
||||||
|
*remainingPatch = afterSecondLine;
|
||||||
|
} else if (secondLine.startsWith(QStringLiteral("deleted file mode "))) {
|
||||||
|
fileData->fileOperation = FileData::DeleteFile;
|
||||||
|
*remainingPatch = afterSecondLine;
|
||||||
|
} else if (secondLine.startsWith(QStringLiteral("old mode "))) {
|
||||||
|
QStringRef afterThirdLine;
|
||||||
|
// new mode
|
||||||
|
readLine(afterSecondLine, &afterThirdLine, &hasNewLine);
|
||||||
|
if (!hasNewLine)
|
||||||
|
return false; // we need to have at least one more line
|
||||||
|
|
||||||
|
// TODO: validate new mode line
|
||||||
|
*remainingPatch = afterThirdLine;
|
||||||
} else {
|
} else {
|
||||||
const QRegularExpressionMatch modeChangeMatch = modeChangeRegExp.match(patch);
|
*remainingPatch = afterDiffGit;
|
||||||
if (modeChangeMatch.hasMatch() && modeChangeMatch.capturedStart() == 0) {
|
|
||||||
patch = patch.mid(modeChangeMatch.capturedEnd());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const QRegularExpressionMatch indexMatch = indexRegExp.match(patch);
|
} else {
|
||||||
if (indexMatch.hasMatch() && indexMatch.capturedStart() == 0) {
|
// copy / rename
|
||||||
fileData.leftFileInfo.typeInfo = indexMatch.captured(1);
|
|
||||||
fileData.rightFileInfo.typeInfo = indexMatch.captured(2);
|
|
||||||
|
|
||||||
patch = patch.mid(indexMatch.capturedEnd());
|
QStringRef afterSimilarity;
|
||||||
}
|
// (dis)similarity index [0-100]%
|
||||||
|
readLine(afterDiffGit, &afterSimilarity, &hasNewLine);
|
||||||
|
if (!hasNewLine)
|
||||||
|
return false; // we need to have at least one more line
|
||||||
|
|
||||||
const QString binaryLine = QString::fromLatin1("Binary files ") + leftFileName
|
// TODO: validate similarity line
|
||||||
+ QLatin1String(" and ") + rightFileName + QLatin1String(" differ");
|
|
||||||
const QString leftStart = QString::fromLatin1("--- ") + leftFileName;
|
|
||||||
QChar leftFollow = patch.count() > leftStart.count() ? patch.at(leftStart.count()) : QLatin1Char('\n');
|
|
||||||
|
|
||||||
// empty or followed either by leftFileRegExp or by binaryRegExp
|
QStringRef afterCopyRenameFrom;
|
||||||
if (patch.isEmpty() && (fileData.fileOperation == FileData::NewFile
|
// [copy / rename] from leftFileName
|
||||||
|| fileData.fileOperation == FileData::DeleteFile)) {
|
const QStringRef copyRenameFrom = readLine(afterSimilarity, &afterCopyRenameFrom, &hasNewLine);
|
||||||
readOk = true;
|
if (!hasNewLine)
|
||||||
} else if (patch.startsWith(leftStart) && fileNameEnd(leftFollow)) {
|
return false; // we need to have at least one more line
|
||||||
patch = patch.mid(patch.indexOf(QLatin1Char('\n'), leftStart.count()) + 1);
|
|
||||||
|
|
||||||
const QString rightStart = QString::fromLatin1("+++ ") + rightFileName;
|
const QLatin1String copyFrom("copy from ");
|
||||||
QChar rightFollow = patch.count() > rightStart.count() ? patch.at(rightStart.count()) : QLatin1Char('\n');
|
const QLatin1String renameFrom("rename from ");
|
||||||
|
if (copyRenameFrom.startsWith(copyFrom)) {
|
||||||
// followed by rightFileRegExp
|
fileData->fileOperation = FileData::CopyFile;
|
||||||
if (patch.startsWith(rightStart) && fileNameEnd(rightFollow)) {
|
fileData->leftFileInfo.fileName = copyRenameFrom.mid(copyFrom.size()).toString();
|
||||||
patch = patch.mid(patch.indexOf(QLatin1Char('\n'), rightStart.count()) + 1);
|
} else if (copyRenameFrom.startsWith(renameFrom)) {
|
||||||
|
fileData->fileOperation = FileData::RenameFile;
|
||||||
fileData.chunks = readChunks(patch,
|
fileData->leftFileInfo.fileName = copyRenameFrom.mid(renameFrom.size()).toString();
|
||||||
&fileData.lastChunkAtTheEndOfFile,
|
} else {
|
||||||
&readOk);
|
return false;
|
||||||
}
|
}
|
||||||
} else if (patch == binaryLine) {
|
|
||||||
readOk = true;
|
|
||||||
fileData.binaryFiles = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ok)
|
QStringRef afterCopyRenameTo;
|
||||||
*ok = readOk;
|
// [copy / rename] to rightFileName
|
||||||
|
const QStringRef copyRenameTo = readLine(afterCopyRenameFrom, &afterCopyRenameTo, &hasNewLine);
|
||||||
|
|
||||||
if (!readOk)
|
// if (dis)similarity index is 100% we don't have more lines
|
||||||
return FileData();
|
|
||||||
|
|
||||||
return fileData;
|
const QLatin1String copyTo("copy to ");
|
||||||
}
|
const QLatin1String renameTo("rename to ");
|
||||||
|
if (fileData->fileOperation == FileData::CopyFile && copyRenameTo.startsWith(copyTo)) {
|
||||||
static FileData readCopyRenameChunks(QStringRef copyRenameChunks,
|
fileData->rightFileInfo.fileName = copyRenameTo.mid(copyTo.size()).toString();
|
||||||
FileData::FileOperation fileOperation,
|
} else if (fileData->fileOperation == FileData::RenameFile && copyRenameTo.startsWith(renameTo)) {
|
||||||
const QString &leftFileName,
|
fileData->rightFileInfo.fileName = copyRenameTo.mid(renameTo.size()).toString();
|
||||||
const QString &rightFileName,
|
} else {
|
||||||
bool *ok)
|
return false;
|
||||||
{
|
|
||||||
FileData fileData;
|
|
||||||
fileData.fileOperation = fileOperation;
|
|
||||||
fileData.leftFileInfo.fileName = leftFileName;
|
|
||||||
fileData.rightFileInfo.fileName = rightFileName;
|
|
||||||
|
|
||||||
QStringRef patch = copyRenameChunks;
|
|
||||||
bool readOk = false;
|
|
||||||
|
|
||||||
// index cap1..cap2(optionally: octal)
|
|
||||||
const QRegularExpression indexRegExp("^index (\\w+)\\.{2}(\\w+)(?: \\d+)?(\\n|$)");
|
|
||||||
|
|
||||||
if (fileOperation == FileData::CopyFile || fileOperation == FileData::RenameFile) {
|
|
||||||
const QRegularExpressionMatch indexMatch = indexRegExp.match(patch);
|
|
||||||
if (indexMatch.hasMatch() && indexMatch.capturedStart() == 0) {
|
|
||||||
fileData.leftFileInfo.typeInfo = indexMatch.captured(1);
|
|
||||||
fileData.rightFileInfo.typeInfo = indexMatch.captured(2);
|
|
||||||
|
|
||||||
patch = patch.mid(indexMatch.capturedEnd());
|
|
||||||
|
|
||||||
const QString leftStart = QString::fromLatin1("--- a/") + leftFileName;
|
|
||||||
QChar leftFollow = patch.count() > leftStart.count() ? patch.at(leftStart.count()) : QLatin1Char('\n');
|
|
||||||
|
|
||||||
// followed by leftFileRegExp
|
|
||||||
if (patch.startsWith(leftStart) && fileNameEnd(leftFollow)) {
|
|
||||||
patch = patch.mid(patch.indexOf(QLatin1Char('\n'), leftStart.count()) + 1);
|
|
||||||
|
|
||||||
// followed by rightFileRegExp
|
|
||||||
const QString rightStart = QString::fromLatin1("+++ b/") + rightFileName;
|
|
||||||
QChar rightFollow = patch.count() > rightStart.count() ? patch.at(rightStart.count()) : QLatin1Char('\n');
|
|
||||||
|
|
||||||
// followed by rightFileRegExp
|
|
||||||
if (patch.startsWith(rightStart) && fileNameEnd(rightFollow)) {
|
|
||||||
patch = patch.mid(patch.indexOf(QLatin1Char('\n'), rightStart.count()) + 1);
|
|
||||||
|
|
||||||
fileData.chunks = readChunks(patch,
|
|
||||||
&fileData.lastChunkAtTheEndOfFile,
|
|
||||||
&readOk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (copyRenameChunks.isEmpty()) {
|
|
||||||
readOk = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*remainingPatch = afterCopyRenameTo;
|
||||||
}
|
}
|
||||||
|
return detectIndexAndBinary(*remainingPatch, fileData, remainingPatch);
|
||||||
if (ok)
|
|
||||||
*ok = readOk;
|
|
||||||
|
|
||||||
if (!readOk)
|
|
||||||
return FileData();
|
|
||||||
|
|
||||||
return fileData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static QList<FileData> readGitPatch(QStringRef patch, bool *ok,
|
static QList<FileData> readGitPatch(QStringRef patch, bool *ok,
|
||||||
QFutureInterfaceBase *jobController)
|
QFutureInterfaceBase *jobController)
|
||||||
{
|
{
|
||||||
|
int position = -1;
|
||||||
|
|
||||||
const QRegularExpression simpleGitRegExp(
|
QVector<int> startingPositions; // store starting positions of git headers
|
||||||
"^diff --git a/([^\\n]+) b/\\1\\n" // diff --git a/cap1 b/cap1
|
if (patch.startsWith(QStringLiteral("diff --git ")))
|
||||||
, QRegularExpression::MultilineOption);
|
startingPositions.append(position + 1);
|
||||||
|
|
||||||
const QRegularExpression similarityRegExp(
|
while ((position = patch.indexOf(QStringLiteral("\ndiff --git "), position + 1)) >= 0)
|
||||||
"^diff --git a/([^\\n]+) b/([^\\n]+)\\n" // diff --git a/cap1 b/cap2
|
startingPositions.append(position + 1);
|
||||||
"(?:dis)?similarity index \\d{1,3}%\\n" // similarity / dissimilarity index xxx% (100% max)
|
|
||||||
"(copy|rename) from \\1\\n" // copy / rename from cap1
|
|
||||||
"\\3 to \\2\\n" // copy / rename (cap3) to cap2
|
|
||||||
, QRegularExpression::MultilineOption);
|
|
||||||
|
|
||||||
bool readOk = false;
|
class PatchInfo {
|
||||||
|
public:
|
||||||
QList<FileData> fileDataList;
|
QStringRef patch;
|
||||||
|
FileData fileData;
|
||||||
bool simpleGitMatched;
|
|
||||||
int pos = 0;
|
|
||||||
QRegularExpressionMatch simpleGitMatch = simpleGitRegExp.match(patch);
|
|
||||||
QRegularExpressionMatch similarityMatch = similarityRegExp.match(patch);
|
|
||||||
auto calculateGitMatchAndPosition = [&]() {
|
|
||||||
if (pos > 0) { // don't advance in the initial call
|
|
||||||
if (simpleGitMatch.hasMatch() && similarityMatch.hasMatch()) {
|
|
||||||
const int simpleGitPos = simpleGitMatch.capturedStart();
|
|
||||||
const int similarityPos = similarityMatch.capturedStart();
|
|
||||||
if (simpleGitPos <= similarityPos)
|
|
||||||
simpleGitMatch = simpleGitRegExp.match(patch, simpleGitMatch.capturedEnd() - 1); // advance only simpleGit
|
|
||||||
else
|
|
||||||
similarityMatch = similarityRegExp.match(patch, similarityMatch.capturedEnd() - 1); // advance only similarity
|
|
||||||
} else if (simpleGitMatch.hasMatch()) {
|
|
||||||
simpleGitMatch = simpleGitRegExp.match(patch, simpleGitMatch.capturedEnd() - 1);
|
|
||||||
} else if (similarityMatch.hasMatch()) {
|
|
||||||
similarityMatch = similarityRegExp.match(patch, similarityMatch.capturedEnd() - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (simpleGitMatch.hasMatch() && similarityMatch.hasMatch()) {
|
|
||||||
const int simpleGitPos = simpleGitMatch.capturedStart();
|
|
||||||
const int similarityPos = similarityMatch.capturedStart();
|
|
||||||
pos = qMin(simpleGitPos, similarityPos);
|
|
||||||
simpleGitMatched = (pos == simpleGitPos);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (simpleGitMatch.hasMatch()) {
|
|
||||||
pos = simpleGitMatch.capturedStart();
|
|
||||||
simpleGitMatched = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (similarityMatch.hasMatch()) {
|
|
||||||
pos = similarityMatch.capturedStart();
|
|
||||||
simpleGitMatched = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pos = -1;
|
|
||||||
simpleGitMatched = true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set both pos and simpleGitMatched according to the first match:
|
const QChar newLine('\n');
|
||||||
calculateGitMatchAndPosition();
|
bool readOk = true;
|
||||||
|
|
||||||
if (pos >= 0) { // git style patch
|
QVector<PatchInfo> patches;
|
||||||
readOk = true;
|
const int count = startingPositions.count();
|
||||||
int endOfLastHeader = 0;
|
for (int i = 0; i < count; i++) {
|
||||||
QString lastLeftFileName;
|
if (jobController && jobController->isCanceled())
|
||||||
QString lastRightFileName;
|
return QList<FileData>();
|
||||||
FileData::FileOperation lastOperation = FileData::ChangeFile;
|
|
||||||
|
|
||||||
auto collectFileData = [&]() {
|
const int diffStart = startingPositions.at(i);
|
||||||
if (endOfLastHeader > 0 && readOk) {
|
const int diffEnd = (i < count - 1)
|
||||||
const int end = pos < 0 ? patch.count() : pos;
|
// drop the newline before the next header start
|
||||||
QStringRef headerAndChunks = patch.mid(endOfLastHeader,
|
? startingPositions.at(i + 1) - 1
|
||||||
qMax(end - endOfLastHeader - 1, 0));
|
// drop the possible newline by the end of file
|
||||||
|
: (patch.at(patch.count() - 1) == newLine ? patch.count() - 1 : patch.count());
|
||||||
|
|
||||||
FileData fileData;
|
// extract the patch for just one file
|
||||||
if (lastOperation == FileData::ChangeFile) {
|
const QStringRef fileDiff = patch.mid(diffStart, diffEnd - diffStart);
|
||||||
fileData = readGitHeaderAndChunks(headerAndChunks,
|
|
||||||
lastLeftFileName,
|
|
||||||
&readOk);
|
|
||||||
} else {
|
|
||||||
fileData = readCopyRenameChunks(headerAndChunks,
|
|
||||||
lastOperation,
|
|
||||||
lastLeftFileName,
|
|
||||||
lastRightFileName,
|
|
||||||
&readOk);
|
|
||||||
}
|
|
||||||
if (readOk)
|
|
||||||
fileDataList.append(fileData);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
do {
|
FileData fileData;
|
||||||
if (jobController && jobController->isCanceled())
|
QStringRef remainingFileDiff;
|
||||||
|
readOk = detectFileData(fileDiff, &fileData, &remainingFileDiff);
|
||||||
|
|
||||||
|
if (!readOk)
|
||||||
|
break;
|
||||||
|
|
||||||
|
patches.append(PatchInfo { remainingFileDiff, fileData });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!readOk) {
|
||||||
|
if (ok)
|
||||||
|
*ok = readOk;
|
||||||
|
return QList<FileData>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (jobController)
|
||||||
|
jobController->setProgressRange(0, patches.count());
|
||||||
|
|
||||||
|
QList<FileData> fileDataList;
|
||||||
|
readOk = false;
|
||||||
|
int i = 0;
|
||||||
|
for (const auto &patchInfo : Utils::asConst(patches)) {
|
||||||
|
if (jobController) {
|
||||||
|
if (jobController->isCanceled())
|
||||||
return QList<FileData>();
|
return QList<FileData>();
|
||||||
|
jobController->setProgressValue(i++);
|
||||||
|
}
|
||||||
|
|
||||||
collectFileData();
|
FileData fileData = patchInfo.fileData;
|
||||||
if (!readOk)
|
if (!patchInfo.patch.isEmpty() || fileData.fileOperation == FileData::ChangeFile)
|
||||||
break;
|
fileData.chunks = readChunks(patchInfo.patch, &fileData.lastChunkAtTheEndOfFile, &readOk);
|
||||||
|
else
|
||||||
|
readOk = true;
|
||||||
|
|
||||||
if (simpleGitMatched) {
|
if (!readOk)
|
||||||
const QString fileName = simpleGitMatch.captured(1);
|
break;
|
||||||
pos = simpleGitMatch.capturedEnd();
|
|
||||||
lastLeftFileName = fileName;
|
|
||||||
lastRightFileName = fileName;
|
|
||||||
lastOperation = FileData::ChangeFile;
|
|
||||||
} else {
|
|
||||||
lastLeftFileName = similarityMatch.captured(1);
|
|
||||||
lastRightFileName = similarityMatch.captured(2);
|
|
||||||
const QString operation = similarityMatch.captured(3);
|
|
||||||
pos = similarityMatch.capturedEnd();
|
|
||||||
if (operation == QLatin1String("copy"))
|
|
||||||
lastOperation = FileData::CopyFile;
|
|
||||||
else if (operation == QLatin1String("rename"))
|
|
||||||
lastOperation = FileData::RenameFile;
|
|
||||||
else
|
|
||||||
break; // either copy or rename, otherwise broken
|
|
||||||
}
|
|
||||||
endOfLastHeader = pos;
|
|
||||||
|
|
||||||
// give both pos and simpleGitMatched a new value for the next match
|
fileDataList.append(fileData);
|
||||||
calculateGitMatchAndPosition();
|
|
||||||
} while (pos != -1);
|
|
||||||
|
|
||||||
if (readOk)
|
|
||||||
collectFileData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ok)
|
if (ok)
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ protected:
|
|||||||
QColor replacementPenColor(int blockNumber) const override;
|
QColor replacementPenColor(int blockNumber) const override;
|
||||||
QString plainTextFromSelection(const QTextCursor &cursor) const override;
|
QString plainTextFromSelection(const QTextCursor &cursor) const override;
|
||||||
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
||||||
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||||
void paintEvent(QPaintEvent *e) override;
|
void paintEvent(QPaintEvent *e) override;
|
||||||
void scrollContentsBy(int dx, int dy) override;
|
void scrollContentsBy(int dx, int dy) override;
|
||||||
@@ -393,6 +394,16 @@ void SideDiffEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
|
|||||||
SelectableTextEditorWidget::mouseDoubleClickEvent(e);
|
SelectableTextEditorWidget::mouseDoubleClickEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SideDiffEditorWidget::keyPressEvent(QKeyEvent *e)
|
||||||
|
{
|
||||||
|
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
|
||||||
|
jumpToOriginalFile(textCursor());
|
||||||
|
e->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SelectableTextEditorWidget::keyPressEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
void SideDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
void SideDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
||||||
{
|
{
|
||||||
QPointer<QMenu> menu = createStandardContextMenu();
|
QPointer<QMenu> menu = createStandardContextMenu();
|
||||||
|
|||||||
@@ -161,6 +161,16 @@ void UnifiedDiffEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
|
|||||||
SelectableTextEditorWidget::mouseDoubleClickEvent(e);
|
SelectableTextEditorWidget::mouseDoubleClickEvent(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void UnifiedDiffEditorWidget::keyPressEvent(QKeyEvent *e)
|
||||||
|
{
|
||||||
|
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
|
||||||
|
jumpToOriginalFile(textCursor());
|
||||||
|
e->accept();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SelectableTextEditorWidget::keyPressEvent(e);
|
||||||
|
}
|
||||||
|
|
||||||
void UnifiedDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
void UnifiedDiffEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
||||||
{
|
{
|
||||||
QPointer<QMenu> menu = createStandardContextMenu();
|
QPointer<QMenu> menu = createStandardContextMenu();
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ signals:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
||||||
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
void contextMenuEvent(QContextMenuEvent *e) override;
|
void contextMenuEvent(QContextMenuEvent *e) override;
|
||||||
QString lineNumber(int blockNumber) const override;
|
QString lineNumber(int blockNumber) const override;
|
||||||
int lineNumberDigits() const override;
|
int lineNumberDigits() const override;
|
||||||
|
|||||||
@@ -394,10 +394,10 @@ IosQmlProfilerSupport::IosQmlProfilerSupport(RunControl *runControl)
|
|||||||
|
|
||||||
m_runner = new IosRunner(runControl);
|
m_runner = new IosRunner(runControl);
|
||||||
m_runner->setQmlDebugging(QmlDebug::QmlProfilerServices);
|
m_runner->setQmlDebugging(QmlDebug::QmlProfilerServices);
|
||||||
addDependency(m_runner);
|
addStartDependency(m_runner);
|
||||||
|
|
||||||
m_profiler = runControl->createWorker(runControl->runMode());
|
m_profiler = runControl->createWorker(runControl->runMode());
|
||||||
m_profiler->addDependency(this);
|
m_profiler->addStartDependency(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IosQmlProfilerSupport::start()
|
void IosQmlProfilerSupport::start()
|
||||||
@@ -428,7 +428,7 @@ IosDebugSupport::IosDebugSupport(RunControl *runControl)
|
|||||||
m_runner->setCppDebugging(isCppDebugging());
|
m_runner->setCppDebugging(isCppDebugging());
|
||||||
m_runner->setQmlDebugging(isQmlDebugging() ? QmlDebug::QmlDebuggerServices : QmlDebug::NoQmlDebugServices);
|
m_runner->setQmlDebugging(isQmlDebugging() ? QmlDebug::QmlDebuggerServices : QmlDebug::NoQmlDebugServices);
|
||||||
|
|
||||||
addDependency(m_runner);
|
addStartDependency(m_runner);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IosDebugSupport::start()
|
void IosDebugSupport::start()
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ void IosSettingsWidget::onReset()
|
|||||||
|
|
||||||
const int userInput = QMessageBox::question(this, tr("Reset"),
|
const int userInput = QMessageBox::question(this, tr("Reset"),
|
||||||
tr("Do you really want to reset the contents and settings"
|
tr("Do you really want to reset the contents and settings"
|
||||||
" of the selected devices", "",
|
" of the selected devices?", "",
|
||||||
simulatorInfoList.count()));
|
simulatorInfoList.count()));
|
||||||
if (userInput == QMessageBox::No)
|
if (userInput == QMessageBox::No)
|
||||||
return;
|
return;
|
||||||
@@ -243,7 +243,7 @@ void IosSettingsWidget::onDelete()
|
|||||||
|
|
||||||
const int userInput = QMessageBox::question(this, tr("Delete Device"),
|
const int userInput = QMessageBox::question(this, tr("Delete Device"),
|
||||||
tr("Do you really want to delete the selected "
|
tr("Do you really want to delete the selected "
|
||||||
"devices", "", simulatorInfoList.count()));
|
"devices?", "", simulatorInfoList.count()));
|
||||||
if (userInput == QMessageBox::No)
|
if (userInput == QMessageBox::No)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -37,11 +37,13 @@
|
|||||||
#include <ssh/sshconnection.h>
|
#include <ssh/sshconnection.h>
|
||||||
|
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
|
#include <utils/hostosinfo.h>
|
||||||
#include <utils/portlist.h>
|
#include <utils/portlist.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
using namespace ProjectExplorer::Constants;
|
using namespace ProjectExplorer::Constants;
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
|
|
||||||
@@ -137,6 +139,104 @@ DeviceEnvironmentFetcher::Ptr DesktopDevice::environmentFetcher() const
|
|||||||
return DeviceEnvironmentFetcher::Ptr(new DesktopDeviceEnvironmentFetcher());
|
return DeviceEnvironmentFetcher::Ptr(new DesktopDeviceEnvironmentFetcher());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DesktopPortsGatheringMethod : public PortsGatheringMethod
|
||||||
|
{
|
||||||
|
Runnable runnable(QAbstractSocket::NetworkLayerProtocol protocol) const override
|
||||||
|
{
|
||||||
|
// We might encounter the situation that protocol is given IPv6
|
||||||
|
// but the consumer of the free port information decides to open
|
||||||
|
// an IPv4(only) port. As a result the next IPv6 scan will
|
||||||
|
// report the port again as open (in IPv6 namespace), while the
|
||||||
|
// same port in IPv4 namespace might still be blocked, and
|
||||||
|
// re-use of this port fails.
|
||||||
|
// GDBserver behaves exactly like this.
|
||||||
|
|
||||||
|
Q_UNUSED(protocol)
|
||||||
|
|
||||||
|
StandardRunnable runnable;
|
||||||
|
if (HostOsInfo::isWindowsHost()) {
|
||||||
|
runnable.executable = "netstat";
|
||||||
|
runnable.commandLineArguments = "-a -n";
|
||||||
|
} else if (HostOsInfo::isLinuxHost()) {
|
||||||
|
runnable.executable = "/bin/sh";
|
||||||
|
runnable.commandLineArguments = "-c 'cat /proc/net/tcp*'";
|
||||||
|
}
|
||||||
|
return runnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Utils::Port> usedPorts(const QByteArray &output) const override
|
||||||
|
{
|
||||||
|
QList<Utils::Port> ports;
|
||||||
|
const QList<QByteArray> lines = output.split('\n');
|
||||||
|
if (HostOsInfo::isWindowsHost()) {
|
||||||
|
// Expected output is something like
|
||||||
|
//
|
||||||
|
// Active Connections
|
||||||
|
//
|
||||||
|
// Proto Local Address Foreign Address State
|
||||||
|
// TCP 0.0.0.0:80 0.0.0.0:0 LISTENING
|
||||||
|
// TCP 0.0.0.0:113 0.0.0.0:0 LISTENING
|
||||||
|
// [...]
|
||||||
|
// TCP 10.9.78.4:14714 0.0.0.0:0 LISTENING
|
||||||
|
// TCP 10.9.78.4:50233 12.13.135.180:993 ESTABLISHED
|
||||||
|
for (const QByteArray &line : lines) {
|
||||||
|
const QByteArray trimmed = line.trimmed();
|
||||||
|
if (!trimmed.startsWith("TCP"))
|
||||||
|
continue;
|
||||||
|
int colonPos = trimmed.indexOf(':');
|
||||||
|
if (colonPos < 0)
|
||||||
|
continue;
|
||||||
|
int spacePos = trimmed.indexOf(':', colonPos + 1);
|
||||||
|
if (spacePos < 0)
|
||||||
|
continue;
|
||||||
|
bool ok;
|
||||||
|
int len = spacePos - colonPos - 1;
|
||||||
|
const Utils::Port port(line.mid(colonPos + 1, len).toInt(&ok, 16));
|
||||||
|
if (ok) {
|
||||||
|
if (!ports.contains(port))
|
||||||
|
ports << port;
|
||||||
|
} else {
|
||||||
|
qWarning("%s: Unexpected string '%s' is not a port.",
|
||||||
|
Q_FUNC_INFO, line.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (HostOsInfo::isLinuxHost()) {
|
||||||
|
// Expected outpit is something like
|
||||||
|
//
|
||||||
|
// sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt ...
|
||||||
|
// 0: 00000000:2805 00000000:0000 0A 00000000:00000000 00:00000000 00000000 ...
|
||||||
|
//
|
||||||
|
for (const QByteArray &line : lines) {
|
||||||
|
int firstColonPos = line.indexOf(':');
|
||||||
|
if (firstColonPos < 0)
|
||||||
|
continue;
|
||||||
|
int secondColonPos = line.indexOf(':', firstColonPos + 1);
|
||||||
|
if (secondColonPos < 0)
|
||||||
|
continue;
|
||||||
|
int spacePos = line.indexOf(':', secondColonPos + 1);
|
||||||
|
if (spacePos < 0)
|
||||||
|
continue;
|
||||||
|
bool ok;
|
||||||
|
int len = spacePos - secondColonPos - 1;
|
||||||
|
const Utils::Port port(line.mid(secondColonPos + 1, len).toInt(&ok, 16));
|
||||||
|
if (ok) {
|
||||||
|
if (!ports.contains(port))
|
||||||
|
ports << port;
|
||||||
|
} else {
|
||||||
|
qWarning("%s: Unexpected string '%s' is not a port.",
|
||||||
|
Q_FUNC_INFO, line.data());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ports;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PortsGatheringMethod::Ptr DesktopDevice::portsGatheringMethod() const
|
||||||
|
{
|
||||||
|
return DesktopPortsGatheringMethod::Ptr(new DesktopPortsGatheringMethod);
|
||||||
|
}
|
||||||
|
|
||||||
QUrl DesktopDevice::toolControlChannel(const ControlChannelHint &) const
|
QUrl DesktopDevice::toolControlChannel(const ControlChannelHint &) const
|
||||||
{
|
{
|
||||||
QUrl url;
|
QUrl url;
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ public:
|
|||||||
bool canCreateProcessModel() const override;
|
bool canCreateProcessModel() const override;
|
||||||
DeviceProcessList *createProcessListModel(QObject *parent) const override;
|
DeviceProcessList *createProcessListModel(QObject *parent) const override;
|
||||||
bool canCreateProcess() const override { return true; }
|
bool canCreateProcess() const override { return true; }
|
||||||
|
ProjectExplorer::PortsGatheringMethod::Ptr portsGatheringMethod() const override;
|
||||||
DeviceProcess *createProcess(QObject *parent) const override;
|
DeviceProcess *createProcess(QObject *parent) const override;
|
||||||
DeviceProcessSignalOperation::Ptr signalOperation() const override;
|
DeviceProcessSignalOperation::Ptr signalOperation() const override;
|
||||||
DeviceEnvironmentFetcher::Ptr environmentFetcher() const override;
|
DeviceEnvironmentFetcher::Ptr environmentFetcher() const override;
|
||||||
|
|||||||
@@ -23,14 +23,14 @@
|
|||||||
**
|
**
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "deviceprocess.h"
|
||||||
#include "deviceusedportsgatherer.h"
|
#include "deviceusedportsgatherer.h"
|
||||||
|
|
||||||
|
#include <projectexplorer/runnables.h>
|
||||||
|
|
||||||
#include <utils/port.h>
|
#include <utils/port.h>
|
||||||
#include <utils/portlist.h>
|
#include <utils/portlist.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <ssh/sshconnection.h>
|
|
||||||
#include <ssh/sshconnectionmanager.h>
|
|
||||||
#include <ssh/sshremoteprocess.h>
|
|
||||||
|
|
||||||
using namespace QSsh;
|
using namespace QSsh;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
@@ -41,12 +41,12 @@ namespace Internal {
|
|||||||
class DeviceUsedPortsGathererPrivate
|
class DeviceUsedPortsGathererPrivate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SshConnection *connection;
|
QPointer<DeviceProcess> process;
|
||||||
SshRemoteProcess::Ptr process;
|
|
||||||
QList<Port> usedPorts;
|
QList<Port> usedPorts;
|
||||||
QByteArray remoteStdout;
|
QByteArray remoteStdout;
|
||||||
QByteArray remoteStderr;
|
QByteArray remoteStderr;
|
||||||
IDevice::ConstPtr device;
|
IDevice::ConstPtr device;
|
||||||
|
PortsGatheringMethod::Ptr portsGatheringMethod;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
@@ -54,7 +54,6 @@ class DeviceUsedPortsGathererPrivate
|
|||||||
DeviceUsedPortsGatherer::DeviceUsedPortsGatherer(QObject *parent) :
|
DeviceUsedPortsGatherer::DeviceUsedPortsGatherer(QObject *parent) :
|
||||||
QObject(parent), d(new Internal::DeviceUsedPortsGathererPrivate)
|
QObject(parent), d(new Internal::DeviceUsedPortsGathererPrivate)
|
||||||
{
|
{
|
||||||
d->connection = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceUsedPortsGatherer::~DeviceUsedPortsGatherer()
|
DeviceUsedPortsGatherer::~DeviceUsedPortsGatherer()
|
||||||
@@ -65,50 +64,36 @@ DeviceUsedPortsGatherer::~DeviceUsedPortsGatherer()
|
|||||||
|
|
||||||
void DeviceUsedPortsGatherer::start(const IDevice::ConstPtr &device)
|
void DeviceUsedPortsGatherer::start(const IDevice::ConstPtr &device)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(!d->connection, return);
|
|
||||||
QTC_ASSERT(device && device->portsGatheringMethod(), return);
|
|
||||||
|
|
||||||
d->device = device;
|
d->device = device;
|
||||||
d->connection = QSsh::acquireConnection(device->sshParameters());
|
QTC_ASSERT(d->device, emit error("No device given"); return);
|
||||||
connect(d->connection, &SshConnection::error,
|
|
||||||
this, &DeviceUsedPortsGatherer::handleConnectionError);
|
|
||||||
if (d->connection->state() == SshConnection::Connected) {
|
|
||||||
handleConnectionEstablished();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connect(d->connection, &SshConnection::connected,
|
|
||||||
this, &DeviceUsedPortsGatherer::handleConnectionEstablished);
|
|
||||||
if (d->connection->state() == SshConnection::Unconnected)
|
|
||||||
d->connection->connectToHost();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeviceUsedPortsGatherer::handleConnectionEstablished()
|
d->portsGatheringMethod = d->device->portsGatheringMethod();
|
||||||
{
|
QTC_ASSERT(d->portsGatheringMethod, emit error("Not implemented"); return);
|
||||||
const QAbstractSocket::NetworkLayerProtocol protocol
|
|
||||||
= d->connection->connectionInfo().localAddress.protocol();
|
|
||||||
const QByteArray commandLine = d->device->portsGatheringMethod()->commandLine(protocol);
|
|
||||||
d->process = d->connection->createRemoteProcess(commandLine);
|
|
||||||
|
|
||||||
connect(d->process.data(), &SshRemoteProcess::closed, this, &DeviceUsedPortsGatherer::handleProcessClosed);
|
const QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::AnyIPProtocol;
|
||||||
connect(d->process.data(), &SshRemoteProcess::readyReadStandardOutput, this, &DeviceUsedPortsGatherer::handleRemoteStdOut);
|
d->process = d->device->createProcess(this);
|
||||||
connect(d->process.data(), &SshRemoteProcess::readyReadStandardError, this, &DeviceUsedPortsGatherer::handleRemoteStdErr);
|
|
||||||
|
|
||||||
d->process->start();
|
connect(d->process.data(), &DeviceProcess::finished,
|
||||||
|
this, &DeviceUsedPortsGatherer::handleProcessFinished);
|
||||||
|
connect(d->process.data(), &DeviceProcess::error,
|
||||||
|
this, &DeviceUsedPortsGatherer::handleProcessError);
|
||||||
|
connect(d->process.data(), &DeviceProcess::readyReadStandardOutput,
|
||||||
|
this, &DeviceUsedPortsGatherer::handleRemoteStdOut);
|
||||||
|
connect(d->process.data(), &DeviceProcess::readyReadStandardError,
|
||||||
|
this, &DeviceUsedPortsGatherer::handleRemoteStdErr);
|
||||||
|
|
||||||
|
const Runnable runnable = d->portsGatheringMethod->runnable(protocol);
|
||||||
|
d->process->start(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceUsedPortsGatherer::stop()
|
void DeviceUsedPortsGatherer::stop()
|
||||||
{
|
{
|
||||||
if (!d->connection)
|
|
||||||
return;
|
|
||||||
d->usedPorts.clear();
|
d->usedPorts.clear();
|
||||||
d->remoteStdout.clear();
|
d->remoteStdout.clear();
|
||||||
d->remoteStderr.clear();
|
d->remoteStderr.clear();
|
||||||
if (d->process)
|
if (d->process)
|
||||||
disconnect(d->process.data(), 0, this, 0);
|
disconnect(d->process.data(), 0, this, 0);
|
||||||
d->process.clear();
|
d->process.clear();
|
||||||
disconnect(d->connection, 0, this, 0);
|
|
||||||
QSsh::releaseConnection(d->connection);
|
|
||||||
d->connection = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Port DeviceUsedPortsGatherer::getNextFreePort(PortList *freePorts) const
|
Port DeviceUsedPortsGatherer::getNextFreePort(PortList *freePorts) const
|
||||||
@@ -129,7 +114,7 @@ QList<Port> DeviceUsedPortsGatherer::usedPorts() const
|
|||||||
void DeviceUsedPortsGatherer::setupUsedPorts()
|
void DeviceUsedPortsGatherer::setupUsedPorts()
|
||||||
{
|
{
|
||||||
d->usedPorts.clear();
|
d->usedPorts.clear();
|
||||||
const QList<Port> usedPorts = d->device->portsGatheringMethod()->usedPorts(d->remoteStdout);
|
const QList<Port> usedPorts = d->portsGatheringMethod->usedPorts(d->remoteStdout);
|
||||||
foreach (const Port port, usedPorts) {
|
foreach (const Port port, usedPorts) {
|
||||||
if (d->device->freePorts().contains(port))
|
if (d->device->freePorts().contains(port))
|
||||||
d->usedPorts << port;
|
d->usedPorts << port;
|
||||||
@@ -137,27 +122,23 @@ void DeviceUsedPortsGatherer::setupUsedPorts()
|
|||||||
emit portListReady();
|
emit portListReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceUsedPortsGatherer::handleConnectionError()
|
void DeviceUsedPortsGatherer::handleProcessError()
|
||||||
{
|
{
|
||||||
if (!d->connection)
|
emit error(tr("Connection error: %1").arg(d->process->errorString()));
|
||||||
return;
|
|
||||||
emit error(tr("Connection error: %1").arg(d->connection->errorString()));
|
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceUsedPortsGatherer::handleProcessClosed(int exitStatus)
|
void DeviceUsedPortsGatherer::handleProcessFinished()
|
||||||
{
|
{
|
||||||
if (!d->connection)
|
if (!d->process)
|
||||||
return;
|
return;
|
||||||
QString errMsg;
|
QString errMsg;
|
||||||
|
QProcess::ExitStatus exitStatus = d->process->exitStatus();
|
||||||
switch (exitStatus) {
|
switch (exitStatus) {
|
||||||
case SshRemoteProcess::FailedToStart:
|
case QProcess::CrashExit:
|
||||||
errMsg = tr("Could not start remote process: %1").arg(d->process->errorString());
|
|
||||||
break;
|
|
||||||
case SshRemoteProcess::CrashExit:
|
|
||||||
errMsg = tr("Remote process crashed: %1").arg(d->process->errorString());
|
errMsg = tr("Remote process crashed: %1").arg(d->process->errorString());
|
||||||
break;
|
break;
|
||||||
case SshRemoteProcess::NormalExit:
|
case QProcess::NormalExit:
|
||||||
if (d->process->exitCode() == 0)
|
if (d->process->exitCode() == 0)
|
||||||
setupUsedPorts();
|
setupUsedPorts();
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
namespace ProjectExplorer {
|
namespace ProjectExplorer {
|
||||||
namespace Internal { class DeviceUsedPortsGathererPrivate; }
|
namespace Internal { class DeviceUsedPortsGathererPrivate; }
|
||||||
|
class StandardRunnable;
|
||||||
|
|
||||||
class PROJECTEXPLORER_EXPORT DeviceUsedPortsGatherer : public QObject
|
class PROJECTEXPLORER_EXPORT DeviceUsedPortsGatherer : public QObject
|
||||||
{
|
{
|
||||||
@@ -42,7 +43,7 @@ public:
|
|||||||
DeviceUsedPortsGatherer(QObject *parent = 0);
|
DeviceUsedPortsGatherer(QObject *parent = 0);
|
||||||
~DeviceUsedPortsGatherer() override;
|
~DeviceUsedPortsGatherer() override;
|
||||||
|
|
||||||
void start(const ProjectExplorer::IDevice::ConstPtr &device);
|
void start(const IDevice::ConstPtr &device);
|
||||||
void stop();
|
void stop();
|
||||||
Utils::Port getNextFreePort(Utils::PortList *freePorts) const; // returns -1 if no more are left
|
Utils::Port getNextFreePort(Utils::PortList *freePorts) const; // returns -1 if no more are left
|
||||||
QList<Utils::Port> usedPorts() const;
|
QList<Utils::Port> usedPorts() const;
|
||||||
@@ -52,11 +53,10 @@ signals:
|
|||||||
void portListReady();
|
void portListReady();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void handleConnectionEstablished();
|
|
||||||
void handleConnectionError();
|
|
||||||
void handleProcessClosed(int exitStatus);
|
|
||||||
void handleRemoteStdOut();
|
void handleRemoteStdOut();
|
||||||
void handleRemoteStdErr();
|
void handleRemoteStdErr();
|
||||||
|
void handleProcessError();
|
||||||
|
void handleProcessFinished();
|
||||||
|
|
||||||
void setupUsedPorts();
|
void setupUsedPorts();
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ class Connection;
|
|||||||
class DeviceProcess;
|
class DeviceProcess;
|
||||||
class DeviceProcessList;
|
class DeviceProcessList;
|
||||||
class Kit;
|
class Kit;
|
||||||
|
class Runnable;
|
||||||
class RunControl;
|
class RunControl;
|
||||||
class RunWorker;
|
class RunWorker;
|
||||||
|
|
||||||
@@ -110,7 +111,7 @@ public:
|
|||||||
typedef QSharedPointer<const PortsGatheringMethod> Ptr;
|
typedef QSharedPointer<const PortsGatheringMethod> Ptr;
|
||||||
|
|
||||||
virtual ~PortsGatheringMethod() = default;
|
virtual ~PortsGatheringMethod() = default;
|
||||||
virtual QByteArray commandLine(QAbstractSocket::NetworkLayerProtocol protocol) const = 0;
|
virtual Runnable runnable(QAbstractSocket::NetworkLayerProtocol protocol) const = 0;
|
||||||
virtual QList<Utils::Port> usedPorts(const QByteArray &commandOutput) const = 0;
|
virtual QList<Utils::Port> usedPorts(const QByteArray &commandOutput) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -410,6 +410,54 @@ void ToolChainKitInformation::setToolChain(Kit *k, ToolChain *tc)
|
|||||||
k->setValue(id(), result);
|
k->setValue(id(), result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief ToolChainKitInformation::setAllToolChainsToMatch
|
||||||
|
*
|
||||||
|
* Set up all toolchains to be similar to the one toolchain provided. Similar ideally means
|
||||||
|
* that all toolchains use the "same" compiler from the same installation, but we will
|
||||||
|
* settle for a toolchain with a matching API instead.
|
||||||
|
*
|
||||||
|
* @param k The kit to set up
|
||||||
|
* @param tc The toolchain to match other languages for.
|
||||||
|
*/
|
||||||
|
void ToolChainKitInformation::setAllToolChainsToMatch(Kit *k, ToolChain *tc)
|
||||||
|
{
|
||||||
|
QTC_ASSERT(tc, return);
|
||||||
|
|
||||||
|
const QList<ToolChain *> allTcList = ToolChainManager::toolChains();
|
||||||
|
QTC_ASSERT(allTcList.contains(tc), return);
|
||||||
|
|
||||||
|
QVariantMap result = k->value(ToolChainKitInformation::id()).toMap();
|
||||||
|
result.insert(tc->language().toString(), tc->id());
|
||||||
|
|
||||||
|
for (Core::Id l : ToolChainManager::allLanguages()) {
|
||||||
|
if (l == tc->language())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ToolChain *match = nullptr;
|
||||||
|
ToolChain *bestMatch = nullptr;
|
||||||
|
for (ToolChain *other : allTcList) {
|
||||||
|
if (!other->isValid() || other->language() != l)
|
||||||
|
continue;
|
||||||
|
if (other->targetAbi() == tc->targetAbi())
|
||||||
|
match = other;
|
||||||
|
if (match == other
|
||||||
|
&& other->compilerCommand().parentDir() == tc->compilerCommand().parentDir()) {
|
||||||
|
bestMatch = other;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bestMatch)
|
||||||
|
result.insert(l.toString(), bestMatch->id());
|
||||||
|
else if (match)
|
||||||
|
result.insert(l.toString(), match->id());
|
||||||
|
else
|
||||||
|
result.insert(l.toString(), QByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
k->setValue(id(), result);
|
||||||
|
}
|
||||||
|
|
||||||
void ToolChainKitInformation::clearToolChain(Kit *k, Core::Id language)
|
void ToolChainKitInformation::clearToolChain(Kit *k, Core::Id language)
|
||||||
{
|
{
|
||||||
QTC_ASSERT(language.isValid(), return);
|
QTC_ASSERT(language.isValid(), return);
|
||||||
|
|||||||
@@ -98,6 +98,7 @@ public:
|
|||||||
static ToolChain *toolChain(const Kit *k, Core::Id language);
|
static ToolChain *toolChain(const Kit *k, Core::Id language);
|
||||||
static QList<ToolChain *> toolChains(const Kit *k);
|
static QList<ToolChain *> toolChains(const Kit *k);
|
||||||
static void setToolChain(Kit *k, ToolChain *tc);
|
static void setToolChain(Kit *k, ToolChain *tc);
|
||||||
|
static void setAllToolChainsToMatch(Kit *k, ToolChain *tc);
|
||||||
static void clearToolChain(Kit *k, Core::Id language);
|
static void clearToolChain(Kit *k, Core::Id language);
|
||||||
static Abi targetAbi(const Kit *k);
|
static Abi targetAbi(const Kit *k);
|
||||||
|
|
||||||
|
|||||||
@@ -212,7 +212,10 @@ void FlatModel::addOrRebuildProjectModel(Project *project)
|
|||||||
|
|
||||||
if (ProjectNode *projectNode = project->rootProjectNode()) {
|
if (ProjectNode *projectNode = project->rootProjectNode()) {
|
||||||
addFolderNode(container, projectNode, &seen);
|
addFolderNode(container, projectNode, &seen);
|
||||||
} else {
|
if (m_trimEmptyDirectories)
|
||||||
|
trimEmptyDirectories(container);
|
||||||
|
}
|
||||||
|
if (container->childCount() == 0) {
|
||||||
FileNode *projectFileNode = new FileNode(project->projectFilePath(), FileType::Project, false);
|
FileNode *projectFileNode = new FileNode(project->projectFilePath(), FileType::Project, false);
|
||||||
seen.insert(projectFileNode);
|
seen.insert(projectFileNode);
|
||||||
container->appendChild(new WrapperNode(projectFileNode));
|
container->appendChild(new WrapperNode(projectFileNode));
|
||||||
@@ -344,7 +347,7 @@ void FlatModel::addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet<
|
|||||||
addFolderNode(parent, subFolderNode, seen);
|
addFolderNode(parent, subFolderNode, seen);
|
||||||
}
|
}
|
||||||
} else if (FileNode *fileNode = node->asFileNode()) {
|
} else if (FileNode *fileNode = node->asFileNode()) {
|
||||||
const bool isHidden = m_filterProjects && fileNode->isGenerated();
|
const bool isHidden = m_filterGeneratedFiles && fileNode->isGenerated();
|
||||||
if (!isHidden && !seen->contains(fileNode)) {
|
if (!isHidden && !seen->contains(fileNode)) {
|
||||||
seen->insert(fileNode);
|
seen->insert(fileNode);
|
||||||
parent->appendChild(new WrapperNode(fileNode));
|
parent->appendChild(new WrapperNode(fileNode));
|
||||||
@@ -353,6 +356,18 @@ void FlatModel::addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FlatModel::trimEmptyDirectories(WrapperNode *parent)
|
||||||
|
{
|
||||||
|
if (!parent->m_node->asFolderNode())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = parent->childCount() - 1; i >= 0; --i) {
|
||||||
|
if (trimEmptyDirectories(parent->childAt(i)))
|
||||||
|
parent->removeChildAt(i);
|
||||||
|
}
|
||||||
|
return parent->childCount() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
Qt::DropActions FlatModel::supportedDragActions() const
|
Qt::DropActions FlatModel::supportedDragActions() const
|
||||||
{
|
{
|
||||||
return Qt::MoveAction;
|
return Qt::MoveAction;
|
||||||
@@ -399,10 +414,20 @@ void FlatModel::setProjectFilterEnabled(bool filter)
|
|||||||
|
|
||||||
void FlatModel::setGeneratedFilesFilterEnabled(bool filter)
|
void FlatModel::setGeneratedFilesFilterEnabled(bool filter)
|
||||||
{
|
{
|
||||||
|
if (filter == m_filterGeneratedFiles)
|
||||||
|
return;
|
||||||
m_filterGeneratedFiles = filter;
|
m_filterGeneratedFiles = filter;
|
||||||
rebuildModel();
|
rebuildModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FlatModel::setTrimEmptyDirectories(bool filter)
|
||||||
|
{
|
||||||
|
if (filter == m_trimEmptyDirectories)
|
||||||
|
return;
|
||||||
|
m_trimEmptyDirectories = filter;
|
||||||
|
rebuildModel();
|
||||||
|
}
|
||||||
|
|
||||||
bool FlatModel::projectFilterEnabled()
|
bool FlatModel::projectFilterEnabled()
|
||||||
{
|
{
|
||||||
return m_filterProjects;
|
return m_filterProjects;
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ public:
|
|||||||
bool generatedFilesFilterEnabled();
|
bool generatedFilesFilterEnabled();
|
||||||
void setProjectFilterEnabled(bool filter);
|
void setProjectFilterEnabled(bool filter);
|
||||||
void setGeneratedFilesFilterEnabled(bool filter);
|
void setGeneratedFilesFilterEnabled(bool filter);
|
||||||
|
void setTrimEmptyDirectories(bool filter);
|
||||||
|
|
||||||
void onExpanded(const QModelIndex &idx);
|
void onExpanded(const QModelIndex &idx);
|
||||||
void onCollapsed(const QModelIndex &idx);
|
void onCollapsed(const QModelIndex &idx);
|
||||||
@@ -87,12 +88,14 @@ signals:
|
|||||||
private:
|
private:
|
||||||
bool m_filterProjects = false;
|
bool m_filterProjects = false;
|
||||||
bool m_filterGeneratedFiles = true;
|
bool m_filterGeneratedFiles = true;
|
||||||
|
bool m_trimEmptyDirectories = true;
|
||||||
|
|
||||||
static const QLoggingCategory &logger();
|
static const QLoggingCategory &logger();
|
||||||
|
|
||||||
void updateSubtree(FolderNode *node);
|
void updateSubtree(FolderNode *node);
|
||||||
void rebuildModel();
|
void rebuildModel();
|
||||||
void addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet<Node *> *seen);
|
void addFolderNode(WrapperNode *parent, FolderNode *folderNode, QSet<Node *> *seen);
|
||||||
|
bool trimEmptyDirectories(WrapperNode *parent);
|
||||||
|
|
||||||
ExpandData expandDataForNode(const Node *node) const;
|
ExpandData expandDataForNode(const Node *node) const;
|
||||||
void loadExpandData();
|
void loadExpandData();
|
||||||
|
|||||||
@@ -119,8 +119,8 @@ static FolderNode *recursiveFindOrCreateFolderNode(FolderNode *folder,
|
|||||||
\sa ProjectExplorer::NodesWatcher
|
\sa ProjectExplorer::NodesWatcher
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Node::Node(NodeType nodeType, const Utils::FileName &filePath, int line) :
|
Node::Node(NodeType nodeType, const Utils::FileName &filePath, int line, const QByteArray &id) :
|
||||||
m_filePath(filePath), m_line(line), m_nodeType(nodeType)
|
m_filePath(filePath), m_nodeId(id), m_line(line), m_nodeType(nodeType)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void Node::setPriority(int p)
|
void Node::setPriority(int p)
|
||||||
@@ -222,6 +222,11 @@ int Node::line() const
|
|||||||
return m_line;
|
return m_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray Node::id() const
|
||||||
|
{
|
||||||
|
return m_nodeId;
|
||||||
|
}
|
||||||
|
|
||||||
QString Node::displayName() const
|
QString Node::displayName() const
|
||||||
{
|
{
|
||||||
return filePath().fileName();
|
return filePath().fileName();
|
||||||
@@ -309,9 +314,9 @@ FileType Node::fileTypeForFileName(const Utils::FileName &file)
|
|||||||
\sa ProjectExplorer::FolderNode, ProjectExplorer::ProjectNode
|
\sa ProjectExplorer::FolderNode, ProjectExplorer::ProjectNode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
FileNode::FileNode(const Utils::FileName &filePath,
|
FileNode::FileNode(const Utils::FileName &filePath, const FileType fileType, bool generated,
|
||||||
const FileType fileType,
|
int line, const QByteArray &id) :
|
||||||
bool generated, int line) : Node(NodeType::File, filePath, line),
|
Node(NodeType::File, filePath, line, id),
|
||||||
m_fileType(fileType)
|
m_fileType(fileType)
|
||||||
{
|
{
|
||||||
setListInProject(true);
|
setListInProject(true);
|
||||||
@@ -324,7 +329,7 @@ FileNode::FileNode(const Utils::FileName &filePath,
|
|||||||
|
|
||||||
FileNode *FileNode::clone() const
|
FileNode *FileNode::clone() const
|
||||||
{
|
{
|
||||||
auto fn = new FileNode(filePath(), fileType(), isGenerated(), line());
|
auto fn = new FileNode(filePath(), fileType(), isGenerated(), line(), id());
|
||||||
fn->setEnabled(isEnabled());
|
fn->setEnabled(isEnabled());
|
||||||
fn->setPriority(priority());
|
fn->setPriority(priority());
|
||||||
fn->setListInProject(listInProject());
|
fn->setListInProject(listInProject());
|
||||||
@@ -418,8 +423,9 @@ bool FileNode::supportsAction(ProjectAction action, const Node *node) const
|
|||||||
|
|
||||||
\sa ProjectExplorer::FileNode, ProjectExplorer::ProjectNode
|
\sa ProjectExplorer::FileNode, ProjectExplorer::ProjectNode
|
||||||
*/
|
*/
|
||||||
FolderNode::FolderNode(const Utils::FileName &folderPath, NodeType nodeType, const QString &displayName) :
|
FolderNode::FolderNode(const Utils::FileName &folderPath, NodeType nodeType,
|
||||||
Node(nodeType, folderPath, -1),
|
const QString &displayName, const QByteArray &id) :
|
||||||
|
Node(nodeType, folderPath, -1, id),
|
||||||
m_displayName(displayName)
|
m_displayName(displayName)
|
||||||
{
|
{
|
||||||
setPriority(DefaultFolderPriority);
|
setPriority(DefaultFolderPriority);
|
||||||
@@ -745,8 +751,9 @@ bool FolderNode::showInSimpleTree() const
|
|||||||
|
|
||||||
\sa ProjectExplorer::FileNode, ProjectExplorer::ProjectNode
|
\sa ProjectExplorer::FileNode, ProjectExplorer::ProjectNode
|
||||||
*/
|
*/
|
||||||
VirtualFolderNode::VirtualFolderNode(const Utils::FileName &folderPath, int priority) :
|
VirtualFolderNode::VirtualFolderNode(const Utils::FileName &folderPath, int priority,
|
||||||
FolderNode(folderPath, NodeType::VirtualFolder, QString())
|
const QByteArray &id) :
|
||||||
|
FolderNode(folderPath, NodeType::VirtualFolder, QString(), id)
|
||||||
{
|
{
|
||||||
setPriority(priority);
|
setPriority(priority);
|
||||||
}
|
}
|
||||||
@@ -771,12 +778,11 @@ QString VirtualFolderNode::addFileFilter() const
|
|||||||
/*!
|
/*!
|
||||||
Creates an uninitialized project node object.
|
Creates an uninitialized project node object.
|
||||||
*/
|
*/
|
||||||
ProjectNode::ProjectNode(const Utils::FileName &projectFilePath) :
|
ProjectNode::ProjectNode(const Utils::FileName &projectFilePath, const QByteArray &id) :
|
||||||
FolderNode(projectFilePath, NodeType::Project)
|
FolderNode(projectFilePath, NodeType::Project, projectFilePath.fileName(), id)
|
||||||
{
|
{
|
||||||
setPriority(DefaultProjectPriority);
|
setPriority(DefaultProjectPriority);
|
||||||
setListInProject(true);
|
setListInProject(true);
|
||||||
setDisplayName(projectFilePath.fileName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProjectNode::canAddSubProject(const QString &proFilePath) const
|
bool ProjectNode::canAddSubProject(const QString &proFilePath) const
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ public:
|
|||||||
|
|
||||||
const Utils::FileName &filePath() const; // file system path
|
const Utils::FileName &filePath() const; // file system path
|
||||||
int line() const;
|
int line() const;
|
||||||
|
QByteArray id() const;
|
||||||
virtual QString displayName() const;
|
virtual QString displayName() const;
|
||||||
virtual QString tooltip() const;
|
virtual QString tooltip() const;
|
||||||
bool isEnabled() const;
|
bool isEnabled() const;
|
||||||
@@ -154,7 +155,8 @@ public:
|
|||||||
static FileType fileTypeForFileName(const Utils::FileName &file);
|
static FileType fileTypeForFileName(const Utils::FileName &file);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Node(NodeType nodeType, const Utils::FileName &filePath, int line = -1);
|
Node(NodeType nodeType, const Utils::FileName &filePath, int line = -1,
|
||||||
|
const QByteArray &id = {});
|
||||||
|
|
||||||
void setPriority(int priority);
|
void setPriority(int priority);
|
||||||
void setIsGenerated(bool g);
|
void setIsGenerated(bool g);
|
||||||
@@ -162,6 +164,7 @@ protected:
|
|||||||
private:
|
private:
|
||||||
FolderNode *m_parentFolderNode = nullptr;
|
FolderNode *m_parentFolderNode = nullptr;
|
||||||
Utils::FileName m_filePath;
|
Utils::FileName m_filePath;
|
||||||
|
QByteArray m_nodeId;
|
||||||
int m_line = -1;
|
int m_line = -1;
|
||||||
int m_priority = DefaultPriority;
|
int m_priority = DefaultPriority;
|
||||||
const NodeType m_nodeType;
|
const NodeType m_nodeType;
|
||||||
@@ -178,7 +181,8 @@ private:
|
|||||||
class PROJECTEXPLORER_EXPORT FileNode : public Node
|
class PROJECTEXPLORER_EXPORT FileNode : public Node
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FileNode(const Utils::FileName &filePath, const FileType fileType, bool generated, int line = -1);
|
FileNode(const Utils::FileName &filePath, const FileType fileType, bool generated, int line = -1,
|
||||||
|
const QByteArray &id = {});
|
||||||
|
|
||||||
FileNode *clone() const;
|
FileNode *clone() const;
|
||||||
|
|
||||||
@@ -208,7 +212,7 @@ class PROJECTEXPLORER_EXPORT FolderNode : public Node
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit FolderNode(const Utils::FileName &folderPath, NodeType nodeType = NodeType::Folder,
|
explicit FolderNode(const Utils::FileName &folderPath, NodeType nodeType = NodeType::Folder,
|
||||||
const QString &displayName = QString());
|
const QString &displayName = QString(), const QByteArray &id = {});
|
||||||
~FolderNode() override;
|
~FolderNode() override;
|
||||||
|
|
||||||
QString displayName() const override;
|
QString displayName() const override;
|
||||||
@@ -299,7 +303,8 @@ private:
|
|||||||
class PROJECTEXPLORER_EXPORT VirtualFolderNode : public FolderNode
|
class PROJECTEXPLORER_EXPORT VirtualFolderNode : public FolderNode
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit VirtualFolderNode(const Utils::FileName &folderPath, int priority);
|
explicit VirtualFolderNode(const Utils::FileName &folderPath, int priority,
|
||||||
|
const QByteArray &id = {});
|
||||||
|
|
||||||
void setAddFileFilter(const QString &filter) { m_addFileFilter = filter; }
|
void setAddFileFilter(const QString &filter) { m_addFileFilter = filter; }
|
||||||
QString addFileFilter() const override;
|
QString addFileFilter() const override;
|
||||||
@@ -334,7 +339,7 @@ public:
|
|||||||
const ProjectNode *asProjectNode() const final { return this; }
|
const ProjectNode *asProjectNode() const final { return this; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit ProjectNode(const Utils::FileName &projectFilePath);
|
explicit ProjectNode(const Utils::FileName &projectFilePath, const QByteArray &id = {});
|
||||||
};
|
};
|
||||||
|
|
||||||
class PROJECTEXPLORER_EXPORT ContainerNode : public FolderNode
|
class PROJECTEXPLORER_EXPORT ContainerNode : public FolderNode
|
||||||
|
|||||||
@@ -255,6 +255,12 @@ ProjectTreeWidget::ProjectTreeWidget(QWidget *parent) : QWidget(parent)
|
|||||||
connect(m_filterGeneratedFilesAction, &QAction::toggled,
|
connect(m_filterGeneratedFilesAction, &QAction::toggled,
|
||||||
this, &ProjectTreeWidget::setGeneratedFilesFilter);
|
this, &ProjectTreeWidget::setGeneratedFilesFilter);
|
||||||
|
|
||||||
|
m_trimEmptyDirectoriesAction = new QAction(tr("Hide Empty Directories"), this);
|
||||||
|
m_trimEmptyDirectoriesAction->setCheckable(true);
|
||||||
|
m_trimEmptyDirectoriesAction->setChecked(true);
|
||||||
|
connect(m_trimEmptyDirectoriesAction, &QAction::toggled,
|
||||||
|
this, &ProjectTreeWidget::setTrimEmptyDirectories);
|
||||||
|
|
||||||
// connections
|
// connections
|
||||||
connect(m_model, &FlatModel::renamed,
|
connect(m_model, &FlatModel::renamed,
|
||||||
this, &ProjectTreeWidget::renamed);
|
this, &ProjectTreeWidget::renamed);
|
||||||
@@ -494,6 +500,12 @@ void ProjectTreeWidget::setGeneratedFilesFilter(bool filter)
|
|||||||
m_filterGeneratedFilesAction->setChecked(filter);
|
m_filterGeneratedFilesAction->setChecked(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectTreeWidget::setTrimEmptyDirectories(bool filter)
|
||||||
|
{
|
||||||
|
m_model->setTrimEmptyDirectories(filter);
|
||||||
|
m_trimEmptyDirectoriesAction->setChecked(filter);
|
||||||
|
}
|
||||||
|
|
||||||
bool ProjectTreeWidget::generatedFilesFilter()
|
bool ProjectTreeWidget::generatedFilesFilter()
|
||||||
{
|
{
|
||||||
return m_model->generatedFilesFilterEnabled();
|
return m_model->generatedFilesFilterEnabled();
|
||||||
@@ -527,6 +539,7 @@ NavigationView ProjectTreeWidgetFactory::createWidget()
|
|||||||
auto filterMenu = new QMenu(filter);
|
auto filterMenu = new QMenu(filter);
|
||||||
filterMenu->addAction(ptw->m_filterProjectsAction);
|
filterMenu->addAction(ptw->m_filterProjectsAction);
|
||||||
filterMenu->addAction(ptw->m_filterGeneratedFilesAction);
|
filterMenu->addAction(ptw->m_filterGeneratedFilesAction);
|
||||||
|
filterMenu->addAction(ptw->m_trimEmptyDirectoriesAction);
|
||||||
filter->setMenu(filterMenu);
|
filter->setMenu(filterMenu);
|
||||||
|
|
||||||
n.dockToolBarWidgets << filter << ptw->toggleSync();
|
n.dockToolBarWidgets << filter << ptw->toggleSync();
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
void setProjectFilter(bool filter);
|
void setProjectFilter(bool filter);
|
||||||
void setGeneratedFilesFilter(bool filter);
|
void setGeneratedFilesFilter(bool filter);
|
||||||
|
void setTrimEmptyDirectories(bool filter);
|
||||||
|
|
||||||
void handleCurrentItemChange(const QModelIndex ¤t);
|
void handleCurrentItemChange(const QModelIndex ¤t);
|
||||||
void showContextMenu(const QPoint &pos);
|
void showContextMenu(const QPoint &pos);
|
||||||
@@ -83,8 +84,9 @@ private:
|
|||||||
QTreeView *m_view = nullptr;
|
QTreeView *m_view = nullptr;
|
||||||
FlatModel *m_model = nullptr;
|
FlatModel *m_model = nullptr;
|
||||||
QAction *m_filterProjectsAction = nullptr;
|
QAction *m_filterProjectsAction = nullptr;
|
||||||
QAction *m_filterGeneratedFilesAction;
|
QAction *m_filterGeneratedFilesAction = nullptr;
|
||||||
QToolButton *m_toggleSync;
|
QAction *m_trimEmptyDirectoriesAction = nullptr;
|
||||||
|
QToolButton *m_toggleSync = nullptr;
|
||||||
|
|
||||||
QString m_modelId;
|
QString m_modelId;
|
||||||
bool m_autoSync = true;
|
bool m_autoSync = true;
|
||||||
|
|||||||
@@ -533,7 +533,7 @@ namespace Internal {
|
|||||||
|
|
||||||
enum class RunWorkerState
|
enum class RunWorkerState
|
||||||
{
|
{
|
||||||
Initialized, Starting, Running, Stopping, Done, Failed
|
Initialized, Starting, Running, Stopping, Done
|
||||||
};
|
};
|
||||||
|
|
||||||
static QString stateName(RunWorkerState s)
|
static QString stateName(RunWorkerState s)
|
||||||
@@ -545,7 +545,6 @@ static QString stateName(RunWorkerState s)
|
|||||||
SN(RunWorkerState::Running)
|
SN(RunWorkerState::Running)
|
||||||
SN(RunWorkerState::Stopping)
|
SN(RunWorkerState::Stopping)
|
||||||
SN(RunWorkerState::Done)
|
SN(RunWorkerState::Done)
|
||||||
SN(RunWorkerState::Failed)
|
|
||||||
}
|
}
|
||||||
return QString("<unknown: %1>").arg(int(s));
|
return QString("<unknown: %1>").arg(int(s));
|
||||||
# undef SN
|
# undef SN
|
||||||
@@ -557,12 +556,14 @@ public:
|
|||||||
RunWorkerPrivate(RunWorker *runWorker, RunControl *runControl);
|
RunWorkerPrivate(RunWorker *runWorker, RunControl *runControl);
|
||||||
|
|
||||||
bool canStart() const;
|
bool canStart() const;
|
||||||
|
bool canStop() const;
|
||||||
void timerEvent(QTimerEvent *ev) override;
|
void timerEvent(QTimerEvent *ev) override;
|
||||||
|
|
||||||
RunWorker *q;
|
RunWorker *q;
|
||||||
RunWorkerState state = RunWorkerState::Initialized;
|
RunWorkerState state = RunWorkerState::Initialized;
|
||||||
QPointer<RunControl> runControl;
|
QPointer<RunControl> runControl;
|
||||||
QList<RunWorker *> dependencies;
|
QList<RunWorker *> startDependencies;
|
||||||
|
QList<RunWorker *> stopDependencies;
|
||||||
QString id;
|
QString id;
|
||||||
|
|
||||||
QVariantMap data;
|
QVariantMap data;
|
||||||
@@ -571,6 +572,7 @@ public:
|
|||||||
int stopWatchdogInterval = 0; // 5000;
|
int stopWatchdogInterval = 0; // 5000;
|
||||||
int stopWatchdogTimerId = -1;
|
int stopWatchdogTimerId = -1;
|
||||||
bool supportsReRunning = true;
|
bool supportsReRunning = true;
|
||||||
|
bool essential = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class RunControlState
|
enum class RunControlState
|
||||||
@@ -637,6 +639,7 @@ public:
|
|||||||
void initiateReStart();
|
void initiateReStart();
|
||||||
void continueStart();
|
void continueStart();
|
||||||
void initiateStop();
|
void initiateStop();
|
||||||
|
void continueStopOrFinish();
|
||||||
void initiateFinish();
|
void initiateFinish();
|
||||||
|
|
||||||
void onWorkerStarted(RunWorker *worker);
|
void onWorkerStarted(RunWorker *worker);
|
||||||
@@ -834,12 +837,6 @@ void RunControlPrivate::continueStart()
|
|||||||
case RunWorkerState::Stopping:
|
case RunWorkerState::Stopping:
|
||||||
debugMessage(" " + workerId + " currently stopping");
|
debugMessage(" " + workerId + " currently stopping");
|
||||||
continue;
|
continue;
|
||||||
case RunWorkerState::Failed:
|
|
||||||
// Should not happen.
|
|
||||||
debugMessage(" " + workerId + " failed before");
|
|
||||||
QTC_CHECK(false);
|
|
||||||
//setState(RunControlState::Stopped);
|
|
||||||
break;
|
|
||||||
case RunWorkerState::Done:
|
case RunWorkerState::Done:
|
||||||
debugMessage(" " + workerId + " was done before");
|
debugMessage(" " + workerId + " was done before");
|
||||||
break;
|
break;
|
||||||
@@ -854,11 +851,29 @@ void RunControlPrivate::continueStart()
|
|||||||
|
|
||||||
void RunControlPrivate::initiateStop()
|
void RunControlPrivate::initiateStop()
|
||||||
{
|
{
|
||||||
checkState(RunControlState::Running);
|
if (state != RunControlState::Starting && state != RunControlState::Running)
|
||||||
|
qDebug() << "Unexpected initiateStop() in state" << stateName(state);
|
||||||
|
|
||||||
setState(RunControlState::Stopping);
|
setState(RunControlState::Stopping);
|
||||||
debugMessage("Queue: Stopping for all workers");
|
debugMessage("Queue: Stopping for all workers");
|
||||||
|
|
||||||
|
continueStopOrFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunControlPrivate::continueStopOrFinish()
|
||||||
|
{
|
||||||
bool allDone = true;
|
bool allDone = true;
|
||||||
|
|
||||||
|
auto queueStop = [this](RunWorker *worker, const QString &message) {
|
||||||
|
if (worker->d->canStop()) {
|
||||||
|
debugMessage(message);
|
||||||
|
worker->d->state = RunWorkerState::Stopping;
|
||||||
|
QTimer::singleShot(0, worker, &RunWorker::initiateStop);
|
||||||
|
} else {
|
||||||
|
debugMessage(" " + worker->d->id + " is waiting for dependent workers to stop");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
for (RunWorker *worker : m_workers) {
|
for (RunWorker *worker : m_workers) {
|
||||||
if (worker) {
|
if (worker) {
|
||||||
const QString &workerId = worker->d->id;
|
const QString &workerId = worker->d->id;
|
||||||
@@ -873,33 +888,35 @@ void RunControlPrivate::initiateStop()
|
|||||||
allDone = false;
|
allDone = false;
|
||||||
break;
|
break;
|
||||||
case RunWorkerState::Starting:
|
case RunWorkerState::Starting:
|
||||||
debugMessage(" " + workerId + " was Starting, queuing stop");
|
queueStop(worker, " " + workerId + " was Starting, queuing stop");
|
||||||
worker->d->state = RunWorkerState::Stopping;
|
|
||||||
QTimer::singleShot(0, worker, &RunWorker::initiateStop);
|
|
||||||
allDone = false;
|
allDone = false;
|
||||||
break;
|
break;
|
||||||
case RunWorkerState::Running:
|
case RunWorkerState::Running:
|
||||||
debugMessage(" " + workerId + " was Running, queuing stop");
|
queueStop(worker, " " + workerId + " was Running, queuing stop");
|
||||||
worker->d->state = RunWorkerState::Stopping;
|
|
||||||
allDone = false;
|
allDone = false;
|
||||||
QTimer::singleShot(0, worker, &RunWorker::initiateStop);
|
|
||||||
break;
|
break;
|
||||||
case RunWorkerState::Done:
|
case RunWorkerState::Done:
|
||||||
debugMessage(" " + workerId + " was Done. Good.");
|
debugMessage(" " + workerId + " was Done. Good.");
|
||||||
break;
|
break;
|
||||||
case RunWorkerState::Failed:
|
|
||||||
debugMessage(" " + workerId + " was Failed. Good");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debugMessage("Found unknown deleted worker");
|
debugMessage("Found unknown deleted worker");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (allDone) {
|
|
||||||
debugMessage("All stopped.");
|
RunControlState targetState;
|
||||||
setState(RunControlState::Stopped);
|
if (state == RunControlState::Finishing) {
|
||||||
|
targetState = RunControlState::Finished;
|
||||||
} else {
|
} else {
|
||||||
debugMessage("Not all workers stopped. Waiting...");
|
checkState(RunControlState::Stopping);
|
||||||
|
targetState = RunControlState::Stopped;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allDone) {
|
||||||
|
debugMessage("All Stopped");
|
||||||
|
setState(targetState);
|
||||||
|
} else {
|
||||||
|
debugMessage("Not all workers Stopped. Waiting...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -908,48 +925,7 @@ void RunControlPrivate::initiateFinish()
|
|||||||
setState(RunControlState::Finishing);
|
setState(RunControlState::Finishing);
|
||||||
debugMessage("Ramping down");
|
debugMessage("Ramping down");
|
||||||
|
|
||||||
bool allDone = true;
|
continueStopOrFinish();
|
||||||
for (RunWorker *worker : m_workers) {
|
|
||||||
if (worker) {
|
|
||||||
const QString &workerId = worker->d->id;
|
|
||||||
debugMessage(" Examining worker " + workerId);
|
|
||||||
switch (worker->d->state) {
|
|
||||||
case RunWorkerState::Initialized:
|
|
||||||
debugMessage(" " + workerId + " was Initialized, setting to Done");
|
|
||||||
worker->d->state = RunWorkerState::Done;
|
|
||||||
break;
|
|
||||||
case RunWorkerState::Stopping:
|
|
||||||
debugMessage(" " + workerId + " was already Stopping. Keeping it that way");
|
|
||||||
allDone = false;
|
|
||||||
break;
|
|
||||||
case RunWorkerState::Starting:
|
|
||||||
debugMessage(" " + workerId + " was Starting, queuing stop");
|
|
||||||
worker->d->state = RunWorkerState::Stopping;
|
|
||||||
QTimer::singleShot(0, worker, &RunWorker::initiateStop);
|
|
||||||
allDone = false;
|
|
||||||
break;
|
|
||||||
case RunWorkerState::Running:
|
|
||||||
debugMessage(" " + workerId + " was Running, queuing stop");
|
|
||||||
worker->d->state = RunWorkerState::Stopping;
|
|
||||||
allDone = false;
|
|
||||||
QTimer::singleShot(0, worker, &RunWorker::initiateStop);
|
|
||||||
break;
|
|
||||||
case RunWorkerState::Done:
|
|
||||||
debugMessage(" " + workerId + " was Done. Good.");
|
|
||||||
break;
|
|
||||||
case RunWorkerState::Failed:
|
|
||||||
debugMessage(" " + workerId + " was Failed. Good");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debugMessage("Found unknown deleted worker");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (allDone) {
|
|
||||||
setState(RunControlState::Finished);
|
|
||||||
} else {
|
|
||||||
debugMessage("Not all workers finished. Waiting...");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunControlPrivate::onWorkerStarted(RunWorker *worker)
|
void RunControlPrivate::onWorkerStarted(RunWorker *worker)
|
||||||
@@ -968,10 +944,13 @@ void RunControlPrivate::onWorkerStarted(RunWorker *worker)
|
|||||||
|
|
||||||
void RunControlPrivate::onWorkerFailed(RunWorker *worker, const QString &msg)
|
void RunControlPrivate::onWorkerFailed(RunWorker *worker, const QString &msg)
|
||||||
{
|
{
|
||||||
worker->d->state = RunWorkerState::Failed;
|
worker->d->state = RunWorkerState::Done;
|
||||||
|
|
||||||
showError(msg);
|
showError(msg);
|
||||||
initiateStop();
|
if (state == RunControlState::Running || state == RunControlState::Starting)
|
||||||
|
initiateStop();
|
||||||
|
else
|
||||||
|
continueStopOrFinish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunControlPrivate::onWorkerStopped(RunWorker *worker)
|
void RunControlPrivate::onWorkerStopped(RunWorker *worker)
|
||||||
@@ -994,10 +973,34 @@ void RunControlPrivate::onWorkerStopped(RunWorker *worker)
|
|||||||
default:
|
default:
|
||||||
debugMessage(workerId + " stopped unexpectedly in state"
|
debugMessage(workerId + " stopped unexpectedly in state"
|
||||||
+ stateName(worker->d->state));
|
+ stateName(worker->d->state));
|
||||||
worker->d->state = RunWorkerState::Failed;
|
worker->d->state = RunWorkerState::Done;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state == RunControlState::Finishing || state == RunControlState::Stopping) {
|
||||||
|
continueStopOrFinish();
|
||||||
|
return;
|
||||||
|
} else if (worker->isEssential()) {
|
||||||
|
debugMessage(workerId + " is essential. Stopping all others.");
|
||||||
|
initiateStop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (RunWorker *dependent : worker->d->stopDependencies) {
|
||||||
|
switch (dependent->d->state) {
|
||||||
|
case RunWorkerState::Done:
|
||||||
|
break;
|
||||||
|
case RunWorkerState::Initialized:
|
||||||
|
dependent->d->state = RunWorkerState::Done;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
debugMessage("Killing " + dependent->d->id + " as it depends on stopped " + workerId);
|
||||||
|
dependent->d->state = RunWorkerState::Stopping;
|
||||||
|
QTimer::singleShot(0, dependent, &RunWorker::initiateStop);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
debugMessage("Checking whether all stopped");
|
debugMessage("Checking whether all stopped");
|
||||||
bool allDone = true;
|
bool allDone = true;
|
||||||
for (RunWorker *worker : m_workers) {
|
for (RunWorker *worker : m_workers) {
|
||||||
@@ -1023,32 +1026,21 @@ void RunControlPrivate::onWorkerStopped(RunWorker *worker)
|
|||||||
case RunWorkerState::Done:
|
case RunWorkerState::Done:
|
||||||
debugMessage(" " + workerId + " was Done. Good.");
|
debugMessage(" " + workerId + " was Done. Good.");
|
||||||
break;
|
break;
|
||||||
case RunWorkerState::Failed:
|
|
||||||
debugMessage(" " + workerId + " was Failed. Good");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debugMessage("Found unknown deleted worker");
|
debugMessage("Found unknown deleted worker");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state == RunControlState::Finishing) {
|
|
||||||
if (allDone) {
|
if (allDone) {
|
||||||
debugMessage("All finished. Deleting myself");
|
if (state == RunControlState::Stopped) {
|
||||||
setState(RunControlState::Finished);
|
debugMessage("All workers stopped, but runControl was already stopped.");
|
||||||
} else {
|
} else {
|
||||||
debugMessage("Not all workers finished. Waiting...");
|
debugMessage("All workers stopped. Set runControl to Stopped");
|
||||||
|
setState(RunControlState::Stopped);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (allDone) {
|
debugMessage("Not all workers stopped. Waiting...");
|
||||||
if (state == RunControlState::Stopped) {
|
|
||||||
debugMessage("All workers stopped, but runControl was already stopped.");
|
|
||||||
} else {
|
|
||||||
debugMessage("All workers stopped. Set runControl to Stopped");
|
|
||||||
setState(RunControlState::Stopped);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debugMessage("Not all workers stopped. Waiting...");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1254,6 +1246,7 @@ bool RunControlPrivate::isAllowedTransition(RunControlState from, RunControlStat
|
|||||||
|| to == RunControlState::Finishing;
|
|| to == RunControlState::Finishing;
|
||||||
case RunControlState::Starting:
|
case RunControlState::Starting:
|
||||||
return to == RunControlState::Running
|
return to == RunControlState::Running
|
||||||
|
|| to == RunControlState::Stopping
|
||||||
|| to == RunControlState::Finishing;
|
|| to == RunControlState::Finishing;
|
||||||
case RunControlState::Running:
|
case RunControlState::Running:
|
||||||
return to == RunControlState::Stopping
|
return to == RunControlState::Stopping
|
||||||
@@ -1480,8 +1473,8 @@ bool RunWorkerPrivate::canStart() const
|
|||||||
{
|
{
|
||||||
if (state != RunWorkerState::Initialized)
|
if (state != RunWorkerState::Initialized)
|
||||||
return false;
|
return false;
|
||||||
for (RunWorker *worker : dependencies) {
|
for (RunWorker *worker : startDependencies) {
|
||||||
QTC_ASSERT(worker, return true);
|
QTC_ASSERT(worker, continue);
|
||||||
if (worker->d->state != RunWorkerState::Done
|
if (worker->d->state != RunWorkerState::Done
|
||||||
&& worker->d->state != RunWorkerState::Running)
|
&& worker->d->state != RunWorkerState::Running)
|
||||||
return false;
|
return false;
|
||||||
@@ -1489,6 +1482,18 @@ bool RunWorkerPrivate::canStart() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RunWorkerPrivate::canStop() const
|
||||||
|
{
|
||||||
|
if (state != RunWorkerState::Starting && state != RunWorkerState::Running)
|
||||||
|
return false;
|
||||||
|
for (RunWorker *worker : stopDependencies) {
|
||||||
|
QTC_ASSERT(worker, continue);
|
||||||
|
if (worker->d->state != RunWorkerState::Done)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void RunWorkerPrivate::timerEvent(QTimerEvent *ev)
|
void RunWorkerPrivate::timerEvent(QTimerEvent *ev)
|
||||||
{
|
{
|
||||||
if (ev->timerId() == startWatchdogTimerId) {
|
if (ev->timerId() == startWatchdogTimerId) {
|
||||||
@@ -1501,7 +1506,42 @@ void RunWorkerPrivate::timerEvent(QTimerEvent *ev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunWorker
|
/*!
|
||||||
|
\class ProjectExplorer::RunWorker
|
||||||
|
|
||||||
|
\brief The RunWorker class encapsulates a task that forms part, or
|
||||||
|
the whole of the operation of a tool for a certain \c RunConfiguration
|
||||||
|
according to some \c RunMode.
|
||||||
|
|
||||||
|
A typical example for a \c RunWorker is a process, either the
|
||||||
|
application process itself, or a helper process, such as a watchdog
|
||||||
|
or a log parser.
|
||||||
|
|
||||||
|
A \c RunWorker has a simple state model covering the \c Initialized,
|
||||||
|
\c Starting, \c Running, \c Stopping, and \c Done states.
|
||||||
|
|
||||||
|
In the course of the operation of tools several \c RunWorkers
|
||||||
|
may co-operate and form a combined state that is presented
|
||||||
|
to the user as \c RunControl, with direct interaction made
|
||||||
|
possible through the buttons in the \uicontrol{Application Output}
|
||||||
|
pane.
|
||||||
|
|
||||||
|
RunWorkers are typically created together with their RunControl.
|
||||||
|
The startup order of RunWorkers under a RunControl can be
|
||||||
|
specified by making a RunWorker dependent on others.
|
||||||
|
|
||||||
|
When a RunControl starts, it calls \c initiateStart() on RunWorkers
|
||||||
|
with fulfilled dependencies until all workers are \c Running, or in case
|
||||||
|
of short-lived helper tasks, \c Done.
|
||||||
|
|
||||||
|
A RunWorker can stop spontaneously, for example when the main application
|
||||||
|
process ends. In this case, it typically calls \c initiateStop()
|
||||||
|
on its RunControl, which in turn passes this to all sibling
|
||||||
|
RunWorkers.
|
||||||
|
|
||||||
|
Pressing the stop button in the \uicontrol{Application Output} pane
|
||||||
|
also calls \c initiateStop on the RunControl.
|
||||||
|
*/
|
||||||
|
|
||||||
RunWorker::RunWorker(RunControl *runControl)
|
RunWorker::RunWorker(RunControl *runControl)
|
||||||
: d(new RunWorkerPrivate(this, runControl))
|
: d(new RunWorkerPrivate(this, runControl))
|
||||||
@@ -1513,6 +1553,10 @@ RunWorker::~RunWorker()
|
|||||||
delete d;
|
delete d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* This function is called by the RunControl once all dependencies
|
||||||
|
* are fulfilled.
|
||||||
|
*/
|
||||||
void RunWorker::initiateStart()
|
void RunWorker::initiateStart()
|
||||||
{
|
{
|
||||||
if (d->startWatchdogInterval != 0)
|
if (d->startWatchdogInterval != 0)
|
||||||
@@ -1521,6 +1565,12 @@ void RunWorker::initiateStart()
|
|||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* This function has to be called by a RunWorker implementation
|
||||||
|
* to notify its RunControl about the successful start of this RunWorker.
|
||||||
|
*
|
||||||
|
* The RunControl may start other RunWorkers in response.
|
||||||
|
*/
|
||||||
void RunWorker::reportStarted()
|
void RunWorker::reportStarted()
|
||||||
{
|
{
|
||||||
if (d->startWatchdogInterval != 0)
|
if (d->startWatchdogInterval != 0)
|
||||||
@@ -1529,6 +1579,12 @@ void RunWorker::reportStarted()
|
|||||||
emit started();
|
emit started();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* This function is called by the RunControl in its own \c initiateStop
|
||||||
|
* implementation, which is triggered in response to pressing the
|
||||||
|
* stop button in the \uicontrol{Application Output} pane or on direct
|
||||||
|
* request of one of the sibling RunWorkers.
|
||||||
|
*/
|
||||||
void RunWorker::initiateStop()
|
void RunWorker::initiateStop()
|
||||||
{
|
{
|
||||||
if (d->stopWatchdogInterval != 0)
|
if (d->stopWatchdogInterval != 0)
|
||||||
@@ -1538,6 +1594,15 @@ void RunWorker::initiateStop()
|
|||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* This function has to be called by a RunWorker implementation
|
||||||
|
* to notify its RunControl about this RunWorker having stopped.
|
||||||
|
*
|
||||||
|
* The stop can be spontaneous, or in response to an initiateStop()
|
||||||
|
* or an initiateFinish() call.
|
||||||
|
*
|
||||||
|
* The RunControl will adjust its global state in response.
|
||||||
|
*/
|
||||||
void RunWorker::reportStopped()
|
void RunWorker::reportStopped()
|
||||||
{
|
{
|
||||||
if (d->stopWatchdogInterval != 0)
|
if (d->stopWatchdogInterval != 0)
|
||||||
@@ -1546,11 +1611,47 @@ void RunWorker::reportStopped()
|
|||||||
emit stopped();
|
emit stopped();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* This function can be called by a RunWorker implementation for short-lived
|
||||||
|
* tasks to notify its RunControl about this task being successful finished.
|
||||||
|
* Dependent startup tasks can proceed, in cases of spontaneous or scheduled
|
||||||
|
* stops, the effect is the same as \c reportStopped().
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void RunWorker::reportDone()
|
||||||
|
{
|
||||||
|
switch (d->state) {
|
||||||
|
case RunWorkerState::Initialized:
|
||||||
|
QTC_CHECK(false);
|
||||||
|
d->state = RunWorkerState::Done;
|
||||||
|
break;
|
||||||
|
case RunWorkerState::Starting:
|
||||||
|
reportStarted();
|
||||||
|
reportStopped();
|
||||||
|
break;
|
||||||
|
case RunWorkerState::Running:
|
||||||
|
case RunWorkerState::Stopping:
|
||||||
|
reportStopped();
|
||||||
|
break;
|
||||||
|
case RunWorkerState::Done:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* This function can be called by a RunWorker implementation to
|
||||||
|
* signal a problem in the operation in this worker. The
|
||||||
|
* RunControl will start to ramp down through initiateStop().
|
||||||
|
*/
|
||||||
void RunWorker::reportFailure(const QString &msg)
|
void RunWorker::reportFailure(const QString &msg)
|
||||||
{
|
{
|
||||||
d->runControl->d->onWorkerFailed(this, msg);
|
d->runControl->d->onWorkerFailed(this, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Appends a message in the specified \a format to
|
||||||
|
* the owning RunControl's \uicontrol{Application Output} pane.
|
||||||
|
*/
|
||||||
void RunWorker::appendMessage(const QString &msg, OutputFormat format)
|
void RunWorker::appendMessage(const QString &msg, OutputFormat format)
|
||||||
{
|
{
|
||||||
if (msg.endsWith('\n'))
|
if (msg.endsWith('\n'))
|
||||||
@@ -1574,9 +1675,14 @@ Core::Id RunWorker::runMode() const
|
|||||||
return d->runControl->runMode();
|
return d->runControl->runMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunWorker::addDependency(RunWorker *dependency)
|
void RunWorker::addStartDependency(RunWorker *dependency)
|
||||||
{
|
{
|
||||||
d->dependencies.append(dependency);
|
d->startDependencies.append(dependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunWorker::addStopDependency(RunWorker *dependency)
|
||||||
|
{
|
||||||
|
d->stopDependencies.append(dependency);
|
||||||
}
|
}
|
||||||
|
|
||||||
RunControl *RunWorker::runControl() const
|
RunControl *RunWorker::runControl() const
|
||||||
@@ -1619,11 +1725,6 @@ bool RunWorker::supportsReRunning() const
|
|||||||
return d->supportsReRunning;
|
return d->supportsReRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RunWorker::hasFailed() const
|
|
||||||
{
|
|
||||||
return d->state == RunWorkerState::Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString RunWorker::userMessageForProcessError(QProcess::ProcessError error, const QString &program)
|
QString RunWorker::userMessageForProcessError(QProcess::ProcessError error, const QString &program)
|
||||||
{
|
{
|
||||||
QString failedToStart = tr("The process failed to start.");
|
QString failedToStart = tr("The process failed to start.");
|
||||||
@@ -1657,6 +1758,16 @@ QString RunWorker::userMessageForProcessError(QProcess::ProcessError error, cons
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RunWorker::isEssential() const
|
||||||
|
{
|
||||||
|
return d->essential;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunWorker::setEssential(bool essential)
|
||||||
|
{
|
||||||
|
d->essential = essential;
|
||||||
|
}
|
||||||
|
|
||||||
void RunWorker::start()
|
void RunWorker::start()
|
||||||
{
|
{
|
||||||
reportStarted();
|
reportStarted();
|
||||||
|
|||||||
@@ -326,7 +326,8 @@ public:
|
|||||||
|
|
||||||
RunControl *runControl() const;
|
RunControl *runControl() const;
|
||||||
|
|
||||||
void addDependency(RunWorker *dependency);
|
void addStartDependency(RunWorker *dependency);
|
||||||
|
void addStopDependency(RunWorker *dependency);
|
||||||
|
|
||||||
void setDisplayName(const QString &id) { setId(id); } // FIXME: Obsoleted by setId.
|
void setDisplayName(const QString &id) { setId(id); } // FIXME: Obsoleted by setId.
|
||||||
void setId(const QString &id);
|
void setId(const QString &id);
|
||||||
@@ -350,13 +351,17 @@ public:
|
|||||||
void initiateStop();
|
void initiateStop();
|
||||||
void reportStopped();
|
void reportStopped();
|
||||||
|
|
||||||
|
void reportDone();
|
||||||
|
|
||||||
void reportFailure(const QString &msg = QString());
|
void reportFailure(const QString &msg = QString());
|
||||||
void setSupportsReRunning(bool reRunningSupported);
|
void setSupportsReRunning(bool reRunningSupported);
|
||||||
bool supportsReRunning() const;
|
bool supportsReRunning() const;
|
||||||
bool hasFailed() const;
|
|
||||||
|
|
||||||
static QString userMessageForProcessError(QProcess::ProcessError, const QString &programName);
|
static QString userMessageForProcessError(QProcess::ProcessError, const QString &programName);
|
||||||
|
|
||||||
|
bool isEssential() const;
|
||||||
|
void setEssential(bool essential);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void started();
|
void started();
|
||||||
void stopped();
|
void stopped();
|
||||||
|
|||||||
@@ -62,6 +62,9 @@
|
|||||||
<property name="text">
|
<property name="text">
|
||||||
<string/>
|
<string/>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="0">
|
<item row="0" column="0">
|
||||||
@@ -76,6 +79,9 @@
|
|||||||
<property name="text">
|
<property name="text">
|
||||||
<string>TextLabel</string>
|
<string>TextLabel</string>
|
||||||
</property>
|
</property>
|
||||||
|
<property name="textInteractionFlags">
|
||||||
|
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
|
||||||
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
|
|||||||
@@ -1011,7 +1011,7 @@ void QbsProject::updateCppCodeModel()
|
|||||||
rpp.setDisplayName(grp.name());
|
rpp.setDisplayName(grp.name());
|
||||||
rpp.setProjectFileLocation(grp.location().filePath(),
|
rpp.setProjectFileLocation(grp.location().filePath(),
|
||||||
grp.location().line(), grp.location().column());
|
grp.location().line(), grp.location().column());
|
||||||
rpp.setBuildSystemTarget(prd.name() + '|' + rpp.projectFile);
|
rpp.setBuildSystemTarget(prd.name());
|
||||||
|
|
||||||
QHash<QString, qbs::ArtifactData> filePathToSourceArtifact;
|
QHash<QString, qbs::ArtifactData> filePathToSourceArtifact;
|
||||||
bool hasCFiles = false;
|
bool hasCFiles = false;
|
||||||
|
|||||||